1
0
mirror of https://github.com/anope/anope.git synced 2026-06-19 22:04:47 +02:00
Files
anope/modules/nickserv/ns_resetpass.cpp
T
2025-06-25 17:50:01 +01:00

205 lines
5.7 KiB
C++

/* 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"
static bool SendResetEmail(User *u, const NickAlias *na, BotInfo *bi);
class CommandNSResetPass final
: public Command
{
public:
CommandNSResetPass(Module *creator) : Command(creator, "nickserv/resetpass", 2, 2)
{
this->SetDesc(_("Helps you reset lost passwords"));
this->SetSyntax(_("\037nickname\037 \037email\037"));
this->AllowUnregistered(true);
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const NickAlias *na;
if (!(na = NickAlias::Find(params[0])))
source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str());
else if (na->nc->HasExt("NS_SUSPENDED"))
source.Reply(NICK_X_SUSPENDED, na->nc->display.c_str());
else if (!na->nc->email.equals_ci(params[1]))
source.Reply(_("Incorrect email address."));
else
{
if (SendResetEmail(source.GetUser(), na, source.service))
{
Log(LOG_COMMAND, source, this) << "for " << na->nick << " (account: " << na->nc->display << ")";
source.Reply(_("Password reset email for \002%s\002 has been sent."), na->nick.c_str());
}
}
return;
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_(
"Sends a confirmation code to the nickname's email address with instructions on "
"how to reset their password. \037email\037 must be the email address associated "
"with the account."
));
return true;
}
};
struct ResetInfo final
{
Anope::string code;
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);
if (Config->GetModule("nickserv").Get<const Anope::string>("registration").equals_ci("mail"))
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)
, commandnsconfirmpassword(this, reset)
, commandnsresetpass(this)
, reset(this, "reset")
{
if (!Config->GetBlock("mail").Get<bool>("usemail"))
throw ModuleException("Not using mail.");
}
};
static bool SendResetEmail(User *u, const NickAlias *na, BotInfo *bi)
{
auto *ri = na->nc->Extend<ResetInfo>("reset");
ri->code = Anope::Random(Config->GetBlock("options").Get<size_t>("codelength", "15"));
ri->time = Anope::CurTime;
Anope::map<Anope::string> vars = {
{ "nick", na->nick },
{ "network", Config->GetBlock("networkinfo").Get<const Anope::string>("networkname") },
{ "code", ri->code },
};
auto subject = Anope::Template(Language::Translate(na->nc, Config->GetBlock("mail").Get<const Anope::string>("reset_subject").c_str()), vars);
auto message = Anope::Template(Language::Translate(na->nc, Config->GetBlock("mail").Get<const Anope::string>("reset_message").c_str()), vars);
return Mail::Send(u, na->nc, bi, subject, message);
}
MODULE_INIT(NSResetPass)