diff options
| author | Adam <Adam@anope.org> | 2015-12-14 16:52:24 -0500 |
|---|---|---|
| committer | Adam <Adam@anope.org> | 2015-12-14 16:52:24 -0500 |
| commit | 6302f980fe6dad83aab7d2dc1046dadea5ffd939 (patch) | |
| tree | 4c27d33f55fc32ce34827977932dac4b3e5ceedd /modules/nickserv | |
| parent | 64dac60071fab652745a6e7a06cf6b7bdbbd3625 (diff) | |
New source tree structure for modules. From commands/cs_access => chanserv/access etc.
Diffstat (limited to 'modules/nickserv')
37 files changed, 6766 insertions, 0 deletions
diff --git a/modules/nickserv/CMakeLists.txt b/modules/nickserv/CMakeLists.txt new file mode 100644 index 000000000..cd225a94d --- /dev/null +++ b/modules/nickserv/CMakeLists.txt @@ -0,0 +1 @@ +build_modules(${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/modules/nickserv/access.cpp b/modules/nickserv/access.cpp new file mode 100644 index 000000000..ba2116ac9 --- /dev/null +++ b/modules/nickserv/access.cpp @@ -0,0 +1,257 @@ +/* NickServ core functions + * + * (C) 2003-2014 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" +#include "modules/nickserv.h" +#include "modules/ns_access.h" + +class NickAccessImpl : public NickAccess +{ + public: + NickAccessImpl(Serialize::TypeBase *type) : NickAccess(type) { } + NickAccessImpl(Serialize::TypeBase *type, Serialize::ID id) : NickAccess(type, id) { } + + NickServ::Account *GetAccount() override; + void SetAccount(NickServ::Account *) override; + + Anope::string GetMask() override; + void SetMask(const Anope::string &) override; +}; + +class NickAccessType : public Serialize::Type<NickAccessImpl> +{ + public: + Serialize::ObjectField<NickAccessImpl, NickServ::Account *> account; + Serialize::Field<NickAccessImpl, Anope::string> mask; + + NickAccessType(Module *creator) : Serialize::Type<NickAccessImpl>(creator, "NSAccess") + , account(this, "account", true) + , mask(this, "mask") + { + } +}; + +NickServ::Account *NickAccessImpl::GetAccount() +{ + return Get(&NickAccessType::account); +} + +void NickAccessImpl::SetAccount(NickServ::Account *acc) +{ + Set(&NickAccessType::account, acc); +} + +Anope::string NickAccessImpl::GetMask() +{ + return Get(&NickAccessType::mask); +} + +void NickAccessImpl::SetMask(const Anope::string &m) +{ + Set(&NickAccessType::mask, m); +} + +class CommandNSAccess : public Command +{ + private: + void DoAdd(CommandSource &source, NickServ::Account *nc, const Anope::string &mask) + { + if (mask.empty()) + { + this->OnSyntaxError(source, "ADD"); + return; + } + + if (Anope::ReadOnly) + { + source.Reply(_("Services are in read-only mode.")); + return; + } + + std::vector<NickAccess *> access = nc->GetRefs<NickAccess *>(nsaccess); + + if (access.size() >= Config->GetModule(this->owner)->Get<unsigned>("accessmax", "32")) + { + source.Reply(_("Sorry, the maximum of \002{0}\002 access entries has been reached."), Config->GetModule(this->owner)->Get<unsigned>("accessmax")); + return; + } + + for (NickAccess *a : access) + if (a->GetMask().equals_ci(mask)) + { + source.Reply(_("Mask \002{0}\002 already present on the access list of \002{1}\002."), mask, nc->GetDisplay()); + return; + } + + NickAccess *a = nsaccess.Create(); + a->SetAccount(nc); + a->SetMask(mask); + + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD mask " << mask << " to " << nc->GetDisplay(); + source.Reply(_("\002{0}\002 added to the access list of \002{1}\002."), mask, nc->GetDisplay()); + } + + void DoDel(CommandSource &source, NickServ::Account *nc, const Anope::string &mask) + { + if (mask.empty()) + { + this->OnSyntaxError(source, "DEL"); + return; + } + + if (Anope::ReadOnly) + { + source.Reply(_("Services are in read-only mode.")); + return; + } + + for (NickAccess *a : nc->GetRefs<NickAccess *>(nsaccess)) + if (a->GetMask().equals_ci(mask)) + { + a->Delete(); + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE mask " << mask << " from " << nc->GetDisplay(); + source.Reply(_("\002{0}\002 deleted from the access list of \002{1}\002."), mask, nc->GetDisplay()); + return; + } + + + source.Reply(_("\002{0}\002 not found on the access list of \002{1}\002."), mask, nc->GetDisplay()); + } + + void DoList(CommandSource &source, NickServ::Account *nc, const Anope::string &mask) + { + std::vector<NickAccess *> access = nc->GetRefs<NickAccess *>(nsaccess); + if (access.empty()) + { + source.Reply(_("The access list of \002{0}\002 is empty."), nc->GetDisplay()); + return; + } + + source.Reply(_("Access list for \002{0}\002:"), nc->GetDisplay()); + for (NickAccess *a : access) + { + if (!mask.empty() && !Anope::Match(a->GetMask(), mask)) + continue; + + source.Reply(" {0}", a->GetMask()); + } + } + public: + CommandNSAccess(Module *creator) : Command(creator, "nickserv/access", 1, 3) + { + this->SetDesc(_("Modify the list of authorized addresses")); + this->SetSyntax(_("ADD [\037nickname\037] \037mask\037")); + this->SetSyntax(_("DEL [\037nickname\037] \037mask\037")); + this->SetSyntax(_("LIST [\037nickname\037]")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + const Anope::string &cmd = params[0]; + Anope::string nick, mask; + + if (cmd.equals_ci("LIST")) + nick = params.size() > 1 ? params[1] : ""; + else + { + nick = params.size() == 3 ? params[1] : ""; + mask = params.size() > 1 ? params[params.size() - 1] : ""; + } + + NickServ::Account *nc; + if (!nick.empty() && source.HasPriv("nickserv/access")) + { + NickServ::Nick *na = NickServ::FindNick(nick); + if (na == NULL) + { + source.Reply(_("\002{0}\002 isn't registered."), nick); + return; + } + + if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.GetAccount() != na->GetAccount() && na->GetAccount()->IsServicesOper() && !cmd.equals_ci("LIST")) + { + source.Reply(_("You may view but not modify the access list of other Services Operators.")); + return; + } + + nc = na->GetAccount(); + } + else + nc = source.nc; + + if (!mask.empty() && (mask.find('@') == Anope::string::npos || mask.find('!') != Anope::string::npos)) + { + source.Reply(_("Mask must be in the form \037user\037@\037host\037.")); + source.Reply(_("\002%s%s HELP %s\002 for more information."), Config->StrictPrivmsg, source.service->nick, source.command); // XXX + } + else if (cmd.equals_ci("LIST")) + return this->DoList(source, nc, mask); + else if (nc->HasFieldS("NS_SUSPENDED")) + source.Reply(_("\002{0}\002 is suspended."), nc->GetDisplay()); + else if (cmd.equals_ci("ADD")) + return this->DoAdd(source, nc, mask); + else if (cmd.equals_ci("DEL")) + return this->DoDel(source, nc, mask); + else + this->OnSyntaxError(source, ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) override + { + source.Reply(_("Modifies or displays the access list for your account." + " The access list is a list of addresses that {1} uses to recognize you." + " If you match one of the hosts on the access list, services will not force you to change your nickname if the \002KILL\002 option is set." + " Furthermore, if the \002SECURE\002 option is disabled, services will recognize you just based on your hostmask, without having to supply a password." + " To gain access to channels when only recognized by your hostmask, the channel must too have the \002SECURE\002 option off." + " Services Operators may provide \037nickname\037 to modify other user's access lists.\n" + "\n" + "Examples:\n" + " \n" + " {command} ADD anyone@*.bepeg.com\n" + " Allows access to user \"anyone\" from any machine in the \"bepeg.com\" domain.\n" + "\n" + " {command} DEL anyone@*.bepeg.com\n" + " Reverses the previous command.\n" + "\n" + " {command} LIST\n" + " Displays the current access list."), + source.command, source.service->nick); + return true; + } +}; + +class NSAccess : public Module + , public EventHook<NickServ::Event::NickRegister> +{ + CommandNSAccess commandnsaccess; + NickAccessType nick_type; + + public: + NSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR) + , commandnsaccess(this) + , nick_type(this) + { + } + + void OnNickRegister(User *u, NickServ::Nick *na, const Anope::string &) override + { + if (u && Config->GetModule(this)->Get<bool>("addaccessonreg")) + { + NickAccess *a = nsaccess.Create(); + a->SetAccount(na->GetAccount()); + a->SetMask(u->Mask()); + //XXX? +// source.Reply(_("\002{0}\002 has been registered under your hostmask: \002{1}\002"), u_nick, na->GetAccount()->GetAccess(0)); + } + } +}; + +MODULE_INIT(NSAccess) diff --git a/modules/nickserv/ajoin.cpp b/modules/nickserv/ajoin.cpp new file mode 100644 index 000000000..135beb403 --- /dev/null +++ b/modules/nickserv/ajoin.cpp @@ -0,0 +1,381 @@ +/* NickServ core functions + * + * (C) 2003-2014 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" +#include "modules/ns_ajoin.h" + +class AutoJoinImpl : public AutoJoin +{ + public: + AutoJoinImpl(Serialize::TypeBase *type) : AutoJoin(type) { } + AutoJoinImpl(Serialize::TypeBase *type, Serialize::ID id) : AutoJoin(type, id) { } + + NickServ::Account *GetOwner() override; + void SetOwner(NickServ::Account *acc) override; + + Anope::string GetChannel() override; + void SetChannel(const Anope::string &c) override; + + Anope::string GetKey() override; + void SetKey(const Anope::string &k) override; +}; + +class AutoJoinType : public Serialize::Type<AutoJoinImpl> +{ + public: + Serialize::ObjectField<AutoJoinImpl, NickServ::Account *> owner; + Serialize::Field<AutoJoinImpl, Anope::string> channel, key; + + AutoJoinType(Module *me) : Serialize::Type<AutoJoinImpl>(me, "AutoJoin") + , owner(this, "owner", true) + , channel(this, "channel") + , key(this, "key") + { + } +}; + +NickServ::Account *AutoJoinImpl::GetOwner() +{ + return Get(&AutoJoinType::owner); +} + +void AutoJoinImpl::SetOwner(NickServ::Account *acc) +{ + Set(&AutoJoinType::owner, acc); +} + +Anope::string AutoJoinImpl::GetChannel() +{ + return Get(&AutoJoinType::channel); +} + +void AutoJoinImpl::SetChannel(const Anope::string &c) +{ + Set(&AutoJoinType::channel, c); +} + +Anope::string AutoJoinImpl::GetKey() +{ + return Get(&AutoJoinType::key); +} + +void AutoJoinImpl::SetKey(const Anope::string &k) +{ + Set(&AutoJoinType::key, k); +} + +class CommandNSAJoin : public Command +{ + void DoList(CommandSource &source, NickServ::Account *nc) + { + std::vector<AutoJoin *> channels = nc->GetRefs<AutoJoin *>(autojoin); + + if (channels.empty()) + { + source.Reply(_("The auto join list of \002{0}\002 is empty."), nc->GetDisplay()); + return; + } + + ListFormatter list(source.GetAccount()); + list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Key")); + for (unsigned i = 0; i < channels.size(); ++i) + { + AutoJoin *aj = channels[i]; + ListFormatter::ListEntry entry; + entry["Number"] = stringify(i + 1); + entry["Channel"] = aj->GetChannel(); + entry["Key"] = aj->GetKey(); + list.AddEntry(entry); + } + + source.Reply(_("Auto join list of \002{0}\002:"), nc->GetDisplay()); + + std::vector<Anope::string> replies; + list.Process(replies); + + for (unsigned i = 0; i < replies.size(); ++i) + source.Reply(replies[i]); + } + + void DoAdd(CommandSource &source, NickServ::Account *nc, const Anope::string &chans, const Anope::string &keys) + { + std::vector<AutoJoin *> channels = nc->GetRefs<AutoJoin *>(autojoin); + + Anope::string addedchans; + Anope::string alreadyadded; + Anope::string invalidkey; + commasepstream ksep(keys, true); + commasepstream csep(chans); + for (Anope::string chan, key; csep.GetToken(chan);) + { + ksep.GetToken(key); + + unsigned i = 0; + for (; i < channels.size(); ++i) + if (channels[i]->GetChannel().equals_ci(chan)) + break; + + if (channels.size() >= Config->GetModule(this->owner)->Get<unsigned>("ajoinmax")) + { + source.Reply(_("Sorry, the maximum of \002{0}\002 auto join entries has been reached."), Config->GetModule(this->owner)->Get<unsigned>("ajoinmax")); + return; + } + + if (i != channels.size()) + alreadyadded += chan + ", "; + else if (IRCD->IsChannelValid(chan) == false) + source.Reply(_("\002{0}\002 isn't a valid channel."), chan); + else + { + Channel *c = Channel::Find(chan); + Anope::string k; + if (c && c->GetParam("KEY", k) && key != k) + { + invalidkey += chan + ", "; + continue; + } + + AutoJoin *entry = autojoin.Create(); + entry->SetOwner(nc); + entry->SetChannel(chan); + entry->SetKey(key); + + addedchans += chan + ", "; + } + } + + if (!alreadyadded.empty()) + { + alreadyadded = alreadyadded.substr(0, alreadyadded.length() - 2); + source.Reply(_("\002{0}\002 is already on the auto join list of \002{1}\002."), alreadyadded, nc->GetDisplay()); + } + + if (!invalidkey.empty()) + { + invalidkey = invalidkey.substr(0, invalidkey.length() - 2); + source.Reply(_("\002{0}\002 had an invalid key specified, and was ignored."), invalidkey); + } + + if (addedchans.empty()) + return; + + addedchans = addedchans.substr(0, addedchans.length() - 2); + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD channel " << addedchans << " to " << nc->GetDisplay(); + source.Reply(_("\002{0}\002 added to the auto join list of \002{1}\002."), addedchans, nc->GetDisplay()); + } + + void DoDel(CommandSource &source, NickServ::Account *nc, const Anope::string &chans) + { + std::vector<AutoJoin *> channels = nc->GetRefs<AutoJoin *>(autojoin); + Anope::string delchans; + Anope::string notfoundchans; + commasepstream sep(chans); + + for (Anope::string chan; sep.GetToken(chan);) + { + unsigned i = 0; + for (; i < channels.size(); ++i) + if (channels[i]->GetChannel().equals_ci(chan)) + break; + + if (i == channels.size()) + notfoundchans += chan + ", "; + else + { + delete channels[i]; + delchans += chan + ", "; + } + } + + if (!notfoundchans.empty()) + { + notfoundchans = notfoundchans.substr(0, notfoundchans.length() - 2); + source.Reply(_("\002{0}\002 was not found on the auto join list of \002{1}\002."), notfoundchans, nc->GetDisplay()); + } + + if (delchans.empty()) + return; + + delchans = delchans.substr(0, delchans.length() - 2); + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE channel " << delchans << " from " << nc->GetDisplay(); + source.Reply(_("\002{0}\002 was removed from the auto join list of \002{1}\002."), delchans, nc->GetDisplay()); + } + + public: + CommandNSAJoin(Module *creator) : Command(creator, "nickserv/ajoin", 1, 4) + { + this->SetDesc(_("Manage your auto join list")); + this->SetSyntax(_("ADD [\037nickname\037] \037channel\037 [\037key\037]")); + this->SetSyntax(_("DEL [\037nickname\037] \037channel\037")); + this->SetSyntax(_("LIST [\037nickname\037]")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + const Anope::string &cmd = params[0]; + Anope::string nick, param, param2; + + if (cmd.equals_ci("LIST")) + nick = params.size() > 1 ? params[1] : ""; + else + nick = (params.size() > 2 && IRCD->IsChannelValid(params[2])) ? params[1] : ""; + + NickServ::Account *nc; + if (!nick.empty() && !source.HasCommand("nickserv/ajoin")) + { + NickServ::Nick *na = NickServ::FindNick(nick); + if (na == NULL) + { + source.Reply(_("\002{0}\002 isn't registered."), nick); + return; + } + + nc = na->GetAccount(); + param = params.size() > 2 ? params[2] : ""; + param2 = params.size() > 3 ? params[3] : ""; + } + else + { + nc = source.nc; + param = params.size() > 1 ? params[1] : ""; + param2 = params.size() > 2 ? params[2] : ""; + } + + if (cmd.equals_ci("LIST")) + return this->DoList(source, nc); + else if (nc->HasFieldS("NS_SUSPENDED")) + source.Reply(_("\002{0}\002 isn't registered."), nc->GetDisplay()); + else if (param.empty()) + this->OnSyntaxError(source, ""); + else if (Anope::ReadOnly) + source.Reply(_("Services are in read-only mode.")); + else if (cmd.equals_ci("ADD")) + return this->DoAdd(source, nc, param, param2); + else if (cmd.equals_ci("DEL")) + return this->DoDel(source, nc, param); + else + this->OnSyntaxError(source, ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) override + { + source.Reply(_("This command manages your auto join list." + " When you identify you will automatically join the channels on your auto join list." + " Services Operators may provide \037nickname\037 to modify other users' auto join lists.")); + return true; + } +}; + +class NSAJoin : public Module + , public EventHook<Event::UserLogin> +{ + CommandNSAJoin commandnsajoin; + AutoJoinType ajtype; + + public: + NSAJoin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR) + , commandnsajoin(this) + , ajtype(this) + { + + if (!IRCD || !IRCD->CanSVSJoin) + throw ModuleException("Your IRCd does not support SVSJOIN"); + + } + + void OnUserLogin(User *u) override + { + ServiceBot *NickServ = Config->GetClient("NickServ"); + if (!NickServ) + return; + + std::vector<AutoJoin *> channels = u->Account()->GetRefs<AutoJoin *>(autojoin); + if (channels.empty()) + return; + + /* Set +r now, so we can ajoin users into +R channels */ + ModeManager::ProcessModes(); + + for (AutoJoin *entry : channels) + { + Channel *c = Channel::Find(entry->GetChannel()); + ChanServ::Channel *ci; + + if (c) + ci = c->ci; + else + ci = ChanServ::Find(entry->GetChannel()); + + bool need_invite = false; + Anope::string key = entry->GetKey(); + ChanServ::AccessGroup u_access; + + if (ci != NULL) + { + if (ci->HasFieldS("CS_SUSPENDED")) + continue; + u_access = ci->AccessFor(u); + } + if (c != NULL) + { + if (c->FindUser(u) != NULL) + continue; + else if (c->HasMode("OPERONLY") && !u->HasMode("OPER")) + continue; + else if (c->HasMode("ADMINONLY") && !u->HasMode("ADMIN")) + continue; + else if (c->HasMode("SSL") && !(u->HasMode("SSL") || u->HasExtOK("ssl"))) + continue; + else if (c->MatchesList(u, "BAN") == true && c->MatchesList(u, "EXCEPT") == false) + need_invite = true; + else if (c->HasMode("INVITE") && c->MatchesList(u, "INVITEOVERRIDE") == false) + need_invite = true; + + if (c->HasMode("KEY")) + { + Anope::string k; + if (c->GetParam("KEY", k)) + { + if (u_access.HasPriv("GETKEY")) + key = k; + else if (key != k) + need_invite = true; + } + } + if (c->HasMode("LIMIT")) + { + Anope::string l; + if (c->GetParam("LIMIT", l)) + { + try + { + unsigned limit = convertTo<unsigned>(l); + if (c->users.size() >= limit) + need_invite = true; + } + catch (const ConvertException &) { } + } + } + } + + if (need_invite && c != NULL) + { + if (!u_access.HasPriv("INVITE")) + continue; + IRCD->SendInvite(NickServ, c, u); + } + + IRCD->SendSVSJoin(NickServ, u, entry->GetChannel(), key); + } + } +}; + +MODULE_INIT(NSAJoin) diff --git a/modules/nickserv/alist.cpp b/modules/nickserv/alist.cpp new file mode 100644 index 000000000..e4aaff38b --- /dev/null +++ b/modules/nickserv/alist.cpp @@ -0,0 +1,133 @@ +/* NickServ core functions + * + * (C) 2003-2014 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 CommandNSAList : public Command +{ + static bool ChannelSort(ChanServ::Channel *ci1, ChanServ::Channel *ci2) + { + return ci::less()(ci1->GetName(), ci2->GetName()); + } + + public: + CommandNSAList(Module *creator) : Command(creator, "nickserv/alist", 0, 2) + { + this->SetDesc(_("List channels you have access on")); + this->SetSyntax(_("[\037nickname\037]")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + Anope::string nick = source.GetNick(); + NickServ::Account *nc = source.nc; + + if (params.size() && source.HasPriv("nickserv/alist")) + { + nick = params[0]; + NickServ::Nick *na = NickServ::FindNick(nick); + if (!na) + { + source.Reply(_("\002{0}\002 isn't registered."), nick); + return; + } + nc = na->GetAccount(); + } + + ListFormatter list(source.GetAccount()); + int chan_count = 0; + + list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Access")).AddColumn(_("Description")); + + std::vector<ChanServ::Channel *> chans = nc->GetRefs<ChanServ::Channel *>(ChanServ::channel); + std::sort(chans.begin(), chans.end(), ChannelSort); + for (ChanServ::Channel *ci : chans) + { + ListFormatter::ListEntry entry; + + if (ci->GetFounder() == nc) + { + ++chan_count; + entry["Number"] = stringify(chan_count); + entry["Channel"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName(); + entry["Access"] = Language::Translate(source.GetAccount(), _("Founder")); + entry["Description"] = ci->GetDesc(); + list.AddEntry(entry); + continue; + } + + if (ci->GetSuccessor() == nc) + { + ++chan_count; + entry["Number"] = stringify(chan_count); + entry["Channel"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName(); + entry["Access"] = Language::Translate(source.GetAccount(), _("Successor")); + entry["Description"] = ci->GetDesc(); + list.AddEntry(entry); + continue; + } + + ChanServ::AccessGroup access = ci->AccessFor(nc, false); + if (access.empty()) + continue; + + ++chan_count; + + entry["Number"] = stringify(chan_count); + entry["Channel"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName(); + for (unsigned j = 0; j < access.size(); ++j) + entry["Access"] = entry["Access"] + ", " + access[j]->AccessSerialize(); + entry["Access"] = entry["Access"].substr(2); + entry["Description"] = ci->GetDesc(); + list.AddEntry(entry); + } + + std::vector<Anope::string> replies; + list.Process(replies); + + if (!chan_count) + { + source.Reply(_("\002{0}\002 has no access in any channels."), nc->GetDisplay()); + } + else + { + source.Reply(_("Channels that \002{0}\002 has access on:"), nc->GetDisplay()); + + for (unsigned i = 0; i < replies.size(); ++i) + source.Reply(replies[i]); + + source.Reply(_("End of list - \002{0}\002 channels shown."), chan_count); + } + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) override + { + source.Reply(_("Lists all channels you have access on.\n" + " \n" + "Channels that have the \037NOEXPIRE\037 option set will be prefixed by an exclamation mark. The nickname parameter is limited to Services Operators")); + + return true; + } +}; + +class NSAList : public Module +{ + CommandNSAList commandnsalist; + + public: + NSAList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR) + , commandnsalist(this) + { + + } +}; + +MODULE_INIT(NSAList) diff --git a/modules/nickserv/cert.cpp b/modules/nickserv/cert.cpp new file mode 100644 index 000000000..fc87f8cb0 --- /dev/null +++ b/modules/nickserv/cert.cpp @@ -0,0 +1,364 @@ +/* NickServ core functions + * + * (C) 2003-2014 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" +#include "modules/ns_cert.h" +#inclu |
