summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/nickserv.example.conf22
-rw-r--r--include/config.h4
-rw-r--r--modules/commands/ns_ghost.cpp214
-rw-r--r--modules/commands/ns_recover.cpp264
-rw-r--r--modules/commands/ns_release.cpp139
-rw-r--r--modules/protocol/unreal.cpp1
-rw-r--r--src/config.cpp2
7 files changed, 193 insertions, 453 deletions
diff --git a/data/nickserv.example.conf b/data/nickserv.example.conf
index 16f241fd3..d35e545e9 100644
--- a/data/nickserv.example.conf
+++ b/data/nickserv.example.conf
@@ -374,16 +374,6 @@ command { service = "NickServ"; name = "GETEMAIL"; command = "nickserv/getemail"
#command { service = "NickServ"; name = "GETPASS"; command = "nickserv/getpass"; permission = "nickserv/getpass"; }
/*
- * ns_ghost
- *
- * Provides the command nickserv/ghost.
- *
- * Used for disconnecting "ghost" sessions.
- */
-module { name = "ns_ghost" }
-command { service = "NickServ"; name = "GHOST"; command = "nickserv/ghost"; }
-
-/*
* ns_group
*
* Provides the commands nickserv/group, nickserv/glist, and nickserv/ungroup.
@@ -441,7 +431,7 @@ command { service = "NickServ"; name = "LOGOUT"; command = "nickserv/logout"; }
*
* Provides the command nickserv/recover.
*
- * Used for forcing someone on your nick to a guest nick.
+ * Used for recovering your nick from services or another user.
*/
module { name = "ns_recover" }
command { service = "NickServ"; name = "RECOVER"; command = "nickserv/recover"; }
@@ -459,16 +449,6 @@ command { service = "NickServ"; name = "REGISTER"; command = "nickserv/register"
command { service = "NickServ"; name = "RESEND"; command = "nickserv/resend"; }
/*
- * ns_release
- *
- * Provides the command nickserv/release.
- *
- * Used for releasing names held by nickserv/recover.
- */
-module { name = "ns_release" }
-command { service = "NickServ"; name = "RELEASE"; command = "nickserv/release"; }
-
-/*
* ns_resetpass
*
* Provides the command nickserv/resetpass.
diff --git a/include/config.h b/include/config.h
index ad772033d..7e19ba32f 100644
--- a/include/config.h
+++ b/include/config.h
@@ -550,8 +550,8 @@ class CoreExport ServerConfig
time_t NSKill;
/* Modes set on a user when they identify */
Anope::string NSModesOnID;
- /* Restore nick/channels on ghost */
- bool NSRestoreOnGhost;
+ /* Restore nick/channels on recover */
+ bool NSRestoreOnRecover;
/* Whether or not to use SASL */
bool NSSASL;
diff --git a/modules/commands/ns_ghost.cpp b/modules/commands/ns_ghost.cpp
deleted file mode 100644
index 0f53d849e..000000000
--- a/modules/commands/ns_ghost.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2012 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"
-
-struct NSGhostExtensibleInfo : ExtensibleItem, std::map<Anope::string, ChannelStatus> { };
-
-class NSGhostRequest : public IdentifyRequest
-{
- CommandSource source;
- Command *cmd;
- Reference<User> u;
-
- public:
- NSGhostRequest(Module *o, CommandSource &src, Command *c, User *user, const Anope::string &pass) : IdentifyRequest(o, user->nick, pass), source(src), cmd(c), u(user) { }
-
- void OnSuccess() anope_override
- {
- if (!source.GetUser() || !source.service || !u)
- return;
-
- if (Config->NSRestoreOnGhost)
- {
- if (u->Account() != NULL)
- source.GetUser()->Login(u->Account());
-
- if (!u->chans.empty())
- {
- NSGhostExtensibleInfo *ei = new NSGhostExtensibleInfo;
- for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
- (*ei)[(*it)->chan->name] = *(*it)->status;
-
- source.GetUser()->Extend("ns_ghost_info", ei);
- }
- }
-
- Log(LOG_COMMAND, source, cmd) << "for " << GetAccount();
- Anope::string buf = "GHOST command used by " + source.GetNick();
- u->Kill(source.service->nick, buf);
- source.Reply(_("Ghost with your nick has been killed."));
-
- if (Config->NSRestoreOnGhost)
- IRCD->SendForceNickChange(source.GetUser(), GetAccount(), Anope::CurTime);
- }
-
- void OnFail() anope_override
- {
- if (!u)
- ;
- else if (!NickAlias::Find(GetAccount()))
- source.Reply(NICK_X_NOT_REGISTERED, GetAccount().c_str());
- else
- {
- source.Reply(ACCESS_DENIED);
- if (!GetPassword().empty())
- {
- Log(LOG_COMMAND, source, cmd) << "with an invalid password for " << GetAccount();
- if (source.GetUser())
- source.GetUser()->BadPassword();
- }
- }
- }
-};
-
-class CommandNSGhost : public Command
-{
- public:
- CommandNSGhost(Module *creator) : Command(creator, "nickserv/ghost", 1, 2)
- {
- this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
- this->SetDesc(_("Disconnects a \"ghost\" IRC session using your nick"));
- this->SetSyntax("\037nickname\037 [\037password\037]");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &nick = params[0];
- const Anope::string &pass = params.size() > 1 ? params[1] : "";
-
- User *user = User::Find(nick, true);
- const NickAlias *na = NickAlias::Find(nick);
-
- if (!user)
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
- else if (user->server == Me)
- source.Reply(_("\2%s\2 is a services enforcer."), user->nick.c_str());
- else if (!user->IsIdentified())
- source.Reply(_("You may not ghost an unidentified user, use RECOVER instead."));
- else if (!na)
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- else if (na->nc->HasFlag(NI_SUSPENDED))
- source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
- else if (nick.equals_ci(source.GetNick()))
- source.Reply(_("You can't ghost yourself!"));
- else
- {
- bool ok = false;
- if (source.GetAccount() == na->nc)
- ok = true;
- else if (!na->nc->HasFlag(NI_SECURE) && source.GetUser() && na->nc->IsOnAccess(source.GetUser()))
- ok = true;
- else if (source.GetUser() && !source.GetUser()->fingerprint.empty() && na->nc->FindCert(source.GetUser()->fingerprint))
- ok = true;
-
- if (ok == false && !pass.empty())
- {
- NSGhostRequest *req = new NSGhostRequest(owner, source, this, user, pass);
- FOREACH_MOD(I_OnCheckAuthentication, OnCheckAuthentication(source.GetUser(), req));
- req->Dispatch();
- }
- else
- {
- NSGhostRequest req(owner, source, this, user, pass);
-
- if (ok)
- req.OnSuccess();
- else
- req.OnFail();
- }
- }
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Terminates a \"ghost\" IRC session using your nick. A\n"
- "ghost\" session is one which is not actually connected,\n"
- "but which the IRC server believes is still online for one\n"
- "reason or another. Typically, this happens if your\n"
- "computer crashes or your Internet or modem connection\n"
- "goes down while you're on IRC.\n"
- " \n"
- "In order to use the \002GHOST\002 command for a nick, your\n"
- "current address as shown in /WHOIS must be on that nick's\n"
- "access list, you must be identified and in the group of\n"
- "that nick, or you must supply the correct password for\n"
- "the nickname."));
- return true;
- }
-};
-
-class NSGhost : public Module
-{
- CommandNSGhost commandnsghost;
-
- public:
- NSGhost(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
- commandnsghost(this)
- {
- this->SetAuthor("Anope");
-
- if (Config->NoNicknameOwnership)
- throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
-
- Implementation i[] = { I_OnUserNickChange, I_OnJoinChannel };
- ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
- }
-
- ~NSGhost()
- {
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- it->second->Shrink("ns_ghost_info");
- }
-
- void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
- {
- if (Config->NSRestoreOnGhost)
- {
- NSGhostExtensibleInfo *ei = u->GetExt<NSGhostExtensibleInfo *>("ns_ghost_info");
-
- if (ei != NULL)
- for (std::map<Anope::string, ChannelStatus>::iterator it = ei->begin(), it_end = ei->end(); it != it_end; ++it)
- IRCD->SendSVSJoin(NickServ, u->GetUID(), it->first, "");
- }
- }
-
- void OnJoinChannel(User *u, Channel *c) anope_override
- {
- if (Config->NSRestoreOnGhost)
- {
- NSGhostExtensibleInfo *ei = u->GetExt<NSGhostExtensibleInfo *>("ns_ghost_info");
-
- if (ei != NULL)
- {
- std::map<Anope::string, ChannelStatus>::iterator it = ei->find(c->name);
- if (it != ei->end())
- {
- for (size_t j = CMODE_BEGIN + 1; j < CMODE_END; ++j)
- if (it->second.HasFlag(static_cast<ChannelModeName>(j)))
- c->SetMode(c->ci->WhoSends(), ModeManager::FindChannelModeByName(static_cast<ChannelModeName>(j)), u->GetUID());
-
- ei->erase(it);
- if (ei->empty())
- u->Shrink("ns_ghost_info");
- }
- }
- }
- }
-};
-
-MODULE_INIT(NSGhost)
diff --git a/modules/commands/ns_recover.cpp b/modules/commands/ns_recover.cpp
index 54a10f3b4..287224a1b 100644
--- a/modules/commands/ns_recover.cpp
+++ b/modules/commands/ns_recover.cpp
@@ -1,4 +1,3 @@
-
/* NickServ core functions
*
* (C) 2003-2012 Anope Team
@@ -14,46 +13,105 @@
#include "module.h"
+struct NSRecoverExtensibleInfo : ExtensibleItem, std::map<Anope::string, ChannelStatus> { };
+
class NSRecoverRequest : public IdentifyRequest
{
CommandSource source;
Command *cmd;
- Reference<NickAlias> na;
- Reference<User> u;
+ Anope::string user;
public:
- NSRecoverRequest(Module *m, CommandSource &src, Command *c, User *user, NickAlias *n, const Anope::string &pass) : IdentifyRequest(m, n->nc->display, pass), source(src), cmd(c), na(n), u(user) { }
+ NSRecoverRequest(Module *o, CommandSource &src, Command *c, const Anope::string &nick, const Anope::string &pass) : IdentifyRequest(o, nick, pass), source(src), cmd(c), user(nick) { }
void OnSuccess() anope_override
{
- if (!na || !u)
+ User *u = User::Find(user, true);
+ if (!source.GetUser() || !source.service)
return;
- u->SendMessage(source.service, FORCENICKCHANGE_NOW);
+ NickAlias *na = NickAlias::Find(user);
+ if (!na)
+ return;
- if (u->Account() == na->nc)
+ Log(LOG_COMMAND, source, cmd) << "for " << na->nick;
+
+ /* Nick is being held by us, release it */
+ if (na->HasFlag(NS_HELD))
+ {
+ na->Release();
+ source.Reply(_("Service's hold on \002%s\002 has been released."), na->nick.c_str());
+ }
+ else if (!u)
{
- IRCD->SendLogout(u);
- u->RemoveMode(NickServ, UMODE_REGISTERED);
+ source.Reply(_("No one is using your nick, and services are not holding it."));
}
+ // If the user being recovered is identified for the account of the nick then the user is the
+ // same person that is executing the command, so kill them off (old GHOST command).
+ else if (u->Account() == na->nc)
+ {
+ if (!source.GetAccount() && na->nc->HasFlag(NI_SECURE))
+ {
+ source.GetUser()->Login(u->Account());
+ Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << u->Account()->display;
+ }
+
+ if (Config->NSRestoreOnRecover)
+ {
+ if (!u->chans.empty())
+ {
+ NSRecoverExtensibleInfo *ei = new NSRecoverExtensibleInfo;
+ for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
+ (*ei)[(*it)->chan->name] = *(*it)->status;
+
+ source.GetUser()->Extend("ns_recover_info", ei);
+ }
+ }
+
+ u->SendMessage(NickServ, _("This nickname has been recovered by %s. If you did not do\n"
+ "this then %s may have your password, and you should change it.\n"),
+ source.GetNick().c_str(), source.GetNick().c_str());
+
+ Anope::string buf = source.command.upper() + " command used by " + source.GetNick();
+ u->Kill(source.service->nick, buf);
- u->Collide(na);
+ source.Reply(_("Ghost with your nick has been killed."));
- /* Convert Config->NSReleaseTimeout seconds to string format */
- Anope::string relstr = Anope::Duration(Config->NSReleaseTimeout);
- source.Reply(NICK_RECOVERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), na->nick.c_str(), relstr.c_str());
+ if (IRCD->CanSVSNick)
+ IRCD->SendForceNickChange(source.GetUser(), GetAccount(), Anope::CurTime);
+ }
+ /* User is not identified or not identified to the same account as the person using this command */
+ else
+ {
+ if (!source.GetAccount() && na->nc->HasFlag(NI_SECURE))
+ {
+ source.GetUser()->Login(na->nc); // Identify the user using the command if they arent identified
+ Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << na->nick << " (" << na->nc->display << ")";
+ }
+
+ u->SendMessage(NickServ, _("This nickname has been recovered by %s."), source.GetNick().c_str());
+ u->Collide(na);
+
+ if (IRCD->CanSVSNick)
+ {
+ /* If we can svsnick then release our hold and svsnick the user using the command */
+ na->Release();
+ IRCD->SendForceNickChange(source.GetUser(), GetAccount(), Anope::CurTime);
+ }
+ else
+ source.Reply(_("The user with your nick has been removed. Use this command again\n"
+ "to release services's hold on your nick."));
+ }
}
void OnFail() anope_override
{
- if (!na || !u)
- return;
-
source.Reply(ACCESS_DENIED);
if (!GetPassword().empty())
{
- Log(LOG_COMMAND, source, cmd) << "with invalid password for " << na->nick;
- u->BadPassword();
+ Log(LOG_COMMAND, source, cmd) << "with an invalid password for " << GetAccount();
+ if (source.GetUser())
+ source.GetUser()->BadPassword();
}
}
};
@@ -64,84 +122,71 @@ class CommandNSRecover : public Command
CommandNSRecover(Module *creator) : Command(creator, "nickserv/recover", 1, 2)
{
this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
- this->SetDesc(_("Kill another user who has taken your nick"));
- this->SetSyntax(_("\037nickname\037 [\037password\037]"));
+ this->SetDesc(_("Regains control of your nick"));
+ this->SetSyntax("\037nickname\037 [\037password\037]");
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
{
-
const Anope::string &nick = params[0];
const Anope::string &pass = params.size() > 1 ? params[1] : "";
- NickAlias *na;
- User *u2;
- if (!(u2 = User::Find(nick, true)))
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
- else if (u2->server == Me)
- source.Reply(_("\2%s\2 has already been recovered."), u2->nick.c_str());
- else if (!(na = NickAlias::Find(u2->nick)))
+ User *user = User::Find(nick, true);
+
+ if (user && source.GetUser() == user)
+ {
+ source.Reply(_("You can't %s yourself!"), source.command.lower().c_str());
+ return;
+ }
+
+ const NickAlias *na = NickAlias::Find(nick);
+
+ if (!na)
+ {
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
+ return;
+ }
else if (na->nc->HasFlag(NI_SUSPENDED))
+ {
source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
- else if (nick.equals_ci(source.GetNick()))
- source.Reply(_("You can't recover yourself!"));
+ return;
+ }
+
+ bool ok = false;
+ if (source.GetAccount() == na->nc)
+ ok = true;
+ else if (!na->nc->HasFlag(NI_SECURE) && source.GetUser() && na->nc->IsOnAccess(source.GetUser()))
+ ok = true;
+ else if (source.GetUser() && !source.GetUser()->fingerprint.empty() && na->nc->FindCert(source.GetUser()->fingerprint))
+ ok = true;
+
+ if (ok == false && !pass.empty())
+ {
+ NSRecoverRequest *req = new NSRecoverRequest(owner, source, this, na->nick, pass);
+ FOREACH_MOD(I_OnCheckAuthentication, OnCheckAuthentication(source.GetUser(), req));
+ req->Dispatch();
+ }
else
{
- bool ok = false;
- if (source.GetAccount() == na->nc)
- ok = true;
- else if (!na->nc->HasFlag(NI_SECURE) && source.GetUser() && na->nc->IsOnAccess(source.GetUser()))
- ok = true;
- else if (source.GetUser() && !source.GetUser()->fingerprint.empty() && na->nc->FindCert(source.GetUser()->fingerprint))
- ok = true;
-
- if (ok == false && !pass.empty())
- {
- NSRecoverRequest *req = new NSRecoverRequest(owner, source, this, u2, na, pass);
- FOREACH_MOD(I_OnCheckAuthentication, OnCheckAuthentication(source.GetUser(), req));
- req->Dispatch();
- }
- else
- {
- NSRecoverRequest req(owner, source, this, u2, na, pass);
+ NSRecoverRequest req(owner, source, this, na->nick, pass);
- if (ok)
- req.OnSuccess();
- else
- req.OnFail();
- }
+ if (ok)
+ req.OnSuccess();
+ else
+ req.OnFail();
}
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
{
- /* Convert Config->NSReleaseTimeout seconds to string format */
- Anope::string relstr = Anope::Duration(Config->NSReleaseTimeout);
-
this->SendSyntax(source);
source.Reply(" ");
- source.Reply(_("Allows you to recover your nickname if someone else has\n"
- "taken it; this does the same thing that %s does\n"
- "automatically if someone tries to use a kill-protected\n"
- "nick.\n"
- " \n"
- "When you give this command, %s will bring a fake\n"
- "user online with the same nickname as the user you're\n"
- "trying to recover your nick from. This causes the IRC\n"
- "servers to disconnect the other user. This fake user will\n"
- "remain online for %s to ensure that the other\n"
- "user does not immediately reconnect; after that time, you\n"
- "can reclaim your nick. Alternatively, use the \002RELEASE\002\n"
- "command (\002%s%s HELP RELEASE\002) to get the nick\n"
- "back sooner.\n"
- " \n"
- "In order to use the \002RECOVER\002 command for a nick, your\n"
- "current address as shown in /WHOIS must be on that nick's\n"
- "access list, you must be identified and in the group of\n"
- "that nick, or you must supply the correct password for\n"
- "the nickname."), Config->NickServ.c_str(), Config->NickServ.c_str(), relstr.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str());
-
+ source.Reply(_("Recovers your nick from another user or from services.\n"
+ "If services are currently holding your nick, the hold\n"
+ "will be released. If another user is holding your nick\n"
+ "and is identified they will be killed (similar to the old\n"
+ "GHOST command). If they are not identified they will be\n"
+ "forced off of the nick."));
return true;
}
};
@@ -158,6 +203,73 @@ class NSRecover : public Module
if (Config->NoNicknameOwnership)
throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
+
+ Implementation i[] = { I_OnUserNickChange, I_OnJoinChannel, I_OnShutdown, I_OnRestart };
+ ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
+ }
+
+ ~NSRecover()
+ {
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ it->second->Shrink("ns_recover_info");
+
+ OnShutdown();
+ }
+
+ void OnShutdown() anope_override
+ {
+ /* On shutdown, restart, or mod unload, remove all of our holds for nicks (svshold or qlines)
+ * because some IRCds do not allow us to have these automatically expire
+ */
+ for (nickalias_map::const_iterator it = NickAliasList->begin(); it != NickAliasList->end(); ++it)
+ it->second->Release();
+ }
+
+ void OnRestart() anope_override { OnShutdown(); }
+
+ void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
+ {
+ if (Config->NSRestoreOnRecover)
+ {
+ NSRecoverExtensibleInfo *ei = u->GetExt<NSRecoverExtensibleInfo *>("ns_recover_info");
+
+ if (ei != NULL)
+ for (std::map<Anope::string, ChannelStatus>::iterator it = ei->begin(), it_end = ei->end(); it != it_end;)
+ {
+ Channel *c = Channel::Find(it->first);
+ const Anope::string &cname = it->first;
+ ++it;
+
+ /* User might already be on the channel */
+ if (u->FindChannel(c))
+ this->OnJoinChannel(u, c);
+ else
+ IRCD->SendSVSJoin(NickServ, u->GetUID(), cname, "");
+ }
+ }
+ }
+
+ void OnJoinChannel(User *u, Channel *c) anope_override
+ {
+ if (Config->NSRestoreOnRecover)
+ {
+ NSRecoverExtensibleInfo *ei = u->GetExt<NSRecoverExtensibleInfo *>("ns_recover_info");
+
+ if (ei != NULL)
+ {
+ std::map<Anope::string, ChannelStatus>::iterator it = ei->find(c->name);
+ if (it != ei->end())
+ {
+ for (size_t j = CMODE_BEGIN + 1; j < CMODE_END; ++j)
+ if (it->second.HasFlag(static_cast<ChannelModeName>(j)))
+ c->SetMode(c->ci->WhoSends(), ModeManager::FindChannelModeByName(static_cast<ChannelModeName>(j)), u->GetUID());
+
+ ei->erase(it);
+ if (ei->empty())
+ u->Shrink("ns_recover_info");
+ }
+ }
+ }
}
};
diff --git a/modules/commands/ns_release.cpp b/modules/commands/ns_release.cpp
deleted file mode 100644
index a3004f462..000000000
--- a/modules/commands/ns_release.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2012 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 NSReleaseRequest : public IdentifyRequest
-{
- CommandSource source;
- Command *cmd;
- Reference<NickAlias> na;
-
- public:
- NSReleaseRequest(Module *m, CommandSource &src, Command *c, NickAlias *n, const Anope::string &pass) : IdentifyRequest(m, n->nc->display, pass), source(src), cmd(c), na(n) { }
-
- void OnSuccess() anope_override
- {
- if (!source.GetUser() || !na)
- return;
-
- bool override = source.GetAccount() != na->nc && source.HasPriv("nickserv/release");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, cmd) << "for nickname " << na->nick;
- na->Release();
- source.Reply(_("Services' hold on \002%s\002 has been released."), na->nick.c_str());
- }
-
- void OnFail() anope_override
- {
- source.Reply(ACCESS_DENIED);
- if (!GetPassword().empty())
- {
- Log(LOG_COMMAND, source, cmd) << "with invalid password for " << na->nick;
- if (!source.GetUser())
- source.GetUser()->BadPassword();
- }
- }
-};
-
-class CommandNSRelease : public Command
-{
- public:
- CommandNSRelease(Module *creator) : Command(creator, "nickserv/release", 1, 2)
- {
- this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
- this->SetDesc(_("Regain custody of your nick after RECOVER"));
- this->SetSyntax(_("\037nickname\037 [\037password\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &nick = params[0];
- Anope::string pass = params.size() > 1 ? params[1] : "";
- NickAlias *na;
-
- if (!(na = NickAlias::Find(nick)))
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- else if (na->nc->HasFlag(NI_SUSPENDED))
- source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
- else if (!na->HasFlag(NS_HELD))
- source.Reply(_("Nick \002%s\002 isn't being held."), nick.c_str());
- else
- {
- bool override = source.GetAccount() != na->nc && source.HasPriv("nickserv/release");
-
- bool ok = override;
- if (source.GetAccount() == na->nc)
- ok = true;
- else if (source.GetUser() && !na->nc->HasFlag(NI_SECURE) && na->nc->IsOnAccess(source.GetUser()))
- ok = true;
- else if (source.GetUser() && !source.GetUser()->fingerprint.empty() && na->nc->FindCert(source.GetUser()->fingerprint))
- ok = true;
-
- if (ok == false && !pass.empty())
- {
- NSReleaseRequest *req = new NSReleaseRequest(owner, source, this, na, pass);
- FOREACH_MOD(I_OnCheckAuthentication, OnCheckAuthentication(source.GetUser(), req));
- req->Dispatch();
- }
- else
- {
- NSReleaseRequest req(owner, source, this, na, pass);
-
- if (ok)
- req.OnSuccess();
- else
- req.OnFail();
- }
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- /* Convert Config->NSReleaseTimeout seconds to string format */
- Anope::string relstr = Anope::Duration(Config->NSReleaseTimeout);
-
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Instructs %s to remove any hold on your nickname\n"
- "caused by automatic kill protection or use of the \002RECOVER\002\n"
- "command. This holds lasts for %s;\n"
- "This command gets rid of them sooner.\n"
- " \n"
- "In order to use the \002RELEASE\002 command for a nick, your\n"
- "current address as shown in /WHOIS must be on that nick's\n"
- "access list, you must be identified and in the group of\n"
- "that nick, or you must supply the correct password for\n"
- "the nickname."), Config->NickServ.c_str(), relstr.c_str());
-
-
- return true;
- }
-};
-
-class NSRelease : public Module
-{
- CommandNSRelease commandnsrelease;
-
- public:
- NSRelease(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
- commandnsrelease(this)
- {
- this->SetAuthor("Anope");
-
- if (Config->NoNicknameOwnership)
- throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
- }
-};
-
-MODULE_INIT(NSRelease)
diff --git a/modules/protocol/unreal.cpp b/modules/protocol/unreal.cpp
index da96bd03b..ac15dc846 100644
--- a/modules/protocol/unreal.cpp
+++ b/modules/protocol/unreal.cpp
@@ -128,6 +128,7 @@ class UnrealIRCdProto : public IRCDProto
void SendSVSKillInternal(const BotInfo *source, User *user, const Anope::string &buf) anope_override
{
UplinkSocket::Message(source) << "h " << user->nick << " :" << buf;
+ user->KillInternal(source ? source->nick : Me->GetName(), buf);
}
void SendModeInternal(const BotInfo *bi, const User *u, const Anope::string &buf) anope_override
diff --git a/src/config.cpp b/src/config.cpp
index 48126cf7d..c78ecb8ed 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -1244,7 +1244,7 @@ ConfigItems::ConfigItems(ServerConfig *conf)
{"nickserv", "killquick", "20", new ValueContainerTime(&conf->NSKillQuick), DT_TIME, NoValidation},
{"nickserv", "kill", "60", new ValueContainerTime(&conf->NSKill), DT_TIME, NoValidation},
{"nickserv", "modesonid", "", new ValueContainerString(&conf->NSModesOnID), DT_STRING, NoValidation},
- {"nickserv", "restoreonghost", "yes", new ValueContainerBool(&conf->NSRestoreOnGhost), DT_BOOLEAN, NoValidation},
+ {"nickserv", "restoreonrecover", "yes", new ValueContainerBool(&conf->NSRestoreOnRecover), DT_BOOLEAN, NoValidation},
{"nickserv", "sasl", "yes", new ValueContainerBool(&conf->NSSASL), DT_BOOLEAN, NoValidation},
{"mail", "usemail", "no", new ValueContainerBool(&conf->UseMail), DT_BOOLEAN, ValidateEmailReg},
{"mail", "sendmailpath", "", new ValueContainerString(&conf->SendMailPath), DT_STRING, ValidateMail},