anope

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

ns_register.cpp (15804B)

      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 static bool SendRegmail(User *u, const NickAlias *na, BotInfo *bi);
     15 
     16 class CommandNSConfirm : public Command
     17 {
     18  public:
     19 	CommandNSConfirm(Module *creator) : Command(creator, "nickserv/confirm", 1, 2)
     20 	{
     21 		this->SetDesc(_("Confirm a passcode"));
     22 		this->SetSyntax(_("\037passcode\037"));
     23 		this->AllowUnregistered(true);
     24 	}
     25 
     26 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     27 	{
     28 		const Anope::string &passcode = params[0];
     29 
     30 		if (source.nc && (!source.nc->HasExt("UNCONFIRMED") || source.IsOper()) && source.HasPriv("nickserv/confirm"))
     31 		{
     32 			NickAlias *na = NickAlias::Find(passcode);
     33 			if (na == NULL)
     34 				source.Reply(NICK_X_NOT_REGISTERED, passcode.c_str());
     35 			else if (na->nc->HasExt("UNCONFIRMED") == false)
     36 				source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str());
     37 			else
     38 			{
     39 				na->nc->Shrink<bool>("UNCONFIRMED");
     40 				FOREACH_MOD(OnNickConfirm, (source.GetUser(), na->nc));
     41 				Log(LOG_ADMIN, source, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")";
     42 				source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str());
     43 
     44 				/* Login the users online already */
     45 				for (std::list<User *>::iterator it = na->nc->users.begin(); it != na->nc->users.end(); ++it)
     46 				{
     47 					User *u = *it;
     48 
     49 					IRCD->SendLogin(u, na);
     50 
     51 					NickAlias *u_na = NickAlias::Find(u->nick);
     52 
     53 					/* Set +r if they're on a nick in the group */
     54 					if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && u_na && *u_na->nc == *na->nc)
     55 						u->SetMode(source.service, "REGISTERED");
     56 				}
     57 			}
     58 		}
     59 		else if (source.nc)
     60 		{
     61 			Anope::string *code = source.nc->GetExt<Anope::string>("passcode");
     62 			if (code != NULL && *code == passcode)
     63 			{
     64 				NickCore *nc = source.nc;
     65 				nc->Shrink<Anope::string>("passcode");
     66 				Log(LOG_COMMAND, source, this) << "to confirm their email";
     67 				source.Reply(_("Your email address of \002%s\002 has been confirmed."), source.nc->email.c_str());
     68 				nc->Shrink<bool>("UNCONFIRMED");
     69 				FOREACH_MOD(OnNickConfirm, (source.GetUser(), nc));
     70 
     71 				if (source.GetUser())
     72 				{
     73 					NickAlias *na = NickAlias::Find(source.GetNick());
     74 					if (na)
     75 					{
     76 						IRCD->SendLogin(source.GetUser(), na);
     77 						if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && na->nc == source.GetAccount() && !na->nc->HasExt("UNCONFIRMED"))
     78 							source.GetUser()->SetMode(source.service, "REGISTERED");
     79 					}
     80 				}
     81 			}
     82 			else
     83 				source.Reply(_("Invalid passcode."));
     84 		}
     85 		else
     86 			source.Reply(_("Invalid passcode."));
     87 
     88 		return;
     89 	}
     90 
     91 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
     92 	{
     93 		this->SendSyntax(source);
     94 		source.Reply(" ");
     95 		source.Reply(_("This command is used by several commands as a way to confirm\n"
     96 				"changes made to your account.\n"
     97 				" \n"
     98 				"This is most commonly used to confirm your email address once\n"
     99 				"you register or change it.\n"
    100 				" \n"
    101 				"This is also used after the RESETPASS command has been used to\n"
    102 				"force identify you to your nick so you may change your password."));
    103 		if (source.HasPriv("nickserv/confirm"))
    104 			source.Reply(_("Additionally, Services Operators with the \037nickserv/confirm\037 permission can\n"
    105 				"replace \037passcode\037 with a users nick to force validate them."));
    106 		return true;
    107 	}
    108 
    109 	void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) anope_override
    110 	{
    111 		source.Reply(NICK_CONFIRM_INVALID);
    112 	}
    113 };
    114 
    115 class CommandNSRegister : public Command
    116 {
    117  public:
    118 	CommandNSRegister(Module *creator) : Command(creator, "nickserv/register", 1, 2)
    119 	{
    120 		this->SetDesc(_("Register a nickname"));
    121 		if (Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
    122 			this->SetSyntax(_("\037password\037 \037email\037"));
    123 		else
    124 			this->SetSyntax(_("\037password\037 \037[email]\037"));
    125 		this->AllowUnregistered(true);
    126 	}
    127 
    128 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    129 	{
    130 		User *u = source.GetUser();
    131 		Anope::string u_nick = source.GetNick();
    132 		size_t nicklen = u_nick.length();
    133 		Anope::string pass = params[0];
    134 		Anope::string email = params.size() > 1 ? params[1] : "";
    135 		const Anope::string &nsregister = Config->GetModule(this->owner)->Get<const Anope::string>("registration");
    136 
    137 		if (Anope::ReadOnly)
    138 		{
    139 			source.Reply(_("Sorry, nickname registration is temporarily disabled."));
    140 			return;
    141 		}
    142 
    143 		if (nsregister.equals_ci("disable"))
    144 		{
    145 			source.Reply(_("Registration is currently disabled."));
    146 			return;
    147 		}
    148 
    149 		time_t nickregdelay = Config->GetModule(this->owner)->Get<time_t>("nickregdelay");
    150 		time_t reg_delay = Config->GetModule("nickserv")->Get<time_t>("regdelay");
    151 		if (u && !u->HasMode("OPER") && nickregdelay && Anope::CurTime - u->timestamp < nickregdelay)
    152 		{
    153 			source.Reply(_("You must have been using this nick for at least %d seconds to register."), nickregdelay);
    154 			return;
    155 		}
    156 
    157 		/* Prevent "Guest" nicks from being registered. -TheShadow */
    158 
    159 		/* Guest nick can now have a series of between 1 and 7 digits.
    160 		 *   --lara
    161 		 */
    162 		const Anope::string &guestnick = Config->GetModule("nickserv")->Get<const Anope::string>("guestnickprefix", "Guest");
    163 		if (nicklen <= guestnick.length() + 7 && nicklen >= guestnick.length() + 1 && !u_nick.find_ci(guestnick) && u_nick.substr(guestnick.length()).find_first_not_of("1234567890") == Anope::string::npos)
    164 		{
    165 			source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
    166 			return;
    167 		}
    168 
    169 		if (!IRCD->IsNickValid(u_nick))
    170 		{
    171 			source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
    172 			return;
    173 		}
    174 
    175 		if (BotInfo::Find(u_nick, true))
    176 		{
    177 			source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
    178 			return;
    179 		}
    180 
    181 		if (Config->GetModule("nickserv")->Get<bool>("restrictopernicks"))
    182 		{
    183 			for (unsigned i = 0; i < Oper::opers.size(); ++i)
    184 			{
    185 				Oper *o = Oper::opers[i];
    186 
    187 				if (!source.IsOper() && u_nick.find_ci(o->name) != Anope::string::npos)
    188 				{
    189 					source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
    190 					return;
    191 				}
    192 			}
    193 		}
    194 
    195 		unsigned int passlen = Config->GetModule("nickserv")->Get<unsigned>("passlen", "32");
    196 
    197 		if (Config->GetModule("nickserv")->Get<bool>("forceemail", "yes") && email.empty())
    198 			this->OnSyntaxError(source, "");
    199 		else if (u && Anope::CurTime < u->lastnickreg + reg_delay)
    200 			source.Reply(_("Please wait %d seconds before using the REGISTER command again."), (u->lastnickreg + reg_delay) - Anope::CurTime);
    201 		else if (NickAlias::Find(u_nick) != NULL)
    202 			source.Reply(NICK_ALREADY_REGISTERED, u_nick.c_str());
    203 		else if (pass.equals_ci(u_nick) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && pass.length() < 5))
    204 			source.Reply(MORE_OBSCURE_PASSWORD);
    205 		else if (pass.length() > passlen)
    206 			source.Reply(PASSWORD_TOO_LONG, passlen);
    207 		else if (!email.empty() && !Mail::Validate(email))
    208 			source.Reply(MAIL_X_INVALID, email.c_str());
    209 		else
    210 		{
    211 			NickCore *nc = new NickCore(u_nick);
    212 			NickAlias *na = new NickAlias(u_nick, nc);
    213 			Anope::Encrypt(pass, nc->pass);
    214 			if (!email.empty())
    215 				nc->email = email;
    216 
    217 			if (u)
    218 			{
    219 				na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
    220 				na->last_realname = u->realname;
    221 			}
    222 			else
    223 				na->last_realname = source.GetNick();
    224 
    225 			Log(LOG_COMMAND, source, this) << "to register " << na->nick << " (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")";
    226 
    227 			if (na->nc->GetAccessCount())
    228 				source.Reply(_("Nickname \002%s\002 registered under your user@host-mask: %s"), u_nick.c_str(), na->nc->GetAccess(0).c_str());
    229 			else
    230 				source.Reply(_("Nickname \002%s\002 registered."), u_nick.c_str());
    231 
    232 			Anope::string tmp_pass;
    233 			if (Anope::Decrypt(na->nc->pass, tmp_pass) == 1)
    234 				source.Reply(_("Your password is \002%s\002 - remember this for later use."), tmp_pass.c_str());
    235 
    236 			if (nsregister.equals_ci("admin"))
    237 			{
    238 				nc->Extend<bool>("UNCONFIRMED");
    239 			}
    240 			else if (nsregister.equals_ci("mail"))
    241 			{
    242 				if (!email.empty())
    243 				{
    244 					nc->Extend<bool>("UNCONFIRMED");
    245 					SendRegmail(NULL, na, source.service);
    246 				}
    247 			}
    248 
    249 			FOREACH_MOD(OnNickRegister, (source.GetUser(), na, pass));
    250 
    251 			if (u)
    252 			{
    253 				// This notifies the user that if their registration is unconfirmed
    254 				u->Identify(na);
    255 				u->lastnickreg = Anope::CurTime;
    256 			}
    257 			else if (nc->HasExt("UNCONFIRMED"))
    258 			{
    259 				if (nsregister.equals_ci("admin"))
    260 					source.Reply(_("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed."));
    261 				else if (nsregister.equals_ci("mail"))
    262 					source.Reply(_("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you."));
    263 			}
    264 		}
    265 	}
    266 
    267 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    268 	{
    269 		this->SendSyntax(source);
    270 		source.Reply(" ");
    271 		source.Reply(_("Registers your nickname in the %s database. Once\n"
    272 				"your nick is registered, you can use the \002SET\002 and \002ACCESS\002\n"
    273 				"commands to configure your nick's settings as you like\n"
    274 				"them. Make sure you remember the password you use when\n"
    275 				"registering - you'll need it to make changes to your nick\n"
    276 				"later. (Note that \002case matters!\002 \037ANOPE\037, \037Anope\037, and\n"
    277 				"\037anope\037 are all different passwords!)\n"
    278 				" \n"
    279 				"Guidelines on choosing passwords:\n"
    280 				" \n"
    281 				"Passwords should not be easily guessable. For example,\n"
    282 				"using your real name as a password is a bad idea. Using\n"
    283 				"your nickname as a password is a much worse idea ;) and,\n"
    284 				"in fact, %s will not allow it. Also, short\n"
    285 				"passwords are vulnerable to trial-and-error searches, so\n"
    286 				"you should choose a password at least 5 characters long.\n"
    287 				"Finally, the space character cannot be used in passwords."),
    288 				source.service->nick.c_str(), source.service->nick.c_str());
    289 
    290 		if (!Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
    291 		{
    292 			source.Reply(" ");
    293 			source.Reply(_("The \037email\037 parameter is optional and will set the email\n"
    294 					"for your nick immediately.\n"
    295 					"Your privacy is respected; this e-mail won't be given to\n"
    296 					"any third-party person. You may also wish to \002SET HIDE\002 it\n"
    297 					"after registering if it isn't the default setting already."));
    298 		}
    299 
    300 		source.Reply(" ");
    301 		source.Reply(_("This command also creates a new group for your nickname,\n"
    302 				"that will allow you to register other nicks later sharing\n"
    303 				"the same configuration, the same set of memos and the\n"
    304 				"same channel privileges."));
    305 		return true;
    306 	}
    307 };
    308 
    309 class CommandNSResend : public Command
    310 {
    311  public:
    312 	CommandNSResend(Module *creator) : Command(creator, "nickserv/resend", 0, 0)
    313 	{
    314 		this->SetDesc(_("Resend registration confirmation email"));
    315 	}
    316 
    317 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    318 	{
    319 		if (!Config->GetModule(this->owner)->Get<const Anope::string>("registration").equals_ci("mail"))
    320 		{
    321 			source.Reply(ACCESS_DENIED);
    322 			return;
    323 		}
    324 
    325 		const NickAlias *na = NickAlias::Find(source.GetNick());
    326 
    327 		if (na == NULL)
    328 			source.Reply(NICK_NOT_REGISTERED);
    329 		else if (na->nc != source.GetAccount() || !source.nc->HasExt("UNCONFIRMED"))
    330 			source.Reply(_("Your account is already confirmed."));
    331 		else
    332 		{
    333 			if (Anope::CurTime < source.nc->lastmail + Config->GetModule(this->owner)->Get<time_t>("resenddelay"))
    334 				source.Reply(_("Cannot send mail now; please retry a little later."));
    335 			else if (SendRegmail(source.GetUser(), na, source.service))
    336 			{
    337 				na->nc->lastmail = Anope::CurTime;
    338 				source.Reply(_("Your passcode has been re-sent to %s."), na->nc->email.c_str());
    339 				Log(LOG_COMMAND, source, this) << "to resend registration verification code";
    340 			}
    341 			else
    342 				Log(this->owner) << "Unable to resend registration verification code for " << source.GetNick();
    343 		}
    344 
    345 		return;
    346 	}
    347 
    348 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    349 	{
    350 		if (!Config->GetModule(this->owner)->Get<const Anope::string>("registration").equals_ci("mail"))
    351 			return false;
    352 
    353 		this->SendSyntax(source);
    354 		source.Reply(" ");
    355 		source.Reply(_("This command will resend you the registration confirmation email."));
    356 		return true;
    357 	}
    358 
    359 	void OnServHelp(CommandSource &source) anope_override
    360 	{
    361 		if (Config->GetModule(this->owner)->Get<const Anope::string>("registration").equals_ci("mail"))
    362 			Command::OnServHelp(source);
    363 	}
    364 };
    365 
    366 class NSRegister : public Module
    367 {
    368 	CommandNSRegister commandnsregister;
    369 	CommandNSConfirm commandnsconfirm;
    370 	CommandNSResend commandnsrsend;
    371 
    372 	SerializableExtensibleItem<bool> unconfirmed;
    373 	SerializableExtensibleItem<Anope::string> passcode;
    374 
    375  public:
    376 	NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
    377 		commandnsregister(this), commandnsconfirm(this), commandnsrsend(this), unconfirmed(this, "UNCONFIRMED"),
    378 		passcode(this, "passcode")
    379 	{
    380 		if (Config->GetModule(this)->Get<const Anope::string>("registration").equals_ci("disable"))
    381 			throw ModuleException("Module " + this->name + " will not load with registration disabled.");
    382 	}
    383 
    384 	void OnNickIdentify(User *u) anope_override
    385 	{
    386 		BotInfo *NickServ;
    387 		if (unconfirmed.HasExt(u->Account()) && (NickServ = Config->GetClient("NickServ")))
    388 		{
    389 			const Anope::string &nsregister = Config->GetModule(this)->Get<const Anope::string>("registration");
    390 			if (nsregister.equals_ci("admin"))
    391 				u->SendMessage(NickServ, _("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed."));
    392 			else
    393 				u->SendMessage(NickServ, _("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you."));
    394 			const NickAlias *this_na = NickAlias::Find(u->Account()->display);
    395 			time_t time_registered = Anope::CurTime - this_na->time_registered;
    396 			time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
    397 			if (unconfirmed_expire > time_registered)
    398 				u->SendMessage(NickServ, _("Your account will expire, if not confirmed, in %s."), Anope::Duration(unconfirmed_expire - time_registered, u->Account()).c_str());
    399 		}
    400 	}
    401 
    402 	void OnPreNickExpire(NickAlias *na, bool &expire) anope_override
    403 	{
    404 		if (unconfirmed.HasExt(na->nc))
    405 		{
    406 			time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
    407 			if (unconfirmed_expire && Anope::CurTime - na->time_registered >= unconfirmed_expire)
    408 				expire = true;
    409 		}
    410 	}
    411 };
    412 
    413 static bool SendRegmail(User *u, const NickAlias *na, BotInfo *bi)
    414 {
    415 	NickCore *nc = na->nc;
    416 
    417 	Anope::string *code = na->nc->GetExt<Anope::string>("passcode");
    418 	if (code == NULL)
    419 	{
    420 		code = na->nc->Extend<Anope::string>("passcode");
    421 		*code = Anope::Random(9);
    422 	}
    423 
    424 	Anope::string subject = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("registration_subject").c_str()),
    425 		message = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("registration_message").c_str());
    426 
    427 	subject = subject.replace_all_cs("%n", na->nick);
    428 	subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
    429 	subject = subject.replace_all_cs("%c", *code);
    430 
    431 	message = message.replace_all_cs("%n", na->nick);
    432 	message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
    433 	message = message.replace_all_cs("%c", *code);
    434 
    435 	return Mail::Send(u, nc, bi, subject, message);
    436 }
    437 
    438 MODULE_INIT(NSRegister)