summaryrefslogtreecommitdiff
path: root/modules/rpc
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2025-02-24 05:46:43 +0000
committerSadie Powell <sadie@witchery.services>2025-02-25 19:59:23 +0000
commite500258ce4b01bc02539db99c12fa7d9428cb0a6 (patch)
tree5a9fd3900ad3104741b61a907c3f8f2a99323f35 /modules/rpc
parent801a748e256cb7c4952969103f2f2eaf5ee36552 (diff)
Add the rpc_data module.
- Add rewritten and namespaced versions of the channel, oper, user events. - Add the following new events: * anope.listChannels * anope.listOpers * anope.listServers * anope.listUsers * anope.server
Diffstat (limited to 'modules/rpc')
-rw-r--r--modules/rpc/rpc_data.cpp446
-rw-r--r--modules/rpc/rpc_main.cpp139
2 files changed, 446 insertions, 139 deletions
diff --git a/modules/rpc/rpc_data.cpp b/modules/rpc/rpc_data.cpp
new file mode 100644
index 000000000..670b4c57e
--- /dev/null
+++ b/modules/rpc/rpc_data.cpp
@@ -0,0 +1,446 @@
+/*
+ *
+ * (C) 2010-2025 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Please read COPYING and README for further details.
+ */
+
+#include "module.h"
+#include "modules/rpc.h"
+
+enum
+{
+ // Used by anope.channel, anope.oper, anope.server, and anope.user
+ ERR_NO_SUCH_TARGET = RPC::ERR_CUSTOM_START,
+};
+
+class AnopeListChannelsRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeListChannelsRPCEvent()
+ : RPC::Event("anope.listChannels")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ auto &root = request.Root<RPC::Array>();
+ for (auto &[_, c] : ChannelList)
+ root.Reply(c->name);
+ return true;
+ }
+};
+
+class AnopeChannelRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeChannelRPCEvent()
+ : RPC::Event("anope.channel")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ if (request.data.empty())
+ {
+ request.Error(RPC::ERR_INVALID_PARAMS, "Not enough parameters");
+ return true;
+ }
+
+ auto *c = Channel::Find(request.data[0]);
+ if (!c)
+ {
+ request.Error(ERR_NO_SUCH_TARGET, "No such channel");
+ return true;
+ }
+
+ auto &root = request.Root();
+ root.Reply("created", c->creation_time)
+ .Reply("name", c->name)
+ .Reply("registered", !!c->ci);
+
+ std::map<char, RPC::Array&> modemap;
+ auto &listmodes = root.ReplyMap("listmodes");
+ for (auto *cm : ModeManager::GetChannelModes())
+ {
+ if (cm->type != MODE_LIST)
+ continue;
+
+ for (auto &entry : c->GetModeList(cm->name))
+ {
+ auto *wcm = cm->Wrap(entry);
+
+ auto it = modemap.find(wcm->mchar);
+ if (it == modemap.end())
+ it = modemap.emplace(wcm->mchar, listmodes.ReplyArray(wcm->mchar)).first;
+
+ it->second.Reply(entry);
+ }
+ }
+
+ std::vector<Anope::string> modelist = { "+" };
+ for (const auto &[mname, mvalue] : c->GetModes())
+ {
+ auto *cm = ModeManager::FindChannelModeByName(mname);
+ if (!cm || cm->type == MODE_LIST)
+ continue;
+
+ modelist.front().push_back(cm->mchar);
+ if (!mvalue.empty())
+ modelist.push_back(mvalue);
+ }
+ auto &modes = root.ReplyArray("modes");
+ for (const auto &modeparam : modelist)
+ modes.Reply(modeparam);
+
+ if (c->topic.empty())
+ root.Reply("topic", nullptr);
+ else
+ {
+ auto &topic = root.ReplyMap("topic");
+ topic.Reply("setat", c->topic_ts)
+ .Reply("setby", c->topic_setter)
+ .Reply("value", c->topic);
+ }
+
+ auto &users = root.ReplyArray("users");
+ for (const auto &[_, uc] : c->users)
+ users.Reply(uc->status.BuildModePrefixList() + uc->user->nick);
+
+ return true;
+ }
+};
+
+class AnopeListOpersRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeListOpersRPCEvent()
+ : RPC::Event("anope.listOpers")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ auto &root = request.Root<RPC::Array>();
+ for (auto *oper : Oper::opers)
+ root.Reply(oper->name);
+ return true;
+ }
+};
+
+class AnopeOperRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeOperRPCEvent()
+ : RPC::Event("anope.oper")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ if (request.data.empty())
+ {
+ request.Error(RPC::ERR_INVALID_PARAMS, "Not enough parameters");
+ return true;
+ }
+
+ auto *o = Oper::Find(request.data[0]);
+ if (!o)
+ {
+ request.Error(ERR_NO_SUCH_TARGET, "No such oper");
+ return true;
+ }
+
+ auto &root = request.Root();
+ root
+ .Reply("name", o->name)
+ .Reply("operonly", o->require_oper)
+ .Reply("password", !o->password.empty());
+
+ if (o->certfp.empty())
+ root.Reply("fingerprints", nullptr);
+ else
+ {
+ auto &fingerprints = root.ReplyArray("fingerprints");
+ for (const auto &fingerprint : o->certfp)
+ fingerprints.Reply(fingerprint);
+ }
+
+ if (o->hosts.empty())
+ root.Reply("hosts", nullptr);
+ else
+ {
+ auto &hosts = root.ReplyArray("hosts");
+ for (const auto &host : o->hosts)
+ hosts.Reply(host);
+ }
+
+ auto &opertype = root.ReplyMap("opertype");
+ opertype.Reply("name", o->ot->GetName());
+ {
+ auto &commands = opertype.ReplyArray("commands");
+ for (const auto &command : o->ot->GetCommands())
+ commands.Reply(command);
+
+ auto &privileges = opertype.ReplyArray("privileges");
+ for (const auto &privilege : o->ot->GetPrivs())
+ privileges.Reply(privilege);
+ }
+
+ if (o->vhost.empty())
+ root.Reply("vhost", nullptr);
+ else
+ root.Reply("vhost", o->vhost);
+
+ return true;
+ }
+};
+
+class AnopeListServersRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeListServersRPCEvent()
+ : RPC::Event("anope.listServers")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ auto &root = request.Root<RPC::Array>();
+ for (auto &[_, s] : Servers::ByName)
+ root.Reply(s->GetName());
+ return true;
+ }
+};
+
+class AnopeServerRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeServerRPCEvent()
+ : RPC::Event("anope.server")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ if (request.data.empty())
+ {
+ request.Error(RPC::ERR_INVALID_PARAMS, "Not enough parameters");
+ return true;
+ }
+
+ auto *s = Server::Find(request.data[0]);
+ if (!s)
+ {
+ request.Error(ERR_NO_SUCH_TARGET, "No such server");
+ return true;
+ }
+
+ auto &root = request.Root();
+ root.Reply("description", s->GetDescription())
+ .Reply("juped", s->IsJuped())
+ .Reply("name", s->GetName())
+ .Reply("synced", s->IsSynced())
+ .Reply("ulined", s->IsULined());
+
+ auto &downlinks = root.ReplyArray("downlinks");
+ for (const auto *s : s->GetLinks())
+ downlinks.Reply(s->GetName());
+
+ if (IRCD->RequiresID)
+ root.Reply("sid", s->GetSID());
+ else
+ root.Reply("sid", nullptr);
+
+ if (s->GetUplink())
+ root.Reply("uplink", s->GetUplink()->GetName());
+ else
+ root.Reply("uplink", nullptr);
+
+ return true;
+ }
+};
+
+class AnopeListUsersRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeListUsersRPCEvent()
+ : RPC::Event("anope.listUsers")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ auto &root = request.Root<RPC::Array>();
+ for (auto &[_, u] : UserListByNick)
+ root.Reply(u->nick);
+ return true;
+ }
+};
+
+class AnopeUserRPCEvent final
+ : public RPC::Event
+{
+public:
+ AnopeUserRPCEvent()
+ : RPC::Event("anope.user")
+ {
+ }
+
+ bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
+ {
+ if (request.data.empty())
+ {
+ request.Error(RPC::ERR_INVALID_PARAMS, "Not enough parameters");
+ return true;
+ }
+
+ auto *u = User::Find(request.data[0]);
+ if (!u)
+ {
+ request.Error(ERR_NO_SUCH_TARGET, "No such user");
+ return true;
+ }
+
+ auto &root = request.Root();
+ root.Reply("address", u->ip.addr())
+ .Reply("host", u->host)
+ .Reply("ident", u->GetIdent())
+ .Reply("nick", u->nick)
+ .Reply("nickchanged", u->timestamp)
+ .Reply("real", u->realname)
+ .Reply("server", u->server->GetName())
+ .Reply("signon", u->signon);
+
+ if (u->IsIdentified())
+ {
+ auto &account = root.ReplyMap("account");
+ account.Reply("display", u->Account()->display)
+ .Reply("uniqueid", u->Account()->GetId());
+
+ if (u->Account()->o)
+ account.Reply("opertype", u->Account()->o->ot->GetName());
+ else
+ account.Reply("opertype", nullptr);
+ }
+ else
+ {
+ root.Reply("account", nullptr);
+ }
+
+ auto &channels = root.ReplyArray("channels");
+ for (const auto &[_, cc] : u->chans)
+ channels.Reply(cc->status.BuildModePrefixList() + cc->chan->name);
+
+ if (u->chost.empty())
+ root.Reply("chost", nullptr);
+ else
+ root.Reply("chost", u->chost);
+
+ if (u->fingerprint.empty())
+ root.Reply("fingerprint", nullptr);
+ else
+ root.Reply("fingerprint", u->fingerprint);
+
+ std::vector<Anope::string> modelist = { "+" };
+ for (const auto &[mname, mvalue] : u->GetModeList())
+ {
+ auto *um = ModeManager::FindUserModeByName(mname);
+ if (!um || um->type == MODE_LIST)
+ continue;
+
+ modelist.front().push_back(um->mchar);
+ if (!mvalue.empty())
+ modelist.push_back(mvalue);
+ }
+ auto &modes = root.ReplyArray("modes");
+ for (const auto &modeparam : modelist)
+ modes.Reply(modeparam);
+
+ if (IRCD->RequiresID)
+ root.Reply("uid", u->GetUID());
+ else
+ root.Reply("uid", nullptr);
+
+ if (u->vhost.empty() || u->vhost.equals_cs(u->host))
+ root.Reply("vhost", nullptr);
+ else
+ root.Reply("vhost", u->vhost);
+
+ if (u->GetVIdent().equals_cs(u->GetIdent()))
+ root.Reply("vident", nullptr);
+ else
+ root.Reply("vident", u->GetIdent());
+
+ return true;
+ }
+};
+
+class ModuleRPCData final
+ : public Module
+{
+private:
+ ServiceReference<RPC::ServiceInterface> rpc;
+
+ AnopeListChannelsRPCEvent anopelistchannelsrpcevent;
+ AnopeChannelRPCEvent anopechannelrpcevent;
+
+ AnopeListOpersRPCEvent anopelistopersrpcevent;
+ AnopeOperRPCEvent anopeoperrpcevent;
+
+ AnopeListServersRPCEvent anopelistserversrpcevent;
+ AnopeServerRPCEvent anopeserverrpcevent;
+
+ AnopeListUsersRPCEvent anopelistusersrpcevent;
+ AnopeUserRPCEvent anopeuserrpcevent;
+
+public:
+ ModuleRPCData(const Anope::string &modname, const Anope::string &creator)
+ : Module(modname, creator, EXTRA | VENDOR)
+ , rpc("RPCServiceInterface", "rpc")
+ {
+ if (!rpc)
+ throw ModuleException("Unable to find RPC interface, is jsonrpc/xmlrpc loaded?");
+
+ rpc->Register(&anopelistchannelsrpcevent);
+ rpc->Register(&anopechannelrpcevent);
+
+ rpc->Register(&anopelistopersrpcevent);
+ rpc->Register(&anopeoperrpcevent);
+
+ rpc->Register(&anopelistserversrpcevent);
+ rpc->Register(&anopeserverrpcevent);
+
+ rpc->Register(&anopelistusersrpcevent);
+ rpc->Register(&anopeuserrpcevent);
+
+ }
+
+ ~ModuleRPCData() override
+ {
+ if (!rpc)
+ return;
+
+ rpc->Unregister(&anopelistchannelsrpcevent);
+ rpc->Unregister(&anopechannelrpcevent);
+
+ rpc->Unregister(&anopelistopersrpcevent);
+ rpc->Unregister(&anopeoperrpcevent);
+
+ rpc->Unregister(&anopelistserversrpcevent);
+ rpc->Unregister(&anopeserverrpcevent);
+
+ rpc->Unregister(&anopelistusersrpcevent);
+ rpc->Unregister(&anopeuserrpcevent);
+ }
+};
+
+MODULE_INIT(ModuleRPCData)
diff --git a/modules/rpc/rpc_main.cpp b/modules/rpc/rpc_main.cpp
index 2f22e82c7..0f35de142 100644
--- a/modules/rpc/rpc_main.cpp
+++ b/modules/rpc/rpc_main.cpp
@@ -167,136 +167,6 @@ public:
}
};
-class ChannelRPCEvent final
- : public RPC::Event
-{
-public:
- ChannelRPCEvent()
- : RPC::Event("channel")
- {
- }
-
- bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
- {
- if (request.data.empty())
- {
- request.Error(RPC::ERR_INVALID_PARAMS, "Invalid parameters");
- return true;
- }
-
- Channel *c = Channel::Find(request.data[0]);
-
- auto &root = request.Root();
- root.Reply("name", c ? c->name : request.data[0]);
-
- if (c)
- {
- root.Reply("bancount", c->HasMode("BAN"));
- auto &bans = root.ReplyArray("bans");
- for (auto &ban : c->GetModeList("BAN"))
- bans.Reply(ban);
-
- root.Reply("exceptcount", c->HasMode("EXCEPT"));
- auto &excepts = root.ReplyArray("excepts");
- for (auto &except : c->GetModeList("EXCEPT"))
- excepts.Reply(except);
-
- root.Reply("invitecount", c->HasMode("INVITEOVERRIDE"));
- auto &invites = root.ReplyArray("invites");
- for (auto &invite : c->GetModeList("INVITEOVERRIDE"))
- invites.Reply(invite);
-
- auto &users = root.ReplyArray("users");
- for (const auto &[_, uc] : c->users)
- users.Reply(uc->status.BuildModePrefixList() + uc->user->nick);
-
- if (!c->topic.empty())
- root.Reply("topic", c->topic);
-
- if (!c->topic_setter.empty())
- root.Reply("topicsetter", c->topic_setter);
-
- root.Reply("topictime", c->topic_time);
- root.Reply("topicts", c->topic_ts);
- }
- return true;
- }
-};
-
-class UserRPCEvent final
- : public RPC::Event
-{
-public:
- UserRPCEvent()
- : RPC::Event("user")
- {
- }
-
- bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
- {
- if (request.data.empty())
- {
- request.Error(RPC::ERR_INVALID_PARAMS, "Invalid parameters");
- return true;
- }
-
- User *u = User::Find(request.data[0]);
-
- auto &root = request.Root();
- root.Reply("nick", u ? u->nick : request.data[0]);
-
- if (u)
- {
- root.Reply("ident", u->GetIdent());
- root.Reply("vident", u->GetVIdent());
- root.Reply("host", u->host);
- if (!u->vhost.empty())
- root.Reply("vhost", u->vhost);
- if (!u->chost.empty())
- root.Reply("chost", u->chost);
- root.Reply("ip", u->ip.addr());
- root.Reply("timestamp", u->timestamp);
- root.Reply("signon", u->signon);
- if (u->IsIdentified())
- {
- root.Reply("account", u->Account()->display);
- if (u->Account()->o)
- root.Reply("opertype", u->Account()->o->ot->GetName());
- }
-
- auto &channels = root.ReplyArray("channels");
- for (const auto &[_, cc] : u->chans)
- channels.Reply(cc->status.BuildModePrefixList() + cc->chan->name);
- }
- return true;
- }
-};
-
-class OpersRPCEvent final
- : public RPC::Event
-{
-public:
- OpersRPCEvent()
- : RPC::Event("opers")
- {
- }
-
- bool Run(RPC::ServiceInterface *iface, HTTPClient *client, RPC::Request &request) override
- {
- auto &root = request.Root();
- for (auto *ot : Config->MyOperTypes)
- {
- Anope::string perms;
- for (const auto &priv : ot->GetPrivs())
- perms += " " + priv;
- for (const auto &command : ot->GetCommands())
- perms += " " + command;
- root.Reply(ot->GetName(), perms);
- }
- return true;
- }
-};
-
class NoticeRPCEvent final
: public RPC::Event
{
@@ -334,9 +204,6 @@ private:
CommandRPCEvent commandrpcevent;
CheckAuthenticationRPCEvent checkauthenticationrpcevent;
StatsRPCEvent statsrpcevent;
- ChannelRPCEvent channelrpcevent;
- UserRPCEvent userrpcevent;
- OpersRPCEvent opersrpcevent;
NoticeRPCEvent noticerpcevent;
public:
@@ -352,9 +219,6 @@ public:
rpc->Register(&commandrpcevent);
rpc->Register(&checkauthenticationrpcevent);
rpc->Register(&statsrpcevent);
- rpc->Register(&channelrpcevent);
- rpc->Register(&userrpcevent);
- rpc->Register(&opersrpcevent);
rpc->Register(&noticerpcevent);
}
@@ -366,9 +230,6 @@ public:
rpc->Unregister(&commandrpcevent);
rpc->Unregister(&checkauthenticationrpcevent);
rpc->Unregister(&statsrpcevent);
- rpc->Unregister(&channelrpcevent);
- rpc->Unregister(&userrpcevent);
- rpc->Unregister(&opersrpcevent);
rpc->Unregister(&noticerpcevent);
}
};