1
0
mirror of https://github.com/anope/anope.git synced 2026-06-12 19:14:47 +02:00

Rework how confirmation works and make it modular.

This commit is contained in:
Sadie Powell
2025-06-15 11:50:39 +01:00
parent c4c159d197
commit 61b0c82884
7 changed files with 564 additions and 237 deletions
+6 -5
View File
@@ -741,7 +741,8 @@ log
* nickserv/alist - Can see the channel access list of other users
* nickserv/auspex - Can see any information with /NICKSERV INFO
* nickserv/cert - Can modify other users certificate lists
* nickserv/confirm - Can confirm other users nicknames
* nickserv/confirm/email - Can confirm other users email address change
* nickserv/confirm/register - Can confirm other users account registration
* nickserv/drop - Can drop other users nicks
* nickserv/drop/display - Allows dropping display nicks when preservedisplay is enabled
* nickserv/drop/override - Allows dropping nicks without using a confirmation code
@@ -813,7 +814,7 @@ opertype
commands = "chanserv/list chanserv/suspend chanserv/topic memoserv/staff nickserv/list nickserv/suspend operserv/mode operserv/chankill operserv/akill operserv/session operserv/modinfo operserv/sqline operserv/oper operserv/kick operserv/ignore operserv/snline"
/* What privs (see above) this opertype has */
privs = "chanserv/auspex chanserv/no-register-limit memoserv/* nickserv/auspex nickserv/confirm"
privs = "chanserv/auspex chanserv/no-register-limit memoserv/* nickserv/auspex nickserv/confirm/*"
/*
* Modes to be set on users when they identify to accounts linked to this opertype.
@@ -990,7 +991,7 @@ mail
registration_message = "Hi,
You have requested to register the nickname {nick} on {network}.
Please type \" /msg NickServ CONFIRM {code} \" to complete registration.
Please type \" /msg NickServ CONFIRM REGISTER {code} \" to complete registration.
If you don't know why this mail was sent to you, please ignore it silently.
@@ -1008,7 +1009,7 @@ mail
reset_message = "Hi,
You have requested to have the password for {nick} reset.
To reset your password, type \" /msg NickServ CONFIRM {nick} {code} \"
To reset your password, type \" /msg NickServ CONFIRM RESETPASS {nick} {code} \"
If you don't know why this mail was sent to you, please ignore it silently.
@@ -1028,7 +1029,7 @@ mail
emailchange_message = "Hi,
You have requested to change your email address from {old_email} to {new_email}.
Please type \" /msg NickServ CONFIRM {code} \" to confirm this change.
Please type \" /msg NickServ CONFIRM EMAIL {code} \" to confirm this change.
If you don't know why this mail was sent to you, please ignore it silently.
+32 -5
View File
@@ -335,6 +335,16 @@ module
}
command { service = "NickServ"; name = "CERT"; command = "nickserv/cert"; }
/*
* ns_confirm
*
* Provides the command nickserv/confirm.
*
* Used for confirming previous account actions.
*/
module { name = "ns_confirm" }
command { service = "NickServ"; name = "CONFIRM"; command = "nickserv/confirm"; }
/*
* ns_drop
*
@@ -351,6 +361,7 @@ command { service = "NickServ"; name = "DROP"; command = "nickserv/drop"; }
* Provides various functionality relating to email addresses. This includes the
* following commands:
*
* - nickserv/confirm/email: Used for confirming email changes.
* - nickserv/getemail: Used for getting accounts by searching for emails.
* - nickserv/set/email, nickserv/saset/email: Used for setting an account's
* emailvaddress.
@@ -359,6 +370,12 @@ module
{
name = "ns_email"
/*
* The amount of time a user has after requesting a change of email address
* before it expires. Defaults to 1 day.
*/
#changeexpire = 1d
/*
* The limit to how many registered accounts can use the same email address.
* If set to 0 or left commented there will be no limit enforced when
@@ -373,7 +390,7 @@ module
*/
#remove_aliases = yes
}
command { service = "NickServ"; name = "CONFIRM EMAIL"; command = "nickserv/confirm/email"; }
command { service = "NickServ"; name = "GETEMAIL"; command = "nickserv/getemail"; permission = "nickserv/getemail"; group = "nickserv/admin"; }
command { service = "NickServ"; name = "SET EMAIL"; command = "nickserv/set/email"; }
command { service = "NickServ"; name = "SASET EMAIL"; command = "nickserv/saset/email"; permission = "nickserv/saset/email"; }
@@ -504,7 +521,7 @@ command { service = "NickServ"; name = "RELEASE"; command = "nickserv/recover";
/*
* ns_register
*
* Provides the commands nickserv/confirm, nickserv/register, and nickserv/resend.
* Provides the commands nickserv/confirm/register, nickserv/register, and nickserv/resend.
*
* Used for registering accounts.
*/
@@ -544,18 +561,28 @@ module
*/
#unconfirmedexpire = 1d
}
command { service = "NickServ"; name = "CONFIRM"; command = "nickserv/confirm"; }
command { service = "NickServ"; name = "CONFIRM REGISTER"; command = "nickserv/confirm/register"; }
command { service = "NickServ"; name = "REGISTER"; command = "nickserv/register"; }
command { service = "NickServ"; name = "RESEND"; command = "nickserv/resend"; }
/*
* ns_resetpass
*
* Provides the command nickserv/resetpass.
* Provides the command nickserv/confirm/resetpass and nickserv/resetpass.
*
* Used for resetting passwords by emailing users a temporary one.
*/
module { name = "ns_resetpass" }
module
{
name = "ns_resetpass"
/*
* The amount of time a user has after requesting a password reset before it
* expires. Defaults to 1 day.
*/
#resetexpire = 1d
}
command { service = "NickServ"; name = "CONFIRM RESETPASS"; command = "nickserv/confirm/resetpass"; }
command { service = "NickServ"; name = "RESETPASS"; command = "nickserv/resetpass"; }
/*
+79 -32
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Anope\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-15 11:49+0100\n"
"PO-Revision-Date: 2025-06-10 17:31+0100\n"
"POT-Creation-Date: 2025-06-16 10:00+0100\n"
"PO-Revision-Date: 2025-06-16 10:00+0100\n"
"Last-Translator: Sadie Powell <sadie@witchery.services>\n"
"Language-Team: English\n"
"Language: en_US\n"
@@ -519,6 +519,9 @@ msgstr ""
msgid "channel {ON | OFF}"
msgstr ""
msgid "code"
msgstr ""
msgid "email"
msgstr ""
@@ -621,9 +624,6 @@ msgstr ""
msgid "option setting"
msgstr ""
msgid "passcode"
msgstr ""
msgid "password"
msgstr ""
@@ -645,6 +645,9 @@ msgstr ""
msgid "server [reason]"
msgstr ""
msgid "type parameters"
msgstr ""
msgid "user modes"
msgstr ""
@@ -1155,6 +1158,9 @@ msgstr ""
msgid "<unknown>"
msgstr ""
msgid "@nickname"
msgstr ""
#, c-format
msgid "A confirmation email has been sent to %s. Follow the instructions in it to change your email address."
msgstr ""
@@ -1339,7 +1345,10 @@ msgstr ""
msgid "Additionally, Services Operators with the chanserv/drop/override permission can replace code with OVERRIDE to drop without a confirmation code."
msgstr ""
msgid "Additionally, Services Operators with the nickserv/confirm permission can replace passcode with a users nick to force validate them."
msgid "Additionally, Services Operators with the nickserv/confirm/email permission can specify @nickname instead of code to force confirm another user's change of email address."
msgstr ""
msgid "Additionally, Services Operators with the nickserv/confirm/register permission can specify @nickname instead of code to force confirm another user's account registration."
msgstr ""
msgid "Additionally, Services Operators with the nickserv/drop/override permission can replace code with OVERRIDE to drop without a confirmation code."
@@ -2213,7 +2222,28 @@ msgstr ""
msgid "Configures underlines kicker"
msgstr ""
msgid "Confirm a passcode"
msgid "Confirm a previous account registration"
msgstr ""
msgid "Confirm a previous action"
msgstr ""
msgid "Confirm a previous change of email address"
msgstr ""
msgid "Confirm a previous password reset"
msgstr ""
#, c-format
msgid "Confirms a password reset and identifies you to the specified account. You have %s after requesting a reset to do this before your request expires. Once you have done this you can set the password using %s."
msgstr ""
#, c-format
msgid "Confirms an account registration. You have %s after registration to do this before your registration expires."
msgstr ""
#, c-format
msgid "Confirms an change of email address. You have %s after requesting an email change to do this before your request expires."
msgstr ""
msgid "Control modes and mode locks on a channel"
@@ -3047,9 +3077,6 @@ msgstr ""
msgid "Invalid passcode has been entered, please check the email again, and retry."
msgstr ""
msgid "Invalid passcode."
msgstr ""
#, c-format
msgid "Invalid session limit. It must be a valid integer greater than or equal to zero and less than %d."
msgstr ""
@@ -3684,10 +3711,6 @@ msgstr ""
msgid "Nick %s is already an operator."
msgstr ""
#, c-format
msgid "Nick %s is already confirmed."
msgstr ""
#, c-format
msgid "Nick %s is an illegal nickname and cannot be used."
msgstr ""
@@ -5200,6 +5223,14 @@ msgid_plural "The email address %s has reached its usage limit of %u users."
msgstr[0] ""
msgstr[1] ""
#, c-format
msgid "The email address change confirmation code you specified for %s is incorrect."
msgstr ""
#, c-format
msgid "The email address change request for %s has expired."
msgstr ""
#, c-format
msgid "The email address of %s will now be hidden from %s INFO displays."
msgstr ""
@@ -5208,6 +5239,10 @@ msgstr ""
msgid "The email address of %s will now be shown in %s INFO displays."
msgstr ""
#, c-format
msgid "The email address of %s has been changed from %s to %s."
msgstr ""
#, c-format
msgid "The entry message list for %s is full."
msgstr ""
@@ -5288,6 +5323,18 @@ msgstr ""
msgid "The oper info list for %s is full."
msgstr ""
#, c-format
msgid "The password reset code you specified for %s is incorrect."
msgstr ""
#, c-format
msgid "The password reset request for %s has expired."
msgstr ""
#, c-format
msgid "The registration confirmation code you specified for %s is incorrect."
msgstr ""
#, c-format
msgid "The services access status of %s will now be hidden from %s INFO displays."
msgstr ""
@@ -5335,15 +5382,27 @@ msgstr ""
msgid "There is no bot assigned to %s anymore."
msgstr ""
#, c-format
msgid "There is no email address change confirmation pending for %s."
msgstr ""
msgid "There is no logon news."
msgstr ""
msgid "There is no oper news."
msgstr ""
#, c-format
msgid "There is no password reset confirmation pending for %s."
msgstr ""
msgid "There is no random news."
msgstr ""
#, c-format
msgid "There is no registration confirmation pending for %s."
msgstr ""
#, c-format
msgid "There is no such configuration block %s."
msgstr ""
@@ -5390,12 +5449,7 @@ msgstr ""
msgid "This command is an alias to the command %s."
msgstr ""
msgid ""
"This command is used by several commands as a way to confirm changes made to your account.\n"
"\n"
"This is most commonly used to confirm your email address once you register or change it.\n"
"\n"
"This is also used after the RESETPASS command has been used to force identify you to your nick so you may change your password."
msgid "This command is used by several commands as a way to actions changes made to your account. type can be one of:"
msgstr ""
msgid "This command lists information about the specified loaded module."
@@ -5876,7 +5930,8 @@ msgstr ""
msgid "You are now a super admin."
msgstr ""
msgid "You are now identified for your nick. Change your password now."
#, c-format
msgid "You are now identified as %s. Change your password now using %s."
msgstr ""
#, c-format
@@ -6192,17 +6247,9 @@ msgstr ""
msgid "Your account will expire, if not confirmed, in %s."
msgstr ""
#, c-format
msgid "Your email address has been changed to %s."
msgstr ""
msgid "Your email address is not allowed, choose a different one."
msgstr ""
#, c-format
msgid "Your email address of %s has been confirmed."
msgstr ""
#, c-format
msgid "Your email has been updated to %s"
msgstr ""
@@ -6269,9 +6316,6 @@ msgstr ""
msgid "Your password is too short. It must be longer than %u characters."
msgstr ""
msgid "Your password reset request has expired."
msgstr ""
msgid "Your requested vhost has been approved."
msgstr ""
@@ -6379,6 +6423,9 @@ msgstr ""
msgid "[nickname]"
msgstr ""
msgid "[nickname] code"
msgstr ""
msgid "[parameter]"
msgstr ""
+90
View File
@@ -0,0 +1,90 @@
/* NickServ core functions
*
* (C) 2003-2025 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*/
#include "module.h"
class CommandNSConfirm final
: public Command
{
public:
CommandNSConfirm(Module *creator)
: Command(creator, "nickserv/confirm", 1, 3)
{
this->AllowUnregistered(true);
this->SetDesc(_("Confirm a previous action"));
this->SetSyntax(_("\037type\037 \037parameters\037"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
this->OnSyntaxError(source, "");
return;
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_(
"This command is used by several commands as a way to actions changes made to "
"your account. \037type\037 can be one of:"
));
auto this_name = source.command;
auto hide_privileged_commands = Config->GetBlock("options").Get<bool>("hideprivilegedcommands");
auto hide_registered_commands = Config->GetBlock("options").Get<bool>("hideregisteredcommands");
HelpWrapper help;
for (const auto &[c_name, info] : source.service->commands)
{
if (c_name.find_ci(this_name + " ") == 0)
{
if (info.hide)
continue;
ServiceReference<Command> c("Command", info.name);
if (!c)
continue;
else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
continue;
else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
continue;
source.command = c_name;
c->OnServHelp(source, help);
}
}
help.SendTo(source);
source.Reply(_("Type \002%s\032\037option\037\002 for more information on a specific option."),
source.service->GetQueryCommand("generic/help", this_name).c_str());
return true;
}
};
class NSConfirm final
: public Module
{
private:
CommandNSConfirm commandnsconfirm;
public:
NSConfirm(const Anope::string &modname, const Anope::string &creator)
: Module(modname, creator, VENDOR)
, commandnsconfirm(this)
{
}
};
MODULE_INIT(NSConfirm)
+134 -24
View File
@@ -68,6 +68,133 @@ namespace
}
}
struct EmailChange final
{
Anope::string code;
Anope::string email;
time_t requested = Anope::CurTime;
};
class CommandNSConfirmEmail final
: public Command
{
private:
PrimitiveExtensibleItem<EmailChange> &ns_set_email;
public:
CommandNSConfirmEmail(Module *creator, PrimitiveExtensibleItem<EmailChange> &nse)
: Command(creator, "nickserv/confirm/email", 1, 2)
, ns_set_email(nse)
{
this->SetDesc(_("Confirm a previous change of email address"));
this->SetSyntax(_("\037code\037"));
this->SetSyntax(_("@\037nickname\037"), [](auto &source) { return source.HasPriv("nickserv/confirm/email"); });
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
auto has_priv = source.HasPriv("nickserv/confirm/email");
Anope::string code;
NickAlias *na;
if (params[0] == '@')
{
if (!has_priv)
{
source.Reply(ACCESS_DENIED);
return;
}
auto nick = params[0].substr(0);
na = NickAlias::Find(nick);
if (!na)
{
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
return;
}
}
else
{
code = params[0];
na = source.GetAccount()->na;
}
NickCore *nc = na->nc;
if (nc->HasExt("NS_SUSPENDED"))
{
source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
return;
}
auto *nse = ns_set_email.Get(nc);
if (!nse)
{
source.Reply(_("There is no email address change confirmation pending for %s."),
na->nick.c_str());
return;
}
if (!has_priv)
{
if (!code.equals_cs(nse->code))
{
source.Reply(_("The email address change confirmation code you specified for %s is incorrect."),
na->nick.c_str());
return;
}
auto changeexpire = Config->GetModule(owner).Get<time_t>("changeexpire", "1d");
if (nse->requested < Anope::CurTime - changeexpire)
{
ns_set_email.Unset(nc);
source.Reply(_("The email address change request for %s has expired."),
na->nick.c_str());
return;
}
}
if (!CheckLimitReached(source, nse->email, true))
{
ns_set_email.Unset(nc);
return;
}
auto old_email = nc->email;
nc->email = nse->email;
ns_set_email.Unset(nc);
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to confirm the email change of "
<< nc->display << " from " << old_email << " to " << nc->email;
source.Reply(_("The email address of %s has been changed from \002%s\002 to \002%s\002."),
na->nick.c_str(), old_email.c_str(), nc->email.c_str());
}
bool OnHelp(CommandSource &source, const Anope::string &) override
{
auto changeexpire = Config->GetModule(owner).Get<time_t>("changeexpire", "1d");
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_(
"Confirms an change of email address. You have %s after requesting an email "
"change to do this before your request expires."
),
Anope::Duration(changeexpire, source.GetAccount()).c_str());
if (source.HasPriv("nickserv/confirm/email"))
{
source.Reply(" ");
source.Reply(_(
"Additionally, Services Operators with the \037nickserv/confirm/email\037 "
"permission can specify @\037nickname\037 instead of \037code\037 to force "
"confirm another user's change of email address."
));
}
return true;
}
};
class CommandNSGetEmail final
: public Command
{
@@ -118,18 +245,16 @@ class CommandNSSetEmail
{
static bool SendConfirmMail(User *u, NickCore *nc, BotInfo *bi, const Anope::string &new_email)
{
Anope::string code = Anope::Random(Config->GetBlock("options").Get<size_t>("codelength", "15"));
std::pair<Anope::string, Anope::string> *n = nc->Extend<std::pair<Anope::string, Anope::string> >("ns_set_email");
n->first = new_email;
n->second = code;
auto *nse = nc->Extend<EmailChange>("ns_set_email");
nse->code = Anope::Random(Config->GetBlock("options").Get<size_t>("codelength", "15"));
nse->email = new_email;
Anope::map<Anope::string> vars = {
{ "old_email", nc->email },
{ "new_email", new_email },
{ "account", nc->display },
{ "network", Config->GetBlock("networkinfo").Get<const Anope::string>("networkname") },
{ "code", code },
{ "code", nse->code },
};
auto subject = Anope::Template(Config->GetBlock("mail").Get<const Anope::string>("emailchange_subject"), vars);
@@ -266,16 +391,18 @@ class NSEmail final
: public Module
{
private:
CommandNSConfirmEmail commandnsconfirmemail;
CommandNSGetEmail commandnsgetemail;
CommandNSSetEmail commandnssetemail;
CommandNSSASetEmail commandnssasetemail;
/* email, passcode */
PrimitiveExtensibleItem<std::pair<Anope::string, Anope::string>> ns_set_email;
PrimitiveExtensibleItem<EmailChange> ns_set_email;
public:
NSEmail(const Anope::string &modname, const Anope::string &creator)
: Module(modname, creator, VENDOR)
, commandnsconfirmemail(this, ns_set_email)
, commandnsgetemail(this)
, commandnssetemail(this)
, commandnssasetemail(this)
@@ -292,23 +419,6 @@ public:
EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
{
NickCore *uac = source.nc;
if (command->name == "nickserv/confirm" && !params.empty() && uac)
{
std::pair<Anope::string, Anope::string> *n = ns_set_email.Get(uac);
if (n)
{
if (params[0] == n->second)
{
uac->email = n->first;
Log(LOG_COMMAND, source, command) << "to confirm their email address change to " << uac->email;
source.Reply(_("Your email address has been changed to \002%s\002."), uac->email.c_str());
ns_set_email.Unset(uac);
return EVENT_STOP;
}
}
}
if (!source.IsOper() && command->name == "nickserv/register")
{
if (CheckLimitReached(source, params.size() > 1 ? params[1] : "", false))
+121 -117
View File
@@ -29,115 +29,6 @@ static bool SendRegmail(User *u, const NickAlias *na, BotInfo *bi);
static ServiceReference<NickServService> nickserv("NickServService", "NickServ");
class CommandNSConfirm final
: public Command
{
public:
CommandNSConfirm(Module *creator) : Command(creator, "nickserv/confirm", 1, 2)
{
this->SetDesc(_("Confirm a passcode"));
this->SetSyntax(_("\037passcode\037"));
this->AllowUnregistered(true);
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
Anope::string *code = source.nc ? source.nc->GetExt<Anope::string>("passcode") : NULL;
bool confirming_other = !code || *code != params[0];
if (source.nc && (!source.nc->HasExt("UNCONFIRMED") || (source.IsOper() && confirming_other)) && source.HasPriv("nickserv/confirm"))
{
const Anope::string &nick = params[0];
NickAlias *na = NickAlias::Find(nick);
if (na == NULL)
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
else if (!na->nc->HasExt("UNCONFIRMED"))
source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str());
else
{
na->nc->Shrink<bool>("UNCONFIRMED");
FOREACH_MOD(OnNickConfirm, (source.GetUser(), na->nc));
Log(LOG_ADMIN, source, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")";
source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str());
/* Login the users online already */
for (std::list<User *>::iterator it = na->nc->users.begin(); it != na->nc->users.end(); ++it)
{
User *u = *it;
IRCD->SendLogin(u, na);
NickAlias *u_na = NickAlias::Find(u->nick);
/* Set +r if they're on a nick in the group */
if (!Config->GetModule("nickserv").Get<bool>("nonicknameownership") && u_na && *u_na->nc == *na->nc)
u->SetMode(source.service, "REGISTERED");
}
}
}
else if (source.nc)
{
const Anope::string &passcode = params[0];
if (code != NULL && *code == passcode)
{
NickCore *nc = source.nc;
nc->Shrink<Anope::string>("passcode");
Log(LOG_COMMAND, source, this) << "to confirm their email";
source.Reply(_("Your email address of \002%s\002 has been confirmed."), source.nc->email.c_str());
nc->Shrink<bool>("UNCONFIRMED");
FOREACH_MOD(OnNickConfirm, (source.GetUser(), nc));
if (source.GetUser())
{
NickAlias *na = NickAlias::Find(source.GetNick());
if (na)
{
IRCD->SendLogin(source.GetUser(), na);
if (!Config->GetModule("nickserv").Get<bool>("nonicknameownership") && na->nc == source.GetAccount() && !na->nc->HasExt("UNCONFIRMED"))
source.GetUser()->SetMode(source.service, "REGISTERED");
}
}
}
else
source.Reply(_("Invalid passcode."));
}
else
source.Reply(NICK_IDENTIFY_REQUIRED);
return;
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_(
"This command is used by several commands as a way to confirm "
"changes made to your account."
"\n\n"
"This is most commonly used to confirm your email address once "
"you register or change it."
"\n\n"
"This is also used after the RESETPASS command has been used to "
"force identify you to your nick so you may change your password."
));
if (source.HasPriv("nickserv/confirm"))
{
source.Reply(_(
"Additionally, Services Operators with the \037nickserv/confirm\037 permission can "
"replace \037passcode\037 with a users nick to force validate them."
));
}
return true;
}
void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) override
{
source.Reply(NICK_CONFIRM_INVALID);
}
};
class CommandNSRegister final
: public Command
{
@@ -288,7 +179,7 @@ public:
{
const auto *code = GetCode(na->nc);
source.Reply(_("Your account is not confirmed. To confirm it, type \002%s\002."),
source.service->GetQueryCommand("nickserv/confirm", *code).c_str());
source.service->GetQueryCommand("nickserv/confirm/register", *code).c_str());
}
else if (nsregister.equals_ci("mail"))
source.Reply(_("Your account is not confirmed. To confirm it, follow the instructions that were emailed to you."));
@@ -345,6 +236,115 @@ public:
}
};
class CommandNSConfirmRegister final
: public Command
{
public:
CommandNSConfirmRegister(Module *creator)
: Command(creator, "nickserv/confirm/register", 1, 2)
{
this->SetDesc(_("Confirm a previous account registration"));
this->SetSyntax(_("\037code\037"));
this->SetSyntax(_("@\037nickname\037"), [](auto &source) { return source.HasPriv("nickserv/confirm/register"); });
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
auto has_priv = source.HasPriv("nickserv/confirm/register");
Anope::string code;
NickAlias *na;
if (params[0] == '@')
{
if (!has_priv)
{
source.Reply(ACCESS_DENIED);
return;
}
auto nick = params[0].substr(0);
na = NickAlias::Find(nick);
if (!na)
{
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
return;
}
}
else
{
code = params[0];
na = source.GetAccount()->na;
}
NickCore *nc = na->nc;
if (nc->HasExt("NS_SUSPENDED"))
{
source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
return;
}
auto *passcode = nc->GetExt<Anope::string>("passcode");
if (!passcode)
{
source.Reply(_("There is no registration confirmation pending for %s."),
na->nick.c_str());
return;
}
if (has_priv || !code.equals_cs(*passcode))
{
source.Reply(_("The registration confirmation code you specified for %s is incorrect."),
na->nick.c_str());
return;
}
na->nc->Shrink<bool>("UNCONFIRMED");
FOREACH_MOD(OnNickConfirm, (source.GetUser(), nc));
auto nonicknameownership = Config->GetModule("nickserv").Get<bool>("nonicknameownership");
for (auto *u : nc->users)
{
IRCD->SendLogin(u, na);
if (!nonicknameownership)
continue;
const auto &aliases = *nc->aliases;
auto it = std::find_if(aliases.begin(), aliases.end(), [&u](const auto *na) {
return na->nick.equals_ci(u->nick);
});
if (it != aliases.end())
u->SetMode(source.service, "REGISTERED"); // nick is in the group
}
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to confirm the registration of " << nc->display;
source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str());
}
bool OnHelp(CommandSource &source, const Anope::string &) override
{
auto unconfirmedexpire = Config->GetModule(owner).Get<time_t>("unconfirmedexpire", "1d");
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_(
"Confirms an account registration. You have %s after registration to do this "
"before your registration expires."
),
Anope::Duration(unconfirmedexpire, source.GetAccount()).c_str());
if (source.HasPriv("nickserv/confirm/register"))
{
source.Reply(" ");
source.Reply(_(
"Additionally, Services Operators with the \037nickserv/confirm/register\037 "
"permission can specify @\037nickname\037 instead of \037code\037 to force "
"confirm another user's account registration."
));
}
return true;
}
};
class CommandNSResend final
: public Command
{
@@ -407,16 +407,20 @@ class NSRegister final
: public Module
{
CommandNSRegister commandnsregister;
CommandNSConfirm commandnsconfirm;
CommandNSResend commandnsrsend;
CommandNSConfirmRegister commandnsconfirmregister;
CommandNSResend commandnsresend;
SerializableExtensibleItem<bool> unconfirmed;
SerializableExtensibleItem<Anope::string> passcode;
public:
NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
commandnsregister(this), commandnsconfirm(this), commandnsrsend(this), unconfirmed(this, "UNCONFIRMED"),
passcode(this, "passcode")
NSRegister(const Anope::string &modname, const Anope::string &creator)
: Module(modname, creator, VENDOR)
, commandnsregister(this)
, commandnsconfirmregister(this)
, commandnsresend(this)
, unconfirmed(this, "UNCONFIRMED")
, passcode(this, "passcode")
{
if (Config->GetModule(this).Get<const Anope::string>("registration").equals_ci("disable"))
throw ModuleException("Module " + this->name + " will not load with registration disabled.");
@@ -432,9 +436,9 @@ public:
u->SendMessage(NickServ, _("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed."));
else if (nsregister.equals_ci("code"))
{
const auto *code = GetCode(u->Account() );
const auto *code = GetCode(u->Account());
u->SendMessage(NickServ, _("Your account is not confirmed. To confirm it, type \002%s\002."),
NickServ->GetQueryCommand("nickserv/confirm", *code).c_str());
NickServ->GetQueryCommand("nickserv/confirm/register", *code).c_str());
}
else if (nsregister.equals_ci("mail"))
u->SendMessage(NickServ, _("Your account is not confirmed. To confirm it, follow the instructions that were emailed to you."));
+102 -54
View File
@@ -65,71 +65,119 @@ struct ResetInfo final
time_t time;
};
class CommandNSConfirmResetPass final
: public Command
{
private:
PrimitiveExtensibleItem<ResetInfo> &reset;
public:
CommandNSConfirmResetPass(Module *creator, PrimitiveExtensibleItem<ResetInfo> &r)
: Command(creator, "nickserv/confirm/resetpass", 1, 2)
, reset(r)
{
this->AllowUnregistered(true);
this->SetDesc(_("Confirm a previous password reset"));
this->SetSyntax(_("[\037nickname\037] \037code\037"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
Anope::string code, nick;
if (params.size() > 1)
{
nick = params[0];
code = params[1];
}
else
{
code = params[0];
nick = source.GetNick();
}
auto *na = NickAlias::Find(nick);
if (!na)
{
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
return;
}
NickCore *nc = na->nc;
if (nc->HasExt("NS_SUSPENDED"))
{
source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
return;
}
auto *ri = reset.Get(nc);
if (!ri)
{
source.Reply(_("There is no password reset confirmation pending for %s."),
na->nick.c_str());
return;
}
if (!code.equals_cs(ri->code))
{
source.Reply(_("The password reset code you specified for %s is incorrect."),
na->nick.c_str());
return;
}
auto resetexpire = Config->GetModule(owner).Get<time_t>("resetexpire", "1d");
if (ri->time < Anope::CurTime - resetexpire)
{
reset.Unset(nc);
source.Reply(_("The password reset request for %s has expired."),
na->nick.c_str());
return;
}
reset.Unset(nc);
nc->Shrink<bool>("UNCONFIRMED");
if (source.GetUser())
source.GetUser()->Identify(na);
Log(LOG_COMMAND, source, this) << "to reset their password and forcibly identify as " << na->nick;
source.Reply(_("You are now identified as %s. Change your password now using %s."),
na->nick.c_str(), source.service->GetQueryCommand("nickserv/set/password").c_str());
}
bool OnHelp(CommandSource &source, const Anope::string &) override
{
auto resetexpire = Config->GetModule(owner).Get<time_t>("resetexpire", "1d");
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_(
"Confirms a password reset and identifies you to the specified account. You have "
"%s after requesting a reset to do this before your request expires. Once you "
"have done this you can set the password using %s."
),
Anope::Duration(resetexpire, source.GetAccount()).c_str(),
source.service->GetQueryCommand("nickserv/set/password").c_str()
);
return true;
}
};
class NSResetPass final
: public Module
{
private:
CommandNSConfirmResetPass commandnsconfirmpassword;
CommandNSResetPass commandnsresetpass;
PrimitiveExtensibleItem<ResetInfo> reset;
public:
NSResetPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
commandnsresetpass(this), reset(this, "reset")
NSResetPass(const Anope::string &modname, const Anope::string &creator)
: Module(modname, creator, VENDOR)
, commandnsconfirmpassword(this, reset)
, commandnsresetpass(this)
, reset(this, "reset")
{
if (!Config->GetBlock("mail").Get<bool>("usemail"))
throw ModuleException("Not using mail.");
}
EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
{
if (command->name == "nickserv/confirm" && params.size() > 1)
{
if (Anope::ReadOnly)
{
source.Reply(READ_ONLY_MODE);
return EVENT_STOP;
}
NickAlias *na = NickAlias::Find(params[0]);
ResetInfo *ri = na ? reset.Get(na->nc) : NULL;
if (na && ri)
{
NickCore *nc = na->nc;
if (nc->HasExt("NS_SUSPENDED"))
{
source.Reply(NICK_X_SUSPENDED, nc->display.c_str());
return EVENT_STOP;
}
const Anope::string &passcode = params[1];
if (ri->time < Anope::CurTime - 3600)
{
reset.Unset(nc);
source.Reply(_("Your password reset request has expired."));
}
else if (passcode.equals_cs(ri->code))
{
reset.Unset(nc);
nc->Shrink<bool>("UNCONFIRMED");
Log(LOG_COMMAND, source, &commandnsresetpass) << "to confirm RESETPASS and forcefully identify as " << na->nick;
if (source.GetUser())
{
source.GetUser()->Identify(na);
}
source.Reply(_("You are now identified for your nick. Change your password now."));
}
else
return EVENT_CONTINUE;
return EVENT_STOP;
}
}
return EVENT_CONTINUE;
}
};
static bool SendResetEmail(User *u, const NickAlias *na, BotInfo *bi)