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> ¶ms) 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> ¶ms) 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> ¶ms) 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)