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:
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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 ""
|
||||
|
||||
|
||||
@@ -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> ¶ms) 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
@@ -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> ¶ms) 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> ¶ms) 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
@@ -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> ¶ms) 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> ¶ms) 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."));
|
||||
|
||||
@@ -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> ¶ms) 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> ¶ms) 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)
|
||||
|
||||
Reference in New Issue
Block a user