anope

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

ns_set.cpp (42829B)

      1 /* NickServ 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 
     14 class CommandNSSet : public Command
     15 {
     16  public:
     17 	CommandNSSet(Module *creator) : Command(creator, "nickserv/set", 1, 3)
     18 	{
     19 		this->SetDesc(_("Set options, including kill protection"));
     20 		this->SetSyntax(_("\037option\037 \037parameters\037"));
     21 	}
     22 
     23 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     24 	{
     25 		this->OnSyntaxError(source, "");
     26 		return;
     27 	}
     28 
     29 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
     30 	{
     31 		this->SendSyntax(source);
     32 		source.Reply(" ");
     33 		source.Reply(_("Sets various nickname options. \037option\037 can be one of:"));
     34 
     35 		Anope::string this_name = source.command;
     36 		bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
     37 		     hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
     38 		for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
     39 		{
     40 			const Anope::string &c_name = it->first;
     41 			const CommandInfo &info = it->second;
     42 
     43 			if (c_name.find_ci(this_name + " ") == 0)
     44 			{
     45 				if (info.hide)
     46 					continue;
     47 
     48 				ServiceReference<Command> c("Command", info.name);
     49 				// XXX dup
     50 				if (!c)
     51 					continue;
     52 				else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
     53 					continue;
     54 				else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
     55 					continue;
     56 
     57 				source.command = c_name;
     58 				c->OnServHelp(source);
     59 			}
     60 		}
     61 
     62 		source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information\n"
     63 			"on a specific option."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
     64 
     65 		return true;
     66 	}
     67 };
     68 
     69 class CommandNSSASet : public Command
     70 {
     71  public:
     72 	CommandNSSASet(Module *creator) : Command(creator, "nickserv/saset", 2, 4)
     73 	{
     74 		this->SetDesc(_("Set SET-options on another nickname"));
     75 		this->SetSyntax(_("\037option\037 \037nickname\037 \037parameters\037"));
     76 	}
     77 
     78 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     79 	{
     80 		this->OnSyntaxError(source, "");
     81 		return;
     82 	}
     83 
     84 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
     85 	{
     86 		this->SendSyntax(source);
     87 		source.Reply(" ");
     88 		source.Reply(_("Sets various nickname options. \037option\037 can be one of:"));
     89 
     90 		Anope::string this_name = source.command;
     91 		for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
     92 		{
     93 			const Anope::string &c_name = it->first;
     94 			const CommandInfo &info = it->second;
     95 
     96 			if (c_name.find_ci(this_name + " ") == 0)
     97 			{
     98 				ServiceReference<Command> command("Command", info.name);
     99 				if (command)
    100 				{
    101 					source.command = c_name;
    102 					command->OnServHelp(source);
    103 				}
    104 			}
    105 		}
    106 
    107 		source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information\n"
    108 				"on a specific option. The options will be set on the given\n"
    109 				"\037nickname\037."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
    110 		return true;
    111 	}
    112 };
    113 
    114 class CommandNSSetPassword : public Command
    115 {
    116  public:
    117 	CommandNSSetPassword(Module *creator) : Command(creator, "nickserv/set/password", 1)
    118 	{
    119 		this->SetDesc(_("Set your nickname password"));
    120 		this->SetSyntax(_("\037new-password\037"));
    121 	}
    122 
    123 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    124 	{
    125 		const Anope::string &param = params[0];
    126 		unsigned len = param.length();
    127 
    128 		if (Anope::ReadOnly)
    129 		{
    130 			source.Reply(READ_ONLY_MODE);
    131 			return;
    132 		}
    133 
    134 		if (source.GetNick().equals_ci(param) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && len < 5))
    135 		{
    136 			source.Reply(MORE_OBSCURE_PASSWORD);
    137 			return;
    138 		}
    139 
    140 		unsigned int passlen = Config->GetModule("nickserv")->Get<unsigned>("passlen", "32");
    141 		if (len > passlen)
    142 		{
    143 			source.Reply(PASSWORD_TOO_LONG, passlen);
    144 			return;
    145 		}
    146 
    147 		Log(LOG_COMMAND, source, this) << "to change their password";
    148 
    149 		Anope::Encrypt(param, source.nc->pass);
    150 		Anope::string tmp_pass;
    151 		if (Anope::Decrypt(source.nc->pass, tmp_pass) == 1)
    152 			source.Reply(_("Password for \002%s\002 changed to \002%s\002."), source.nc->display.c_str(), tmp_pass.c_str());
    153 		else
    154 			source.Reply(_("Password for \002%s\002 changed."), source.nc->display.c_str());
    155 	}
    156 
    157 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    158 	{
    159 		this->SendSyntax(source);
    160 		source.Reply(" ");
    161 		source.Reply(_("Changes the password used to identify you as the nick's\n"
    162 			"owner."));
    163 		return true;
    164 	}
    165 };
    166 
    167 class CommandNSSASetPassword : public Command
    168 {
    169  public:
    170 	CommandNSSASetPassword(Module *creator) : Command(creator, "nickserv/saset/password", 2, 2)
    171 	{
    172 		this->SetDesc(_("Set the nickname password"));
    173 		this->SetSyntax(_("\037nickname\037 \037new-password\037"));
    174 	}
    175 
    176 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    177 	{
    178 		if (Anope::ReadOnly)
    179 		{
    180 			source.Reply(READ_ONLY_MODE);
    181 			return;
    182 		}
    183 
    184 		const NickAlias *setter_na = NickAlias::Find(params[0]);
    185 		if (setter_na == NULL)
    186 		{
    187 			source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str());
    188 			return;
    189 		}
    190 		NickCore *nc = setter_na->nc;
    191 
    192 		size_t len = params[1].length();
    193 
    194 		if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.nc != nc && nc->IsServicesOper())
    195 		{
    196 			source.Reply(_("You may not change the password of other Services Operators."));
    197 			return;
    198 		}
    199 
    200 		if (nc->display.equals_ci(params[1]) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && len < 5))
    201 		{
    202 			source.Reply(MORE_OBSCURE_PASSWORD);
    203 			return;
    204 		}
    205 
    206 		unsigned int passlen = Config->GetModule("nickserv")->Get<unsigned>("passlen", "32");
    207 		if (len > passlen)
    208 		{
    209 			source.Reply(PASSWORD_TOO_LONG, passlen);
    210 			return;
    211 		}
    212 
    213 		Log(LOG_ADMIN, source, this) << "to change the password of " << nc->display;
    214 
    215 		Anope::Encrypt(params[1], nc->pass);
    216 		Anope::string tmp_pass;
    217 		if (Anope::Decrypt(nc->pass, tmp_pass) == 1)
    218 			source.Reply(_("Password for \002%s\002 changed to \002%s\002."), nc->display.c_str(), tmp_pass.c_str());
    219 		else
    220 			source.Reply(_("Password for \002%s\002 changed."), nc->display.c_str());
    221 	}
    222 
    223 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    224 	{
    225 		this->SendSyntax(source);
    226 		source.Reply(" ");
    227 		source.Reply(_("Changes the password used to identify as the nick's owner."));
    228 		return true;
    229 	}
    230 };
    231 
    232 class CommandNSSetAutoOp : public Command
    233 {
    234  public:
    235 	CommandNSSetAutoOp(Module *creator, const Anope::string &sname = "nickserv/set/autoop", size_t min = 1) : Command(creator, sname, min, min + 1)
    236 	{
    237 		this->SetDesc(_("Sets whether services should set channel status modes on you automatically."));
    238 		this->SetSyntax("{ON | OFF}");
    239 	}
    240 
    241 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    242 	{
    243 		if (Anope::ReadOnly)
    244 		{
    245 			source.Reply(READ_ONLY_MODE);
    246 			return;
    247 		}
    248 
    249 		const NickAlias *na = NickAlias::Find(user);
    250 		if (na == NULL)
    251 		{
    252 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    253 			return;
    254 		}
    255 		NickCore *nc = na->nc;
    256 
    257 		EventReturn MOD_RESULT;
    258 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
    259 		if (MOD_RESULT == EVENT_STOP)
    260 			return;
    261 
    262 		if (param.equals_ci("ON"))
    263 		{
    264 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable autoop for " << na->nc->display;
    265 			nc->Extend<bool>("AUTOOP");
    266 			source.Reply(_("Services will from now on set status modes on %s in channels."), nc->display.c_str());
    267 		}
    268 		else if (param.equals_ci("OFF"))
    269 		{
    270 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable autoop for " << na->nc->display;
    271 			nc->Shrink<bool>("AUTOOP");
    272 			source.Reply(_("Services will no longer set status modes on %s in channels."), nc->display.c_str());
    273 		}
    274 		else
    275 			this->OnSyntaxError(source, "AUTOOP");
    276 	}
    277 
    278 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    279 	{
    280 		this->Run(source, source.nc->display, params[0]);
    281 	}
    282 
    283 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    284 	{
    285 		BotInfo *bi = Config->GetClient("ChanServ");
    286 		this->SendSyntax(source);
    287 		source.Reply(" ");
    288 		source.Reply(_("Sets whether you will be given your channel status modes automatically.\n"
    289 				"Set to \002ON\002 to allow %s to set status modes on you automatically\n"
    290 				"when entering channels. Note that depending on channel settings some modes\n"
    291 				"may not get set automatically."), bi ? bi->nick.c_str() : "ChanServ");
    292 		return true;
    293 	}
    294 };
    295 
    296 class CommandNSSASetAutoOp : public CommandNSSetAutoOp
    297 {
    298  public:
    299 	CommandNSSASetAutoOp(Module *creator) : CommandNSSetAutoOp(creator, "nickserv/saset/autoop", 2)
    300 	{
    301 		this->ClearSyntax();
    302 		this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
    303 	}
    304 
    305 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    306 	{
    307 		this->Run(source, params[0], params[1]);
    308 	}
    309 
    310 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    311 	{
    312 		BotInfo *bi = Config->GetClient("ChanServ");
    313 		this->SendSyntax(source);
    314 		source.Reply(" ");
    315 		source.Reply(_("Sets whether the given nickname will be given its status modes\n"
    316 				"in channels automatically. Set to \002ON\002 to allow %s\n"
    317 				"to set status modes on the given nickname automatically when it\n"
    318 				"is entering channels. Note that depending on channel settings\n"
    319 				"some modes may not get set automatically."), bi ? bi->nick.c_str() : "ChanServ");
    320 		return true;
    321 	}
    322 };
    323 
    324 class CommandNSSetDisplay : public Command
    325 {
    326  public:
    327 	CommandNSSetDisplay(Module *creator, const Anope::string &sname = "nickserv/set/display", size_t min = 1) : Command(creator, sname, min, min + 1)
    328 	{
    329 		this->SetDesc(_("Set the display of your group in Services"));
    330 		this->SetSyntax(_("\037new-display\037"));
    331 	}
    332 
    333 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    334 	{
    335 		if (Anope::ReadOnly)
    336 		{
    337 			source.Reply(READ_ONLY_MODE);
    338 			return;
    339 		}
    340 
    341 		NickAlias *user_na = NickAlias::Find(user), *na = NickAlias::Find(param);
    342 
    343 		if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
    344 		{
    345 			source.Reply(_("This command may not be used on this network because nickname ownership is disabled."));
    346 			return;
    347 		}
    348 		if (user_na == NULL)
    349 		{
    350 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    351 			return;
    352 		}
    353 		else if (!na || *na->nc != *user_na->nc)
    354 		{
    355 			source.Reply(_("The new display MUST be a nickname of the nickname group %s."), user_na->nc->display.c_str());
    356 			return;
    357 		}
    358 
    359 		EventReturn MOD_RESULT;
    360 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, user_na->nc, param));
    361 		if (MOD_RESULT == EVENT_STOP)
    362 			return;
    363 
    364 		Log(user_na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the display of " << user_na->nc->display << " to " << na->nick;
    365 
    366 		user_na->nc->SetDisplay(na);
    367 
    368 		/* Send updated account name */
    369 		for (std::list<User *>::iterator it = user_na->nc->users.begin(); it != user_na->nc->users.end(); ++it)
    370 		{
    371 			User *u = *it;
    372 			IRCD->SendLogin(u, user_na);
    373 		}
    374 
    375 		source.Reply(NICK_SET_DISPLAY_CHANGED, user_na->nc->display.c_str());
    376 	}
    377 
    378 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    379 	{
    380 		this->Run(source, source.nc->display, params[0]);
    381 	}
    382 
    383 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    384 	{
    385 		this->SendSyntax(source);
    386 		source.Reply(" ");
    387 		source.Reply(_("Changes the display used to refer to your nickname group in\n"
    388 				"Services. The new display MUST be a nick of your group."));
    389 		return true;
    390 	}
    391 };
    392 
    393 class CommandNSSASetDisplay : public CommandNSSetDisplay
    394 {
    395  public:
    396 	CommandNSSASetDisplay(Module *creator) : CommandNSSetDisplay(creator, "nickserv/saset/display", 2)
    397 	{
    398 		this->ClearSyntax();
    399 		this->SetSyntax(_("\037nickname\037 \037new-display\037"));
    400 	}
    401 
    402 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    403 	{
    404 		this->Run(source, params[0], params[1]);
    405 	}
    406 
    407 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    408 	{
    409 		this->SendSyntax(source);
    410 		source.Reply(" ");
    411 		source.Reply(_("Changes the display used to refer to the nickname group in\n"
    412 				"Services. The new display MUST be a nick of the group."));
    413 		return true;
    414 	}
    415 };
    416 
    417 class CommandNSSetEmail : public Command
    418 {
    419 	static bool SendConfirmMail(User *u, NickCore *nc, BotInfo *bi, const Anope::string &new_email)
    420 	{
    421 		Anope::string code = Anope::Random(9);
    422 
    423 		std::pair<Anope::string, Anope::string> *n = nc->Extend<std::pair<Anope::string, Anope::string> >("ns_set_email");
    424 		n->first = new_email;
    425 		n->second = code;
    426 
    427 		Anope::string subject = Config->GetBlock("mail")->Get<const Anope::string>("emailchange_subject"),
    428 			message = Config->GetBlock("mail")->Get<const Anope::string>("emailchange_message");
    429 
    430 		subject = subject.replace_all_cs("%e", nc->email);
    431 		subject = subject.replace_all_cs("%E", new_email);
    432 		subject = subject.replace_all_cs("%n", nc->display);
    433 		subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
    434 		subject = subject.replace_all_cs("%c", code);
    435 
    436 		message = message.replace_all_cs("%e", nc->email);
    437 		message = message.replace_all_cs("%E", new_email);
    438 		message = message.replace_all_cs("%n", nc->display);
    439 		message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
    440 		message = message.replace_all_cs("%c", code);
    441 
    442 		Anope::string old = nc->email;
    443 		nc->email = new_email;
    444 		bool b = Mail::Send(u, nc, bi, subject, message);
    445 		nc->email = old;
    446 		return b;
    447 	}
    448 
    449  public:
    450 	CommandNSSetEmail(Module *creator, const Anope::string &cname = "nickserv/set/email", size_t min = 0) : Command(creator, cname, min, min + 1)
    451 	{
    452 		this->SetDesc(_("Associate an E-mail address with your nickname"));
    453 		this->SetSyntax(_("\037address\037"));
    454 	}
    455 
    456 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    457 	{
    458 		if (Anope::ReadOnly)
    459 		{
    460 			source.Reply(READ_ONLY_MODE);
    461 			return;
    462 		}
    463 
    464 		const NickAlias *na = NickAlias::Find(user);
    465 		if (!na)
    466 		{
    467 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    468 			return;
    469 		}
    470 		NickCore *nc = na->nc;
    471 
    472 		if (nc->HasExt("UNCONFIRMED"))
    473 		{
    474 			source.Reply(_("You may not change the email of an unconfirmed account."));
    475 			return;
    476 		}
    477 
    478 		if (param.empty() && Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
    479 		{
    480 			source.Reply(_("You cannot unset the e-mail on this network."));
    481 			return;
    482 		}
    483 		else if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.nc != nc && nc->IsServicesOper())
    484 		{
    485 			source.Reply(_("You may not change the e-mail of other Services Operators."));
    486 			return;
    487 		}
    488 		else if (!param.empty() && !Mail::Validate(param))
    489 		{
    490 			source.Reply(MAIL_X_INVALID, param.c_str());
    491 			return;
    492 		}
    493 
    494 		EventReturn MOD_RESULT;
    495 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
    496 		if (MOD_RESULT == EVENT_STOP)
    497 			return;
    498 
    499 		if (!param.empty() && Config->GetModule("nickserv")->Get<bool>("confirmemailchanges") && !source.IsServicesOper())
    500 		{
    501 			if (SendConfirmMail(source.GetUser(), source.GetAccount(), source.service, param))
    502 			{
    503 				Log(LOG_COMMAND, source, this) << "to request changing the email of " << nc->display << " to " << param;
    504 				source.Reply(_("A confirmation e-mail has been sent to \002%s\002. Follow the instructions in it to change your e-mail address."), param.c_str());
    505 			}
    506 		}
    507 		else
    508 		{
    509 			if (!param.empty())
    510 			{
    511 				Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the email of " << nc->display << " to " << param;
    512 				nc->email = param;
    513 				source.Reply(_("E-mail address for \002%s\002 changed to \002%s\002."), nc->display.c_str(), param.c_str());
    514 			}
    515 			else
    516 			{
    517 				Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to unset the email of " << nc->display;
    518 				nc->email.clear();
    519 				source.Reply(_("E-mail address for \002%s\002 unset."), nc->display.c_str());
    520 			}
    521 		}
    522 	}
    523 
    524 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    525 	{
    526 		this->Run(source, source.nc->display, params.size() ? params[0] : "");
    527 	}
    528 
    529 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    530 	{
    531 		this->SendSyntax(source);
    532 		source.Reply(" ");
    533 		source.Reply(_("Associates the given E-mail address with your nickname.\n"
    534 				"This address will be displayed whenever someone requests\n"
    535 				"information on the nickname with the \002INFO\002 command."));
    536 		return true;
    537 	}
    538 };
    539 
    540 class CommandNSSASetEmail : public CommandNSSetEmail
    541 {
    542  public:
    543 	CommandNSSASetEmail(Module *creator) : CommandNSSetEmail(creator, "nickserv/saset/email", 2)
    544 	{
    545 		this->ClearSyntax();
    546 		this->SetSyntax(_("\037nickname\037 \037address\037"));
    547 	}
    548 
    549 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    550 	{
    551 		this->Run(source, params[0], params.size() > 1 ? params[1] : "");
    552 	}
    553 
    554 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    555 	{
    556 		this->SendSyntax(source);
    557 		source.Reply(" ");
    558 		source.Reply(_("Associates the given E-mail address with the nickname."));
    559 		return true;
    560 	}
    561 };
    562 
    563 class CommandNSSetKeepModes : public Command
    564 {
    565  public:
    566 	CommandNSSetKeepModes(Module *creator, const Anope::string &sname = "nickserv/set/keepmodes", size_t min = 1) : Command(creator, sname, min, min + 1)
    567 	{
    568 		this->SetDesc(_("Enable or disable keep modes"));
    569 		this->SetSyntax("{ON | OFF}");
    570 	}
    571 
    572 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    573 	{
    574 		if (Anope::ReadOnly)
    575 		{
    576 			source.Reply(READ_ONLY_MODE);
    577 			return;
    578 		}
    579 
    580 		const NickAlias *na = NickAlias::Find(user);
    581 		if (!na)
    582 		{
    583 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    584 			return;
    585 		}
    586 		NickCore *nc = na->nc;
    587 
    588 		EventReturn MOD_RESULT;
    589 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
    590 		if (MOD_RESULT == EVENT_STOP)
    591 			return;
    592 
    593 		if (param.equals_ci("ON"))
    594 		{
    595 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable keepmodes for " << nc->display;
    596 			nc->Extend<bool>("NS_KEEP_MODES");
    597 			source.Reply(_("Keep modes for %s is now \002on\002."), nc->display.c_str());
    598 		}
    599 		else if (param.equals_ci("OFF"))
    600 		{
    601 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable keepmodes for " << nc->display;
    602 			nc->Shrink<bool>("NS_KEEP_MODES");
    603 			source.Reply(_("Keep modes for %s is now \002off\002."), nc->display.c_str());
    604 		}
    605 		else
    606 			this->OnSyntaxError(source, "");
    607 	}
    608 
    609 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    610 	{
    611 		this->Run(source, source.nc->display, params[0]);
    612 	}
    613 
    614 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    615 	{
    616 		this->SendSyntax(source);
    617 		source.Reply(" ");
    618 		source.Reply(_("Enables or disables keepmodes for your nick. If keep\n"
    619 				"modes is enabled, services will remember your usermodes\n"
    620 				"and attempt to re-set them the next time you authenticate."));
    621 		return true;
    622 	}
    623 };
    624 
    625 class CommandNSSASetKeepModes : public CommandNSSetKeepModes
    626 {
    627  public:
    628 	CommandNSSASetKeepModes(Module *creator) : CommandNSSetKeepModes(creator, "nickserv/saset/keepmodes", 2)
    629 	{
    630 		this->ClearSyntax();
    631 		this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
    632 	}
    633 
    634 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    635 	{
    636 		this->Run(source, params[0], params[1]);
    637 	}
    638 
    639 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    640 	{
    641 		this->SendSyntax(source);
    642 		source.Reply(" ");
    643 		source.Reply(_("Enables or disables keepmodes for the given nick. If keep\n"
    644 				"modes is enabled, services will remember users' usermodes\n"
    645 				"and attempt to re-set them the next time they authenticate."));
    646 		return true;
    647 	}
    648 };
    649 
    650 class CommandNSSetKill : public Command
    651 {
    652  public:
    653 	CommandNSSetKill(Module *creator, const Anope::string &sname = "nickserv/set/kill", size_t min = 1) : Command(creator, sname, min, min + 1)
    654 	{
    655 		this->SetDesc(_("Turn protection on or off"));
    656 		this->SetSyntax("{ON | QUICK | IMMED | OFF}");
    657 	}
    658 
    659 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    660 	{
    661 		if (Anope::ReadOnly)
    662 		{
    663 			source.Reply(READ_ONLY_MODE);
    664 			return;
    665 		}
    666 
    667 		if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
    668 		{
    669 			source.Reply(_("This command may not be used on this network because nickname ownership is disabled."));
    670 			return;
    671 		}
    672 
    673 		const NickAlias *na = NickAlias::Find(user);
    674 		if (!na)
    675 		{
    676 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    677 			return;
    678 		}
    679 		NickCore *nc = na->nc;
    680 
    681 		EventReturn MOD_RESULT;
    682 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
    683 		if (MOD_RESULT == EVENT_STOP)
    684 			return;
    685 
    686 		if (param.equals_ci("ON"))
    687 		{
    688 			nc->Extend<bool>("KILLPROTECT");
    689 			nc->Shrink<bool>("KILL_QUICK");
    690 			nc->Shrink<bool>("KILL_IMMED");
    691 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill on for " << nc->display;
    692 			source.Reply(_("Protection is now \002on\002 for \002%s\002."), nc->display.c_str());
    693 		}
    694 		else if (param.equals_ci("QUICK"))
    695 		{
    696 			nc->Extend<bool>("KILLPROTECT");
    697 			nc->Extend<bool>("KILL_QUICK");
    698 			nc->Shrink<bool>("KILL_IMMED");
    699 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill quick for " << nc->display;
    700 			source.Reply(_("Protection is now \002on\002 for \002%s\002, with a reduced delay."), nc->display.c_str());
    701 		}
    702 		else if (param.equals_ci("IMMED"))
    703 		{
    704 			if (Config->GetModule(this->owner)->Get<bool>("allowkillimmed"))
    705 			{
    706 				nc->Extend<bool>("KILLPROTECT");
    707 				nc->Shrink<bool>("KILL_QUICK");
    708 				nc->Extend<bool>("KILL_IMMED");
    709 				Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill immed for " << nc->display;
    710 				source.Reply(_("Protection is now \002on\002 for \002%s\002, with no delay."), nc->display.c_str());
    711 			}
    712 			else
    713 				source.Reply(_("The \002IMMED\002 option is not available on this network."));
    714 		}
    715 		else if (param.equals_ci("OFF"))
    716 		{
    717 			nc->Shrink<bool>("KILLPROTECT");
    718 			nc->Shrink<bool>("KILL_QUICK");
    719 			nc->Shrink<bool>("KILL_IMMED");
    720 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable kill for " << nc->display;
    721 			source.Reply(_("Protection is now \002off\002 for \002%s\002."), nc->display.c_str());
    722 		}
    723 		else
    724 			this->OnSyntaxError(source, "KILL");
    725 
    726 		return;
    727 	}
    728 
    729 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    730 	{
    731 		this->Run(source, source.nc->display, params[0]);
    732 	}
    733 
    734 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    735 	{
    736 		this->SendSyntax(source);
    737 		source.Reply(" ");
    738 		source.Reply(_("Turns the automatic protection option for your nick\n"
    739 				"on or off. With protection on, if another user\n"
    740 				"tries to take your nick, they will be given one minute to\n"
    741 				"change to another nick, after which %s will forcibly change\n"
    742 				"their nick.\n"
    743 				" \n"
    744 				"If you select \002QUICK\002, the user will be given only 20 seconds\n"
    745 				"to change nicks instead of the usual 60. If you select\n"
    746 				"\002IMMED\002, the user's nick will be changed immediately \037without\037 being\n"
    747 				"warned first or given a chance to change their nick; please\n"
    748 				"do not use this option unless necessary. Also, your\n"
    749 				"network's administrators may have disabled this option."), source.service->nick.c_str());
    750 		return true;
    751 	}
    752 };
    753 
    754 class CommandNSSASetKill : public CommandNSSetKill
    755 {
    756  public:
    757 	CommandNSSASetKill(Module *creator) : CommandNSSetKill(creator, "nickserv/saset/kill", 2)
    758 	{
    759 		this->ClearSyntax();
    760 		this->SetSyntax(_("\037nickname\037 {ON | QUICK | IMMED | OFF}"));
    761 	}
    762 
    763 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    764 	{
    765 		this->Run(source, params[0], params[1]);
    766 	}
    767 
    768 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    769 	{
    770 		this->SendSyntax(source);
    771 		source.Reply(" ");
    772 		source.Reply(_("Turns the automatic protection option for the nick\n"
    773 				"on or off. With protection on, if another user\n"
    774 				"tries to take the nick, they will be given one minute to\n"
    775 				"change to another nick, after which %s will forcibly change\n"
    776 				"their nick.\n"
    777 				" \n"
    778 				"If you select \002QUICK\002, the user will be given only 20 seconds\n"
    779 				"to change nicks instead of the usual 60. If you select\n"
    780 				"\002IMMED\002, the user's nick will be changed immediately \037without\037 being\n"
    781 				"warned first or given a chance to change their nick; please\n"
    782 				"do not use this option unless necessary. Also, your\n"
    783 				"network's administrators may have disabled this option."), source.service->nick.c_str());
    784 		return true;
    785 	}
    786 };
    787 
    788 class CommandNSSetLanguage : public Command
    789 {
    790  public:
    791 	CommandNSSetLanguage(Module *creator, const Anope::string &sname = "nickserv/set/language", size_t min = 1) : Command(creator, sname, min, min + 1)
    792 	{
    793 		this->SetDesc(_("Set the language Services will use when messaging you"));
    794 		this->SetSyntax(_("\037language\037"));
    795 	}
    796 
    797 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    798 	{
    799 		if (Anope::ReadOnly)
    800 		{
    801 			source.Reply(READ_ONLY_MODE);
    802 			return;
    803 		}
    804 
    805 		const NickAlias *na = NickAlias::Find(user);
    806 		if (!na)
    807 		{
    808 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    809 			return;
    810 		}
    811 		NickCore *nc = na->nc;
    812 
    813 		EventReturn MOD_RESULT;
    814 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
    815 		if (MOD_RESULT == EVENT_STOP)
    816 			return;
    817 
    818 		if (param != "en_US")
    819 			for (unsigned j = 0; j < Language::Languages.size(); ++j)
    820 			{
    821 				if (Language::Languages[j] == param)
    822 					break;
    823 				else if (j + 1 == Language::Languages.size())
    824 				{
    825 					this->OnSyntaxError(source, "");
    826 					return;
    827 				}
    828 			}
    829 
    830 		Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the language of " << nc->display << " to " << param;
    831 
    832 		nc->language = param;
    833 		if (source.GetAccount() == nc)
    834 			source.Reply(_("Language changed to \002English\002."));
    835 		else
    836 			source.Reply(_("Language for \002%s\002 changed to \002%s\002."), nc->display.c_str(), Language::Translate(param.c_str(), _("English")));
    837 	}
    838 
    839 	void Execute(CommandSource &source, const std::vector<Anope::string> &param) anope_override
    840 	{
    841 		this->Run(source, source.nc->display, param[0]);
    842 	}
    843 
    844 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    845 	{
    846 		this->SendSyntax(source);
    847 		source.Reply(" ");
    848 		source.Reply(_("Changes the language Services uses when sending messages to\n"
    849 				"you (for example, when responding to a command you send).\n"
    850 				"\037language\037 should be chosen from the following list of\n"
    851 				"supported languages:"));
    852 
    853 		source.Reply("         en_US (English)");
    854 		for (unsigned j = 0; j < Language::Languages.size(); ++j)
    855 		{
    856 			const Anope::string &langname = Language::Translate(Language::Languages[j].c_str(), _("English"));
    857 			if (langname == "English")
    858 				continue;
    859 			source.Reply("         %s (%s)", Language::Languages[j].c_str(), langname.c_str());
    860 		}
    861 
    862 		return true;
    863 	}
    864 };
    865 
    866 class CommandNSSASetLanguage : public CommandNSSetLanguage
    867 {
    868  public:
    869 	CommandNSSASetLanguage(Module *creator) : CommandNSSetLanguage(creator, "nickserv/saset/language", 2)
    870 	{
    871 		this->ClearSyntax();
    872 		this->SetSyntax(_("\037nickname\037 \037language\037"));
    873 	}
    874 
    875 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    876 	{
    877 		this->Run(source, params[0], params[1]);
    878 	}
    879 
    880 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    881 	{
    882 		this->SendSyntax(source);
    883 		source.Reply(" ");
    884 		source.Reply(_("Changes the language Services uses when sending messages to\n"
    885 				"the given user (for example, when responding to a command they send).\n"
    886 				"\037language\037 should be chosen from the following list of\n"
    887 				"supported languages:"));
    888 		source.Reply("         en (English)");
    889 		for (unsigned j = 0; j < Language::Languages.size(); ++j)
    890 		{
    891 			const Anope::string &langname = Language::Translate(Language::Languages[j].c_str(), _("English"));
    892 			if (langname == "English")
    893 				continue;
    894 			source.Reply("         %s (%s)", Language::Languages[j].c_str(), langname.c_str());
    895 		}
    896 		return true;
    897 	}
    898 };
    899 
    900 class CommandNSSetMessage : public Command
    901 {
    902  public:
    903 	CommandNSSetMessage(Module *creator, const Anope::string &sname = "nickserv/set/message", size_t min = 1) : Command(creator, sname, min, min + 1)
    904 	{
    905 		this->SetDesc(_("Change the communication method of Services"));
    906 		this->SetSyntax("{ON | OFF}");
    907 	}
    908 
    909 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    910 	{
    911 		if (Anope::ReadOnly)
    912 		{
    913 			source.Reply(READ_ONLY_MODE);
    914 			return;
    915 		}
    916 
    917 		const NickAlias *na = NickAlias::Find(user);
    918 		if (!na)
    919 		{
    920 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    921 			return;
    922 		}
    923 		NickCore *nc = na->nc;
    924 
    925 		if (!Config->GetBlock("options")->Get<bool>("useprivmsg"))
    926 		{
    927 			source.Reply(_("You cannot %s on this network."), source.command.c_str());
    928 			return;
    929 		}
    930 
    931 		EventReturn MOD_RESULT;
    932 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
    933 		if (MOD_RESULT == EVENT_STOP)
    934 			return;
    935 
    936 		if (param.equals_ci("ON"))
    937 		{
    938 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable " << source.command << " for " << nc->display;
    939 			nc->Extend<bool>("MSG");
    940 			source.Reply(_("Services will now reply to \002%s\002 with \002messages\002."), nc->display.c_str());
    941 		}
    942 		else if (param.equals_ci("OFF"))
    943 		{
    944 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable " << source.command << " for " << nc->display;
    945 			nc->Shrink<bool>("MSG");
    946 			source.Reply(_("Services will now reply to \002%s\002 with \002notices\002."), nc->display.c_str());
    947 		}
    948 		else
    949 			this->OnSyntaxError(source, "MSG");
    950 	}
    951 
    952 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    953 	{
    954 		this->Run(source, source.nc->display, params[0]);
    955 	}
    956 
    957 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    958 	{
    959 		Anope::string cmd = source.command;
    960 		size_t i = cmd.find_last_of(' ');
    961 		if (i != Anope::string::npos)
    962 			cmd = cmd.substr(i + 1);
    963 
    964 		this->SendSyntax(source);
    965 		source.Reply(" ");
    966 		source.Reply(_("Allows you to choose the way Services are communicating with\n"
    967 				"you. With \002%s\002 set, Services will use messages, else they'll\n"
    968 				"use notices."), cmd.upper().c_str());
    969 		return true;
    970 	}
    971 
    972 	void OnServHelp(CommandSource &source) anope_override
    973 	{
    974 		if (Config->GetBlock("options")->Get<bool>("useprivmsg"))
    975 			Command::OnServHelp(source);
    976 	}
    977 };
    978 
    979 class CommandNSSASetMessage : public CommandNSSetMessage
    980 {
    981  public:
    982 	CommandNSSASetMessage(Module *creator) : CommandNSSetMessage(creator, "nickserv/saset/message", 2)
    983 	{
    984 		this->ClearSyntax();
    985 		this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
    986 	}
    987 
    988 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    989 	{
    990 		this->SendSyntax(source);
    991 		source.Reply(" ");
    992 		source.Reply(_("Allows you to choose the way Services are communicating with\n"
    993 				"the given user. With \002MSG\002 set, Services will use messages,\n"
    994 				"else they'll use notices."));
    995 		return true;
    996 	}
    997 
    998 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    999 	{
   1000 		this->Run(source, params[0], params[1]);
   1001 	}
   1002 };
   1003 
   1004 class CommandNSSetSecure : public Command
   1005 {
   1006  public:
   1007 	CommandNSSetSecure(Module *creator, const Anope::string &sname = "nickserv/set/secure", size_t min = 1) : Command(creator, sname, min, min + 1)
   1008 	{
   1009 		this->SetDesc(_("Turn nickname security on or off"));
   1010 		this->SetSyntax("{ON | OFF}");
   1011 	}
   1012 
   1013 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
   1014 	{
   1015 		if (Anope::ReadOnly)
   1016 		{
   1017 			source.Reply(READ_ONLY_MODE);
   1018 			return;
   1019 		}
   1020 
   1021 		const NickAlias *na = NickAlias::Find(user);
   1022 		if (!na)
   1023 		{
   1024 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
   1025 			return;
   1026 		}
   1027 		NickCore *nc = na->nc;
   1028 
   1029 		EventReturn MOD_RESULT;
   1030 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
   1031 		if (MOD_RESULT == EVENT_STOP)
   1032 			return;
   1033 
   1034 		if (param.equals_ci("ON"))
   1035 		{
   1036 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable secure for " << nc->display;
   1037 			nc->Extend<bool>("NS_SECURE");
   1038 			source.Reply(_("Secure option is now \002on\002 for \002%s\002."), nc->display.c_str());
   1039 		}
   1040 		else if (param.equals_ci("OFF"))
   1041 		{
   1042 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable secure for " << nc->display;
   1043 			nc->Shrink<bool>("NS_SECURE");
   1044 			source.Reply(_("Secure option is now \002off\002 for \002%s\002."), nc->display.c_str());
   1045 		}
   1046 		else
   1047 			this->OnSyntaxError(source, "SECURE");
   1048 	}
   1049 
   1050 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
   1051 	{
   1052 		this->Run(source, source.nc->display, params[0]);
   1053 	}
   1054 
   1055 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
   1056 	{
   1057 		this->SendSyntax(source);
   1058 		source.Reply(" ");
   1059 		source.Reply(_("Turns %s's security features on or off for your\n"
   1060 				"nick. With \002SECURE\002 set, you must enter your password\n"
   1061 				"before you will be recognized as the owner of the nick,\n"
   1062 				"regardless of whether your address is on the access\n"
   1063 				"list. However, if you are on the access list, %s\n"
   1064 				"will not auto-kill you regardless of the setting of the\n"
   1065 				"\002KILL\002 option."), source.service->nick.c_str(), source.service->nick.c_str());
   1066 		return true;
   1067 	}
   1068 };
   1069 
   1070 class CommandNSSASetSecure : public CommandNSSetSecure
   1071 {
   1072  public:
   1073 	CommandNSSASetSecure(Module *creator) : CommandNSSetSecure(creator, "nickserv/saset/secure", 2)
   1074 	{
   1075 		this->ClearSyntax();
   1076 		this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
   1077 	}
   1078 
   1079 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
   1080 	{
   1081 		this->Run(source, params[0], params[1]);
   1082 	}
   1083 
   1084 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
   1085 	{
   1086 		this->SendSyntax(source);
   1087 		source.Reply(" ");
   1088 		source.Reply(_("Turns %s's security features on or off for your\n"
   1089 				"nick. With \002SECURE\002 set, you must enter your password\n"
   1090 				"before you will be recognized as the owner of the nick,\n"
   1091 				"regardless of whether your address is on the access\n"
   1092 				"list. However, if you are on the access list, %s\n"
   1093 				"will not auto-kill you regardless of the setting of the\n"
   1094 				"\002KILL\002 option."), source.service->nick.c_str(), source.service->nick.c_str());
   1095 		return true;
   1096 	}
   1097 };
   1098 
   1099 class CommandNSSASetNoexpire : public Command
   1100 {
   1101  public:
   1102 	CommandNSSASetNoexpire(Module *creator) : Command(creator, "nickserv/saset/noexpire", 1, 2)
   1103 	{
   1104 		this->SetDesc(_("Prevent the nickname from expiring"));
   1105 		this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
   1106 	}
   1107 
   1108 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
   1109 	{
   1110 		if (Anope::ReadOnly)
   1111 		{
   1112 			source.Reply(READ_ONLY_MODE);
   1113 			return;
   1114 		}
   1115 
   1116 		NickAlias *na = NickAlias::Find(params[0]);
   1117 		if (na == NULL)
   1118 		{
   1119 			source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str());
   1120 			return;
   1121 		}
   1122 
   1123 		Anope::string param = params.size() > 1 ? params[1] : "";
   1124 
   1125 		if (param.equals_ci("ON"))
   1126 		{
   1127 			Log(LOG_ADMIN, source, this) << "to enable noexpire for " << na->nick << " (" << na->nc->display << ")";
   1128 			na->Extend<bool>("NS_NO_EXPIRE");
   1129 			source.Reply(_("Nick %s \002will not\002 expire."), na->nick.c_str());
   1130 		}
   1131 		else if (param.equals_ci("OFF"))
   1132 		{
   1133 			Log(LOG_ADMIN, source, this) << "to disable noexpire for " << na->nick << " (" << na->nc->display << ")";
   1134 			na->Shrink<bool>("NS_NO_EXPIRE");
   1135 			source.Reply(_("Nick %s \002will\002 expire."), na->nick.c_str());
   1136 		}
   1137 		else
   1138 			this->OnSyntaxError(source, "NOEXPIRE");
   1139 	}
   1140 
   1141 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
   1142 	{
   1143 		this->SendSyntax(source);
   1144 		source.Reply(" ");
   1145 		source.Reply(_("Sets whether the given nickname will expire.  Setting this\n"
   1146 				"to \002ON\002 prevents the nickname from expiring."));
   1147 		return true;
   1148 	}
   1149 };
   1150 
   1151 class NSSet : public Module
   1152 {
   1153 	CommandNSSet commandnsset;
   1154 	CommandNSSASet commandnssaset;
   1155 
   1156 	CommandNSSetAutoOp commandnssetautoop;
   1157 	CommandNSSASetAutoOp commandnssasetautoop;
   1158 
   1159 	CommandNSSetDisplay commandnssetdisplay;
   1160 	CommandNSSASetDisplay commandnssasetdisplay;
   1161 
   1162 	CommandNSSetEmail commandnssetemail;
   1163 	CommandNSSASetEmail commandnssasetemail;
   1164 
   1165 	CommandNSSetKeepModes commandnssetkeepmodes;
   1166 	CommandNSSASetKeepModes commandnssasetkeepmodes;
   1167 
   1168 	CommandNSSetKill commandnssetkill;
   1169 	CommandNSSASetKill commandnssasetkill;
   1170 
   1171 	CommandNSSetLanguage commandnssetlanguage;
   1172 	CommandNSSASetLanguage commandnssasetlanguage;
   1173 
   1174 	CommandNSSetMessage commandnssetmessage;
   1175 	CommandNSSASetMessage commandnssasetmessage;
   1176 
   1177 	CommandNSSetPassword commandnssetpassword;
   1178 	CommandNSSASetPassword commandnssasetpassword;
   1179 
   1180 	CommandNSSetSecure commandnssetsecure;
   1181 	CommandNSSASetSecure commandnssasetsecure;
   1182 
   1183 	CommandNSSASetNoexpire commandnssasetnoexpire;
   1184 
   1185 	SerializableExtensibleItem<bool> autoop, killprotect, kill_quick, kill_immed,
   1186 		message, secure, noexpire;
   1187 
   1188 	struct KeepModes : SerializableExtensibleItem<bool>
   1189 	{
   1190 		KeepModes(Module *m, const Anope::string &n) : SerializableExtensibleItem<bool>(m, n) { }
   1191 
   1192 		void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
   1193 		{
   1194 			SerializableExtensibleItem<bool>::ExtensibleSerialize(e, s, data);
   1195 
   1196 			if (s->GetSerializableType()->GetName() != "NickCore")
   1197 				return;
   1198 
   1199 			const NickCore *nc = anope_dynamic_static_cast<const NickCore *>(s);
   1200 			Anope::string modes;
   1201 			for (User::ModeList::const_iterator it = nc->last_modes.begin(); it != nc->last_modes.end(); ++it)
   1202 			{
   1203 				if (!modes.empty())
   1204 					modes += " ";
   1205 				modes += it->first;
   1206 				if (!it->second.empty())
   1207 					modes += "," + it->second;
   1208 			}
   1209 			data["last_modes"] << modes;
   1210 		}
   1211 
   1212 		void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
   1213 		{
   1214 			SerializableExtensibleItem<bool>::ExtensibleUnserialize(e, s, data);
   1215 
   1216 			if (s->GetSerializableType()->GetName() != "NickCore")
   1217 				return;
   1218 
   1219 			NickCore *nc = anope_dynamic_static_cast<NickCore *>(s);
   1220 			Anope::string modes;
   1221 			data["last_modes"] >> modes;
   1222 			nc->last_modes.clear();
   1223 			for (spacesepstream sep(modes); sep.GetToken(modes);)
   1224 			{
   1225 				size_t c = modes.find(',');
   1226 				if (c == Anope::string::npos)
   1227 					nc->last_modes.insert(std::make_pair(modes, ""));
   1228 				else
   1229 					nc->last_modes.insert(std::make_pair(modes.substr(0, c), modes.substr(c + 1)));
   1230 			}
   1231 		}
   1232 	} keep_modes;
   1233 
   1234 	/* email, passcode */
   1235 	PrimitiveExtensibleItem<std::pair<Anope::string, Anope::string > > ns_set_email;
   1236 
   1237  public:
   1238 	NSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
   1239 		commandnsset(this), commandnssaset(this),
   1240 		commandnssetautoop(this), commandnssasetautoop(this),
   1241 		commandnssetdisplay(this), commandnssasetdisplay(this),
   1242 		commandnssetemail(this), commandnssasetemail(this),
   1243 		commandnssetkeepmodes(this), commandnssasetkeepmodes(this),
   1244 		commandnssetkill(this), commandnssasetkill(this),
   1245 		commandnssetlanguage(this), commandnssasetlanguage(this),
   1246 		commandnssetmessage(this), commandnssasetmessage(this),
   1247 		commandnssetpassword(this), commandnssasetpassword(this),
   1248 		commandnssetsecure(this), commandnssasetsecure(this),
   1249 		commandnssasetnoexpire(this),
   1250 
   1251 		autoop(this, "AUTOOP"),
   1252 		killprotect(this, "KILLPROTECT"), kill_quick(this, "KILL_QUICK"),
   1253 		kill_immed(this, "KILL_IMMED"), message(this, "MSG"),
   1254 		secure(this, "NS_SECURE"), noexpire(this, "NS_NO_EXPIRE"),
   1255 
   1256 		keep_modes(this, "NS_KEEP_MODES"), ns_set_email(this, "ns_set_email")
   1257 	{
   1258 
   1259 	}
   1260 
   1261 	EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
   1262 	{
   1263 		NickCore *uac = source.nc;
   1264 
   1265 		if (command->name == "nickserv/confirm" && !params.empty() && uac)
   1266 		{
   1267 			std::pair<Anope::string, Anope::string> *n = ns_set_email.Get(uac);
   1268 			if (n)
   1269 			{
   1270 				if (params[0] == n->second)
   1271 				{
   1272 					uac->email = n->first;
   1273 					Log(LOG_COMMAND, source, command) << "to confirm their email address change to " << uac->email;
   1274 					source.Reply(_("Your email address has been changed to \002%s\002."), uac->email.c_str());
   1275 					ns_set_email.Unset(uac);
   1276 					return EVENT_STOP;
   1277 				}
   1278 			}
   1279 		}
   1280 
   1281 		return EVENT_CONTINUE;
   1282 	}
   1283 
   1284 	void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
   1285 	{
   1286 		if (chan->ci)
   1287 		{
   1288 			/* Only give modes if autoop is set */
   1289 			give_modes &= !user->Account() || autoop.HasExt(user->Account());
   1290 		}
   1291 	}
   1292 
   1293 	void OnPreNickExpire(NickAlias *na, bool &expire) anope_override
   1294 	{
   1295 		if (noexpire.HasExt(na))
   1296 			expire = false;
   1297 	}
   1298 
   1299 	void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
   1300 	{
   1301 		if (!show_hidden)
   1302 			return;
   1303 
   1304 		if (kill_immed.HasExt(na->nc))
   1305 			info.AddOption(_("Immediate protection"));
   1306 		else if (kill_quick.HasExt(na->nc))
   1307 			info.AddOption(_("Quick protection"));
   1308 		else if (killprotect.HasExt(na->nc))
   1309 			info.AddOption(_("Protection"));
   1310 		if (secure.HasExt(na->nc))
   1311 			info.AddOption(_("Security"));
   1312 		if (message.HasExt(na->nc))
   1313 			info.AddOption(_("Message mode"));
   1314 		if (autoop.HasExt(na->nc))
   1315 			info.AddOption(_("Auto-op"));
   1316 		if (noexpire.HasExt(na))
   1317 			info.AddOption(_("No expire"));
   1318 		if (keep_modes.HasExt(na->nc))
   1319 			info.AddOption(_("Keep modes"));
   1320 	}
   1321 
   1322 	void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
   1323 	{
   1324 		if (u->Account() && setter.GetUser() == u)
   1325 			u->Account()->last_modes = u->GetModeList();
   1326 	}
   1327 
   1328 	void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
   1329 	{
   1330 		if (u->Account() && setter.GetUser() == u)
   1331 			u->Account()->last_modes = u->GetModeList();
   1332 	}
   1333 
   1334 	void OnUserLogin(User *u) anope_override
   1335 	{
   1336 		if (keep_modes.HasExt(u->Account()))
   1337 		{
   1338 			User::ModeList modes = u->Account()->last_modes;
   1339 			for (User::ModeList::iterator it = modes.begin(); it != modes.end(); ++it)
   1340 			{
   1341 				UserMode *um = ModeManager::FindUserModeByName(it->first);
   1342 				/* if the null user can set the mode, then it's probably safe */
   1343 				if (um && um->CanSet(NULL))
   1344 					u->SetMode(NULL, it->first, it->second);
   1345 			}
   1346 		}
   1347 	}
   1348 };
   1349 
   1350 MODULE_INIT(NSSet)