ns_register.cpp

Go to the documentation of this file.
00001 /* NickServ core functions
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
00010  */
00011 
00012 /*************************************************************************/
00013 
00014 #include "module.h"
00015 
00016 static bool SendRegmail(User *u, const NickAlias *na, const BotInfo *bi);
00017 
00018 class CommandNSConfirm : public Command
00019 {
00020  public:
00021         CommandNSConfirm(Module *creator) : Command(creator, "nickserv/confirm", 1, 2)
00022         {
00023                 this->SetDesc(_("Confirm a passcode"));
00024                 this->SetSyntax(_("\037passcode\037"));
00025                 this->AllowUnregistered(true);
00026         }
00027 
00028         void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
00029         {
00030                 const Anope::string &passcode = params[0];
00031 
00032                 if (source.nc && !source.nc->HasExt("UNCONFIRMED") && source.HasPriv("nickserv/confirm"))
00033                 {
00034                         NickAlias *na = NickAlias::Find(passcode);
00035                         if (na == NULL)
00036                                 source.Reply(NICK_X_NOT_REGISTERED, passcode.c_str());
00037                         else if (na->nc->HasExt("UNCONFIRMED") == false)
00038                                 source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str());
00039                         else
00040                         {
00041                                 na->nc->Shrink("UNCONFIRMED");
00042                                 Log(LOG_ADMIN, source, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")";
00043                                 source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str());
00044                         }
00045                 }
00046                 else if (source.nc)
00047                 {
00048                         Anope::string *code = source.nc->GetExt<ExtensibleItemClass<Anope::string> *>("ns_register_passcode");
00049                         if (code != NULL && *code == passcode)
00050                         {
00051                                 NickCore *nc = source.nc;
00052                                 nc->Shrink("ns_register_passcode");
00053                                 Log(LOG_COMMAND, source, this) << "to confirm their email";
00054                                 source.Reply(_("Your email address of \002%s\002 has been confirmed."), source.nc->email.c_str());
00055                                 nc->Shrink("UNCONFIRMED");
00056 
00057                                 if (source.GetUser())
00058                                 {
00059                                         IRCD->SendLogin(source.GetUser());
00060                                         const NickAlias *na = NickAlias::Find(source.GetNick());
00061                                         if (!Config->NoNicknameOwnership && na != NULL && na->nc == source.GetAccount() && na->nc->HasExt("UNCONFIRMED") == false)
00062                                                 source.GetUser()->SetMode(NickServ, "REGISTERED");
00063                                 }
00064                         }
00065                         else
00066                                 source.Reply(_("Invalid passcode."));
00067                 }
00068                 else
00069                         source.Reply(_("Invalid passcode."));
00070 
00071                 return;
00072         }
00073 
00074         bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00075         {
00076                 this->SendSyntax(source);
00077                 source.Reply(" ");
00078                 source.Reply(_("This command is used by several commands as a way to confirm\n"
00079                                 "changes made to your account.\n"
00080                                 " \n"
00081                                 "This is most commonly used to confirm your email address once\n"
00082                                 "you register or change it.\n"
00083                                 " \n"
00084                                 "This is also used after the RESETPASS command has been used to\n"
00085                                 "force identify you to your nick so you may change your password."));
00086                 if (source.HasPriv("nickserv/confirm"))
00087                         source.Reply(_("Additionally, Services Operators with the \037nickserv/confirm\037 permission can\n"
00088                                 "replace \037passcode\037 with a users nick to force validate them."));
00089                 return true;
00090         }
00091 
00092         void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) anope_override
00093         {
00094                 source.Reply(NICK_CONFIRM_INVALID);
00095         }
00096 };
00097 
00098 class CommandNSRegister : public Command
00099 {
00100  public:
00101         CommandNSRegister(Module *creator) : Command(creator, "nickserv/register", 1, 2)
00102         {
00103                 this->SetDesc(_("Register a nickname"));
00104                 if (Config->NSForceEmail)
00105                         this->SetSyntax(_("\037password\037 \037email\037"));
00106                 else
00107                         this->SetSyntax(_("\037password\037 \037[email]\037"));
00108                 this->AllowUnregistered(true);
00109         }
00110 
00111         void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
00112         {
00113                 NickAlias *na;
00114                 size_t prefixlen = Config->NSGuestNickPrefix.length();
00115                 User *u = source.GetUser();
00116                 Anope::string u_nick = source.GetNick();
00117                 size_t nicklen = u_nick.length();
00118                 Anope::string pass = params[0];
00119                 Anope::string email = params.size() > 1 ? params[1] : "";
00120 
00121                 if (Anope::ReadOnly)
00122                 {
00123                         source.Reply(_("Sorry, nickname registration is temporarily disabled."));
00124                         return;
00125                 }
00126 
00127                 if (Config->NSRegistration.equals_ci("disable"))
00128                 {
00129                         source.Reply(_("Registration is currently disabled."));
00130                         return;
00131                 }
00132 
00133                 if (u && !u->HasMode("OPER") && Config->NickRegDelay && Anope::CurTime - u->timestamp < Config->NickRegDelay)
00134                 {
00135                         source.Reply(_("You must have been using this nick for at least %d seconds to register."), Config->NickRegDelay);
00136                         return;
00137                 }
00138 
00139                 /* Prevent "Guest" nicks from being registered. -TheShadow */
00140 
00141                 /* Guest nick can now have a series of between 1 and 7 digits.
00142                  *   --lara
00143                  */
00144                 if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && !u_nick.find_ci(Config->NSGuestNickPrefix) && u_nick.substr(prefixlen).find_first_not_of("1234567890") == Anope::string::npos)
00145                 {
00146                         source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
00147                         return;
00148                 }
00149 
00150                 if (!IRCD->IsNickValid(u_nick))
00151                 {
00152                         source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
00153                         return;
00154                 }
00155 
00156                 if (Config->RestrictOperNicks)
00157                         for (unsigned i = 0; i < Config->Opers.size(); ++i)
00158                         {
00159                                 Oper *o = Config->Opers[i];
00160 
00161                                 if (!source.IsOper() && u_nick.find_ci(o->name) != Anope::string::npos)
00162                                 {
00163                                         source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
00164                                         return;
00165                                 }
00166                         }
00167 
00168                 if (Config->NSForceEmail && email.empty())
00169                         this->OnSyntaxError(source, "");
00170                 else if (u && Anope::CurTime < u->lastnickreg + Config->NSRegDelay)
00171                         source.Reply(_("Please wait %d seconds before using the REGISTER command again."), (u->lastnickreg + Config->NSRegDelay) - Anope::CurTime);
00172                 else if ((na = NickAlias::Find(u_nick)))
00173                         source.Reply(NICK_ALREADY_REGISTERED, u_nick.c_str());
00174                 else if (pass.equals_ci(u_nick) || (Config->StrictPasswords && pass.length() < 5))
00175                         source.Reply(MORE_OBSCURE_PASSWORD);
00176                 else if (pass.length() > Config->PassLen)
00177                         source.Reply(PASSWORD_TOO_LONG);
00178                 else if (!email.empty() && !Mail::Validate(email))
00179                         source.Reply(MAIL_X_INVALID, email.c_str());
00180                 else
00181                 {
00182                         NickCore *nc = new NickCore(u_nick);
00183                         na = new NickAlias(u_nick, nc);
00184                         Anope::Encrypt(pass, nc->pass);
00185                         if (!email.empty())
00186                                 nc->email = email;
00187 
00188                         if (u)
00189                         {
00190                                 na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
00191                                 na->last_realname = u->realname;
00192 
00193                                 u->Login(nc);
00194 
00195                                 if (Config->NSAddAccessOnReg)
00196                                         nc->AddAccess(u->Mask());
00197                         }
00198 
00199                         Log(LOG_COMMAND, source, this) << "to register " << na->nick << " (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")";
00200 
00201                         FOREACH_MOD(I_OnNickRegister, OnNickRegister(na));
00202 
00203                         if (Config->NSAddAccessOnReg)
00204                                 source.Reply(_("Nickname \002%s\002 registered under your user@host-mask: %s"), u_nick.c_str(), na->nc->GetAccess(0).c_str());
00205                         else
00206                                 source.Reply(_("Nickname \002%s\002 registered."), u_nick.c_str());
00207 
00208                         Anope::string tmp_pass;
00209                         if (Anope::Decrypt(na->nc->pass, tmp_pass) == 1)
00210                                 source.Reply(_("Your password is \002%s\002 - remember this for later use."), tmp_pass.c_str());
00211 
00212                         if (Config->NSRegistration.equals_ci("admin"))
00213                         {
00214                                 nc->ExtendMetadata("UNCONFIRMED");
00215                                 source.Reply(_("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed."));
00216                         }
00217                         else if (Config->NSRegistration.equals_ci("mail"))
00218                         {
00219                                 nc->ExtendMetadata("UNCONFIRMED");
00220                                 if (SendRegmail(u, na, source.service))
00221                                 {
00222                                         source.Reply(_("A passcode has been sent to %s, please type \002%s%s CONFIRM <passcode>\002 to confirm your email address."), email.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str());
00223                                         source.Reply(_("If you do not confirm your email address within %s your account will expire."), Anope::Duration(Config->NSUnconfirmedExpire).c_str());
00224                                 }
00225                         }
00226                         else if (Config->NSRegistration.equals_ci("none"))
00227                         {
00228                                 if (u)
00229                                 {
00230                                         IRCD->SendLogin(u);
00231                                         if (!Config->NoNicknameOwnership && na->nc == u->Account() && na->nc->HasExt("UNCONFIRMED") == false)
00232                                                 u->SetMode(NickServ, "REGISTERED");
00233                                 }
00234                         }
00235 
00236                         if (u)
00237                                 u->lastnickreg = Anope::CurTime;
00238                 }
00239         }
00240 
00241         bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00242         {
00243                 this->SendSyntax(source);
00244                 source.Reply(" ");
00245                 source.Reply(_("Registers your nickname in the %s database. Once\n"
00246                                 "your nick is registered, you can use the \002SET\002 and \002ACCESS\002\n"
00247                                 "commands to configure your nick's settings as you like\n"
00248                                 "them. Make sure you remember the password you use when\n"
00249                                 "registering - you'll need it to make changes to your nick\n"
00250                                 "later. (Note that \002case matters!\002 \037ANOPE\037, \037Anope\037, and\n"
00251                                 "\037anope\037 are all different passwords!)\n"
00252                                 " \n"
00253                                 "Guidelines on choosing passwords:\n"
00254                                 " \n"
00255                                 "Passwords should not be easily guessable. For example,\n"
00256                                 "using your real name as a password is a bad idea. Using\n"
00257                                 "your nickname as a password is a much worse idea ;) and,\n"
00258                                 "in fact, %s will not allow it. Also, short\n"
00259                                 "passwords are vulnerable to trial-and-error searches, so\n"
00260                                 "you should choose a password at least 5 characters long.\n"
00261                                 "Finally, the space character cannot be used in passwords."),
00262                                 Config->NickServ.c_str(), Config->NickServ.c_str());
00263 
00264                 if (!Config->NSForceEmail)
00265                 {
00266                         source.Reply(" ");
00267                         source.Reply(_("The \037email\037 parameter is optional and will set the email\n"
00268                                         "for your nick immediately.\n"
00269                                         "Your privacy is respected; this e-mail won't be given to\n"
00270                                         "any third-party person. You may also wish to \002SET HIDE\002 it\n"
00271                                         "after registering if it isn't the default setting already."));
00272                 }
00273 
00274                 source.Reply(" ");
00275                 source.Reply(_("This command also creates a new group for your nickname,\n"
00276                                 "that will allow you to register other nicks later sharing\n"
00277                                 "the same configuration, the same set of memos and the\n"
00278                                 "same channel privileges."));
00279                 return true;
00280         }
00281 };
00282 
00283 class CommandNSResend : public Command
00284 {
00285  public:
00286         CommandNSResend(Module *creator) : Command(creator, "nickserv/resend", 0, 0)
00287         {
00288                 this->SetSyntax("");
00289         }
00290 
00291         void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
00292         {
00293                 if (!Config->NSRegistration.equals_ci("mail"))
00294                         return;
00295 
00296                 const NickAlias *na = NickAlias::Find(source.GetNick());
00297 
00298                 if (na == NULL)
00299                         source.Reply(NICK_NOT_REGISTERED);
00300                 else if (na->nc != source.GetAccount() || source.nc->HasExt("UNCONFIRMED") == false)
00301                         source.Reply(_("Your account is already confirmed."));
00302                 else
00303                 {
00304                         if (Anope::CurTime < source.nc->lastmail + Config->NSResendDelay)
00305                                 source.Reply(_("Cannot send mail now; please retry a little later."));
00306                         else if (SendRegmail(source.GetUser(), na, source.service))
00307                         {
00308                                 na->nc->lastmail = Anope::CurTime;
00309                                 source.Reply(_("Your passcode has been re-sent to %s."), na->nc->email.c_str());
00310                                 Log(LOG_COMMAND, source, this) << "to resend registration verification code";
00311                         }
00312                         else
00313                                 Log(this->owner) << "Unable to resend registration verification code for " << source.GetNick();
00314                 }
00315 
00316                 return;
00317         }
00318 
00319         bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00320         {
00321                 if (!Config->NSRegistration.equals_ci("mail"))
00322                         return false;
00323 
00324                 this->SendSyntax(source);
00325                 source.Reply(" ");
00326                 source.Reply(_("This command will re-send the auth code (also called passcode)\n"
00327                                 "to the e-mail address of the nickname in the database."));
00328                 return true;
00329         }
00330 
00331         void OnServHelp(CommandSource &source) anope_override
00332         {
00333                 if (Config->NSRegistration.equals_ci("mail"))
00334                         Command::OnServHelp(source);
00335         }
00336 };
00337 
00338 class NSRegister : public Module
00339 {
00340         CommandNSRegister commandnsregister;
00341         CommandNSConfirm commandnsconfirm;
00342         CommandNSResend commandnsrsend;
00343 
00344  public:
00345         NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00346                 commandnsregister(this), commandnsconfirm(this), commandnsrsend(this)
00347         {
00348                 this->SetAuthor("Anope");
00349 
00350                 if (Config->NSRegistration.equals_ci("disable"))
00351                         throw ModuleException("Module will not load with nickserv:registration disabled.");
00352         }
00353 };
00354 
00355 static bool SendRegmail(User *u, const NickAlias *na, const BotInfo *bi)
00356 {
00357         NickCore *nc = na->nc;
00358 
00359         Anope::string *code = na->nc->GetExt<ExtensibleItemClass<Anope::string> *>("ns_register_passcode");
00360         Anope::string codebuf;
00361         if (code == NULL)
00362         {
00363                 int chars[] = {
00364                         ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
00365                         'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
00366                         'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
00367                         'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
00368                         'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
00369                 };
00370                 int idx, min = 1, max = 62;
00371                 for (idx = 0; idx < 9; ++idx)
00372                         codebuf += chars[1 + static_cast<int>((static_cast<float>(max - min)) * static_cast<uint16_t>(rand()) / 65536.0) + min];
00373                 nc->Extend("ns_register_passcode", new ExtensibleItemClass<Anope::string>(codebuf));
00374         }
00375         else
00376                 codebuf = *code;
00377 
00378         Anope::string subject = Language::Translate(na->nc, Config->MailRegistrationSubject.c_str());
00379         Anope::string message = Language::Translate(na->nc, Config->MailRegistrationMessage.c_str());
00380 
00381         subject = subject.replace_all_cs("%n", na->nick);
00382         subject = subject.replace_all_cs("%N", Config->NetworkName);
00383         subject = subject.replace_all_cs("%c", codebuf);
00384 
00385         message = message.replace_all_cs("%n", na->nick);
00386         message = message.replace_all_cs("%N", Config->NetworkName);
00387         message = message.replace_all_cs("%c", codebuf);
00388 
00389         return Mail::Send(u, nc, bi, subject, message);
00390 }
00391 
00392 MODULE_INIT(NSRegister)