summaryrefslogtreecommitdiff
path: root/modules/nickserv/group.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/nickserv/group.cpp')
-rw-r--r--modules/nickserv/group.cpp393
1 files changed, 393 insertions, 0 deletions
diff --git a/modules/nickserv/group.cpp b/modules/nickserv/group.cpp
new file mode 100644
index 000000000..34910c075
--- /dev/null
+++ b/modules/nickserv/group.cpp
@@ -0,0 +1,393 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2017 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/cert.h"
+#include "modules/nickserv/group.h"
+
+class NSGroupRequestListener : public NickServ::IdentifyRequestListener
+{
+ CommandSource source;
+ Command *cmd;
+ Anope::string nick;
+ Reference<NickServ::Nick> target;
+
+ public:
+ NSGroupRequestListener(CommandSource &src, Command *c, const Anope::string &n, NickServ::Nick *targ) : source(src), cmd(c), nick(n), target(targ) { }
+
+ void OnSuccess(NickServ::IdentifyRequest *) override
+ {
+ if (!source.GetUser() || source.GetUser()->nick != nick || !target)
+ return;
+
+ User *u = source.GetUser();
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ /* If the nick is already registered, drop it. */
+ if (na)
+ {
+ EventManager::Get()->Dispatch(&Event::ChangeCoreDisplay::OnChangeCoreDisplay, na->GetAccount(), u->nick);
+ delete na;
+ }
+
+ na = Serialize::New<NickServ::Nick *>();
+ na->SetNick(nick);
+ na->SetAccount(target->GetAccount());
+ na->SetLastUsermask(u->GetIdent() + "@" + u->GetDisplayedHost());
+ na->SetLastRealname(u->realname);
+ na->SetLastSeen(Anope::CurTime);
+ na->SetTimeRegistered(Anope::CurTime);
+
+ u->Login(target->GetAccount());
+ EventManager::Get()->Dispatch(&Event::NickGroup::OnNickGroup, u, target);
+
+ cmd->logger.Command(LogType::COMMAND, source, _("{source} used {command} to make {2} join group of {3} ({4}) (email: {5})"),
+ source.GetSource(), source.GetCommand(), nick, target->GetNick(), target->GetAccount()->GetDisplay(),
+ !target->GetAccount()->GetEmail().empty() ? target->GetAccount()->GetEmail() : "none");
+
+ source.Reply(_("You are now in the group of \002{0}\002."), target->GetNick());
+
+ u->lastnickreg = Anope::CurTime;
+
+ }
+
+ void OnFail(NickServ::IdentifyRequest *) override
+ {
+ if (!source.GetUser())
+ return;
+
+ cmd->logger.Command(LogType::COMMAND, source, _("{source} used {command} and failed to group to {0}"), target->GetNick());
+ source.Reply(_("Password incorrect."));
+ source.GetUser()->BadPassword();
+ }
+};
+
+class CommandNSGroup : public Command
+{
+ ServiceReference<CertService> certservice;
+
+ public:
+ CommandNSGroup(Module *creator) : Command(creator, "nickserv/group", 0, 2)
+ {
+ this->SetDesc(_("Join a group"));
+ this->SetSyntax(_("\037[target]\037 \037[password]\037"));
+ this->AllowUnregistered(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *user = source.GetUser();
+
+ Anope::string nick;
+ if (params.empty())
+ {
+ NickServ::Account* core = source.GetAccount();
+ if (core)
+ nick = core->GetDisplay();
+ }
+ else
+ {
+ nick = params[0];
+ }
+
+ if (nick.empty())
+ {
+ this->SendSyntax(source);
+ return;
+ }
+
+ const Anope::string &pass = params.size() > 1 ? params[1] : "";
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, nickname grouping is temporarily disabled."));
+ return;
+ }
+
+ if (!IRCD->IsNickValid(source.GetNick()))
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), source.GetNick());
+ return;
+ }
+
+ if (Config->GetModule("nickserv/main")->Get<bool>("restrictopernicks"))
+ for (Oper *o : Serialize::GetObjects<Oper *>())
+ {
+ if (user != nullptr && !user->HasMode("OPER") && user->nick.find_ci(o->GetName()) != Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 may not be registered because it is too similar to an operator nick."), user->nick);
+ return;
+ }
+ }
+
+ NickServ::Nick *target, *na = NickServ::FindNick(source.GetNick());
+ time_t reg_delay = Config->GetModule("nickserv/main")->Get<time_t>("regdelay");
+ if (!(target = NickServ::FindNick(nick)))
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ if (user != nullptr && Anope::CurTime < user->lastnickreg + reg_delay)
+ {
+ source.Reply(_("Please wait \002{0}\002 seconds before using the \002{1}\002 command again."), (reg_delay + user->lastnickreg) - Anope::CurTime, source.GetCommand());
+ return;
+ }
+
+ if (target->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ {
+ logger.Command(LogType::COMMAND, source, _("{source} used {command} and tried to group to suspended nickname {0}"), target->GetNick());
+
+ source.Reply(_("\002{0}\002 is suspended."), target->GetNick());
+ return;
+ }
+
+ if (na && Config->GetModule(this->GetOwner())->Get<bool>("nogroupchange"))
+ {
+ source.Reply(_("Your nick is already registered."));
+ return;
+ }
+
+ if (na && target->GetAccount() == na->GetAccount())
+ {
+ source.Reply(_("You are already a member of the group of \002{0}\002."), target->GetNick());
+ return;
+ }
+
+ if (na && na->GetAccount() != source.GetAccount())
+ {
+ source.Reply(_("\002{0}\002 is already registered."), na->GetNick());
+ return;
+ }
+
+ if (na && Config->GetModule(this->GetOwner())->Get<bool>("nogroupchange"))
+ {
+ source.Reply(_("You are already registered."));
+ return;
+ }
+
+ const Anope::string &guestnick = Config->GetModule("nickserv/main")->Get<Anope::string>("guestnickprefix", "Guest");
+ unsigned maxaliases = Config->GetModule(this->GetOwner())->Get<unsigned>("maxaliases");
+
+ if (maxaliases && target->GetAccount()->GetRefs<NickServ::Nick *>().size() >= maxaliases && !target->GetAccount()->GetOper())
+ {
+ source.Reply(_("There are too many nicknames in your group."));
+ return;
+ }
+
+ if (source.GetNick().length() <= guestnick.length() + 7 &&
+ source.GetNick().length() >= guestnick.length() + 1 &&
+ !source.GetNick().find_ci(guestnick) && !source.GetNick().substr(guestnick.length()).find_first_not_of("1234567890"))
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), source.GetNick());
+ return;
+ }
+
+ bool ok = false;
+ if (!na && source.GetAccount() == target->GetAccount())
+ ok = true;
+
+ if (user != nullptr && certservice && certservice->Matches(user, target->GetAccount()))
+ ok = true;
+
+ if (ok == false && !pass.empty())
+ {
+ NickServ::IdentifyRequest *req = NickServ::service->CreateIdentifyRequest(new NSGroupRequestListener(source, this, source.GetNick(), target), this->GetOwner(), target->GetAccount()->GetDisplay(), pass);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, source.GetUser(), req);
+ req->Dispatch();
+ }
+ else
+ {
+ NSGroupRequestListener req(source, this, source.GetNick(), target);
+
+ if (ok)
+ req.OnSuccess(nullptr);
+ else
+ req.OnFail(nullptr);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command makes your nickname join the \037target\037 nickname's group."
+ " \037password\037 is the password of the target nickname.\n"
+ "\n"
+ "Nicknames in the same group share channel privileges, memos, and most settings, including password."
+ "\n"
+ "You may be able to use this command even if you have not registered your nick yet."
+ " If your nick is already registered, you'll need to identify yourself before using this command."));
+ return true;
+ }
+};
+
+class CommandNSUngroup : public Command
+{
+ public:
+ CommandNSUngroup(Module *creator) : Command(creator, "nickserv/ungroup", 0, 1)
+ {
+ this->SetDesc(_("Remove a nick from a group"));
+ this->SetSyntax(_("[\037nickname\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ Anope::string nick = !params.empty() ? params[0] : "";
+ NickServ::Nick *na = NickServ::FindNick(!nick.empty() ? nick : source.GetNick());
+
+ if (source.GetAccount()->GetRefs<NickServ::Nick *>().size() == 1)
+ {
+ source.Reply(_("Your nickname is not grouped to anything, so you can't ungroup it."));
+ return;
+ }
+
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), !nick.empty() ? nick : source.GetNick());
+ return;
+ }
+
+ if (na->GetAccount() != source.GetAccount())
+ {
+ source.Reply(_("\002{0}\002 is not in your group."), na->GetNick());
+ return;
+ }
+
+
+ NickServ::Account *oldcore = na->GetAccount();
+
+ if (na->GetNick().equals_ci(oldcore->GetDisplay()))
+ oldcore->SetDisplay(oldcore->GetRef<NickServ::Nick *>());
+
+ NickServ::Account *nc = Serialize::New<NickServ::Account *>();
+ nc->SetDisplay(na->GetNick());
+ na->SetAccount(nc);
+
+ nc->SetPassword(oldcore->GetPassword());
+ if (!oldcore->GetEmail().empty())
+ nc->SetEmail(oldcore->GetEmail());
+ nc->SetLanguage(oldcore->GetLanguage());
+
+ source.Reply(_("\002{0}\002 has been ungrouped from \002{1}\002."), na->GetNick(), oldcore->GetDisplay());
+
+ User *user = User::Find(na->GetNick(), true);
+ if (user)
+ /* The user on the nick who was ungrouped may be identified to the old group, set -r */
+ user->RemoveMode(source.service, "REGISTERED");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command ungroups your nickname, or if given, the specificed \037nickname\037, from the group it is in."
+ " The ungrouped nick keeps its registration time, password, email, and language. Everything else is reset."
+ " You may not ungroup yourself if there is only one nickname in your group."));
+ return true;
+ }
+};
+
+class CommandNSGList : public Command
+{
+ public:
+ CommandNSGList(Module *creator) : Command(creator, "nickserv/glist", 0, 1)
+ {
+ this->SetDesc(_("Lists all nicknames in your group"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &nick = !params.empty() ? params[0] : "";
+ NickServ::Account *nc;
+
+ if (!nick.empty() && source.IsServicesOper())
+ {
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ nc = na->GetAccount();
+ }
+ else
+ nc = source.GetAccount();
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Nick")).AddColumn(_("Expires"));
+ time_t nickserv_expire = Config->GetModule("nickserv/main")->Get<time_t>("expire", "21d"),
+ unconfirmed_expire = Config->GetModule("nickserv/main")->Get<time_t>("unconfirmedexpire", "1d");
+ for (NickServ::Nick *na2 : nc->GetRefs<NickServ::Nick *>())
+ {
+ Anope::string expires;
+ if (na2->IsNoExpire())
+ expires = _("Does not expire");
+ else if (!nickserv_expire || Anope::NoExpire)
+ ;
+ else if (na2->GetAccount()->IsUnconfirmed() && unconfirmed_expire)
+ expires = Anope::strftime(na2->GetTimeRegistered() + unconfirmed_expire, source.GetAccount());
+ else
+ expires = Anope::strftime(na2->GetLastSeen() + nickserv_expire, source.GetAccount());
+
+ ListFormatter::ListEntry entry;
+ entry["Nick"] = na2->GetNick();
+ entry["Expires"] = expires;
+ list.AddEntry(entry);
+ }
+
+ source.Reply(nc != source.GetAccount() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->GetDisplay().c_str());
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("%d nickname(s) in the group."), replies.size());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (source.IsServicesOper())
+ source.Reply(_("Without a parameter, lists all nicknames that are in your group.\n"
+ "\n"
+ "With a parameter, lists all nicknames that are in the group of the given nick.\n"
+ "Specifying a nick is limited to \002Services Operators\002."),
+ source.GetCommand());
+ else
+ source.Reply(_("Lists all nicknames in your group."));
+
+ return true;
+ }
+};
+
+class NSGroup : public Module
+{
+ CommandNSGroup commandnsgroup;
+ CommandNSUngroup commandnsungroup;
+ CommandNSGList commandnsglist;
+
+ public:
+ NSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsgroup(this)
+ , commandnsungroup(this)
+ , commandnsglist(this)
+ {
+ if (Config->GetModule("nickserv/main")->Get<bool>("nonicknameownership"))
+ throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
+ }
+};
+
+MODULE_INIT(NSGroup)