From 9a16d96323ed085354c1e8d86ce3b199bbf5bc47 Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 27 Oct 2024 19:08:37 +0100 Subject: [PATCH] [ADD] Filtering XMPP commands This allows us to have commands apply to admins or MUCs only(which would help with many things I want to implement). --- README.MD | 8 +++++- build.conf | 4 +-- src/Routes/Root.c | 8 +++++- src/XMPPCommand/Manager.c | 37 +++++++++++++++++++------ src/XMPPThread/ReListener.c | 46 +++++++++++++++++++++++++++++++- src/XMPPThread/Stanzas/IQ.c | 2 +- src/XMPPThread/Stanzas/Message.c | 4 +-- src/include/XMPPCommand.h | 24 +++++++++++++---- src/include/XMPPCommands.x.h | 25 ++++++++++------- 9 files changed, 127 insertions(+), 31 deletions(-) diff --git a/README.MD b/README.MD index 7522deb..e02db11 100644 --- a/README.MD +++ b/README.MD @@ -55,7 +55,8 @@ parsee-config \ -H 'blow.hole' \ # Matrix homeserver name -J 'parsee.blow.hole' \ # XMPP component host, must be reachable -s 'A very secure XMPP component secret' \ - -p 5347 + -p 5347 \ + -M 65535 # Maximum stanza size. Stanzas larger than this from Parsee will be dropped to avoid stream closures. Leave this empty if you're unsure. ``` If everything goes well, it should generate a `parsee.json` file. @@ -72,7 +73,12 @@ Currently, the main sources of documentation are the Ayadocs(for headers) and th ## TODOS before 1.0 rolls around - Make Parsee actually go *vroooooooooommmmmmm*. +- Make sure Parsee can easily run on just about any reasonable POSIX +system. - Avoid making 'back-puppets' from Matrix as much as possible +- Extension support. I'd need to design a good system, and maybe do it +with either shared libraries(`dlopen`/`dlclose` on POSIX) or use a +language like Janet or Lua. - Add [libomemo](https://github.com/gkdr/libomemo) or something as an optional dependency. - It depends on more stuff anyways, and I don't want to weigh down the dependency list of Parsee for that. diff --git a/build.conf b/build.conf index 3e06176..9ebf253 100644 --- a/build.conf +++ b/build.conf @@ -1,6 +1,6 @@ -CODE=tomboyish-bridges-adventure +CODE=star-of-hope NAME=Parsee -VERSION=0.1.0 +VERSION=0.2.0 BINARY=parsee SOURCE=src INCLUDES=src/include diff --git a/src/Routes/Root.c b/src/Routes/Root.c index a68bfbc..0ddb731 100644 --- a/src/Routes/Root.c +++ b/src/Routes/Root.c @@ -54,7 +54,13 @@ GetRandomQuote(void) NAME ": the federated world's little little kobashi", "Go take a look at your stanzas!", - "Go take a look at your objects!" + "Go take a look at your objects!", + + "DEC Alpha AXP-Certified!", + + "this is the moment parsee started parsing or smth idk" + " - another wise person", + "Ah, merde, mon TGV est en retard de 53 minutes !" }; const size_t count = sizeof(quotes)/sizeof(*quotes); diff --git a/src/XMPPCommand/Manager.c b/src/XMPPCommand/Manager.c index bd38579..69f409a 100644 --- a/src/XMPPCommand/Manager.c +++ b/src/XMPPCommand/Manager.c @@ -34,7 +34,14 @@ struct XMPPCommandManager { HashMap *sessions; void *cookie; + + XMPPCmdFilter filter; }; +static bool +XMPPDefaultFilter(XMPPCommandManager *manager, char *id, XMLElement *stanza) +{ + return true; +} static void XMPPDestroySession(XMPPSession *session) { @@ -118,6 +125,7 @@ XMPPCreateManager(void *cookie) ret->commands = HashMapCreate(); ret->sessions = HashMapCreate(); ret->cookie = cookie; + ret->filter = XMPPDefaultFilter; return ret; } @@ -161,12 +169,12 @@ XMPPFreeManager(XMPPCommandManager *manager) Free(manager); } void -XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p) +XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s) { char *node_name; XMPPCommand *val; XMLElement *item; - if (!m || !p || !jid) + if (!m || !p || !jid || !s) { return; } @@ -174,11 +182,14 @@ XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p) pthread_mutex_lock(&m->lock); while (HashMapIterate(m->commands, &node_name, (void **) &val)) { - item = XMLCreateTag("item"); - XMLAddAttr(item, "jid", jid); - XMLAddAttr(item, "node", node_name); - XMLAddAttr(item, "name", XMPPGetCommandDesc(val)); - XMLAddChild(p, item); + if (m->filter(m, node_name, s)) + { + item = XMLCreateTag("item"); + XMLAddAttr(item, "jid", jid); + XMLAddAttr(item, "node", node_name); + XMLAddAttr(item, "name", XMPPGetCommandDesc(val)); + XMLAddChild(p, item); + } } pthread_mutex_unlock(&m->lock); } @@ -206,6 +217,16 @@ XMPPVerifySession(XMPPCommandManager *mgr, char *s_id, char *from, char *to) return ret; } + +void +XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter) +{ + if (!manager || !filter) + { + return; + } + manager->filter = filter; +} bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) { @@ -242,7 +263,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) /* This is an execution. */ cmd = HashMapGet(m->commands, node); - if (!cmd) + if (!cmd || !m->filter(m, node, stanza)) { /* TODO: Set an error note */ goto end; diff --git a/src/XMPPThread/ReListener.c b/src/XMPPThread/ReListener.c index e8b619f..559ffcb 100644 --- a/src/XMPPThread/ReListener.c +++ b/src/XMPPThread/ReListener.c @@ -126,6 +126,49 @@ ParseeCongestion(void) return congestion; } +bool +XMPPCommandFilter(XMPPCommandManager *m, char *id, XMLElement *stanza) +{ + ParseeData *args = XMPPGetManagerCookie(m); + char *trimmed_from; + char *from; + char *chat_id; + bool is_muc; + if (!m || !id || !stanza) + { + return false; + } + + from = HashMapGet(stanza->attrs, "from"); + trimmed_from = ParseeTrimJID(from); + is_muc = !!(chat_id = ParseeGetFromMUCID(args, trimmed_from)); + Free(trimmed_from); + Free(chat_id); +#define XMPP_COMMAND(f,l,n,t,s) \ + if (StrEquals(n, id)) \ + { \ + if (l == XMPPCMD_ALL) \ + { \ + return true; \ + } \ + else if (l == XMPPCMD_MUC) \ + { \ + return is_muc; \ + } \ + else if (l == XMPPCMD_ADMINS) \ + { \ + bool is_admin; \ + trimmed_from = ParseeTrimJID(from); \ + is_admin = ParseeIsAdmin(args, trimmed_from); \ + Free(trimmed_from); \ + return is_admin; \ + } \ + } + XMPPCOMMANDS +#undef XMPP_COMMAND + + return false; +} void * ParseeXMPPThread(void *argp) @@ -143,9 +186,10 @@ ParseeXMPPThread(void *argp) /* Initialise the managers, and add all handlers. */ info.m = XMPPCreateManager(args); + XMPPManagerSetFilter(info.m, XMPPCommandFilter); { XMPPCommand *cmd; -#define XMPP_COMMAND(f,n,t,s) \ +#define XMPP_COMMAND(f,l,n,t,s) \ cmd = XMPPBasicCmd( \ n, t, f \ ); \ diff --git a/src/XMPPThread/Stanzas/IQ.c b/src/XMPPThread/Stanzas/IQ.c index d185e4c..e944168 100644 --- a/src/XMPPThread/Stanzas/IQ.c +++ b/src/XMPPThread/Stanzas/IQ.c @@ -409,7 +409,7 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr) XMLElement *q = XMLCreateTag("query"); XMLAddAttr(q, "xmlns", "http://jabber.org/protocol/disco#items"); XMLAddAttr(q, "node", "http://jabber.org/protocol/commands"); - XMPPShoveCommandList(thr->info->m, to, q); + XMPPShoveCommandList(thr->info->m, to, q, stanza); XMLAddChild(iq_reply, q); } XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size); diff --git a/src/XMPPThread/Stanzas/Message.c b/src/XMPPThread/Stanzas/Message.c index 69342ca..d72d4d9 100644 --- a/src/XMPPThread/Stanzas/Message.c +++ b/src/XMPPThread/Stanzas/Message.c @@ -472,13 +472,13 @@ end_error: reaction = ArrayGet(react_child, i); react_data = ArrayGet(reaction->children, 0); - event_id = LazySend( + Free(LazySend( args, encoded, mroom_id, "m.reaction", ShoveStanza( MatrixCreateReact(event_id, react_data->data), stanza ) - ); + )); } Free(event_id); event_id = NULL; diff --git a/src/include/XMPPCommand.h b/src/include/XMPPCommand.h index 2680a64..33b121f 100644 --- a/src/include/XMPPCommand.h +++ b/src/include/XMPPCommand.h @@ -12,6 +12,7 @@ typedef struct XMPPCommand XMPPCommand; typedef struct XMPPOption XMPPOption; typedef void (*XMPPCmdCallback)(XMPPCommandManager *, char *, XMLElement *, XMLElement *); typedef void (*XMPPOptionWriter)(XMPPCommandManager *, XMPPCommand *, char *); +typedef bool (*XMPPCmdFilter)(XMPPCommandManager *, char *id, XMLElement *stanza); /** Creates a simple XMPP command manager, which routes commands * with a single-stage form system, with a {cookie} @@ -19,16 +20,28 @@ typedef void (*XMPPOptionWriter)(XMPPCommandManager *, XMPPCommand *, char *); * Returns: An opaque command manager[LA:HEAP] * Modifies: NOTHING * See-Also: XMPPFreeManager */ -extern XMPPCommandManager * XMPPCreateManager(void *cookie); +extern XMPPCommandManager * XMPPCreateManager(void *cookie); extern void * XMPPGetManagerCookie(XMPPCommandManager *manager); +/** Changes the filter used for the command manager. This function + * takes in the source stanza and the command "ID" to filter, and + * returns true IFF the command should be shown to the requester. + * ----------- + * Returns: NOTHING + * Modifies: the manager's filter + * See-Also: XMPPCreateManager */ +extern void +XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter); + /** Create a basic command with a node and name description * ----------------------------------------------------- * Returns: A command to be used with {XMPPRegisterCommand}[LA:HEAP] * Modifies: NOTHING * See-Also: XMPPRegisterCommand */ -extern XMPPCommand * XMPPBasicCmd(char *node, char *name, XMPPCmdCallback cb); -extern void XMPPCmdOptionsCreator(XMPPCommand *cmd, XMPPOptionWriter writer); +extern XMPPCommand * + XMPPBasicCmd(char *node, char *name, XMPPCmdCallback cb); +extern void +XMPPCmdOptionsCreator(XMPPCommand *cmd, XMPPOptionWriter writer); extern void XMPPAddOption(XMPPCommand *cmd, XMPPOption *opt); extern XMLElement * XMPPFormifyCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from); extern char * XMPPGetCommandNode(XMPPCommand *cmd); @@ -71,11 +84,12 @@ extern bool XMPPVerifyForm(XMPPCommand *cmd, XMLElement *form); extern void XMPPRegisterCommand(XMPPCommandManager *m, XMPPCommand *cmd); /** Shoves all {m} commands into XML as children of {p}, and a {jid} + * (and with the stanza source) * ----------------------------------------------------- * Returns: NOTHING * Modifies: {p} * See-Also: XMPPCreateManager */ -extern void XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p); +extern void XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s); /** Destroys all memory related to the command {manager}. * ----------------------------------------------------- @@ -95,7 +109,7 @@ extern bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeD /* --------------------------------- COMMANDS --------------------------------- */ /* Please edit stc/XMPPThread.c (you can just force-save) for these to apply! */ -#define XMPP_COMMAND(f,n,t,s) \ +#define XMPP_COMMAND(f,l,n,t,s) \ extern void \ f(XMPPCommandManager *, char *, XMLElement *, XMLElement *); \ /* This symbol might not exist. We do, however, not care, as diff --git a/src/include/XMPPCommands.x.h b/src/include/XMPPCommands.x.h index eaeb5b9..5000a2f 100644 --- a/src/include/XMPPCommands.x.h +++ b/src/include/XMPPCommands.x.h @@ -1,10 +1,15 @@ /* C X-macro file */ +typedef enum XMPPCommandFlags { + XMPPCMD_ALL = 0, + XMPPCMD_MUC , /* Only for MUCs */ + XMPPCMD_ADMINS /* Only for administrators */ +} XMPPCommandFlags; #define XMPPCOMMANDS \ - XMPP_COMMAND(StatusCallback, "stats", "Get Parsee statistics", {}) \ - XMPP_COMMAND(CleanCallback, "clean", "Cleanup temporary Parsee data", {}) \ - XMPP_COMMAND(AdminsCallback, "admin", "Get Parsee admin list", {}) \ - XMPP_COMMAND(NoflyCallback, "nofly", "Get Parsee nofly list", {}) \ - XMPP_COMMAND(AddAdminCallback, "add-admin", "Adds glob as admin", { \ + XMPP_COMMAND(StatusCallback, XMPPCMD_ALL, "stats", "Get Parsee statistics", {}) \ + XMPP_COMMAND(CleanCallback, XMPPCMD_ADMINS, "clean", "Cleanup temporary Parsee data", {}) \ + XMPP_COMMAND(AdminsCallback, XMPPCMD_ALL, "admin", "Get Parsee admin list", {}) \ + XMPP_COMMAND(NoflyCallback, XMPPCMD_ADMINS, "nofly", "Get Parsee nofly list", {}) \ + XMPP_COMMAND(AddAdminCallback, XMPPCMD_ADMINS, "add-admin", "Adds glob as admin", { \ XMPPOption *glob = XMPPCreateText(true, "glob", ""); \ XMPPSetDescription(glob, "Glob pattern to set as admin"); \ XMPPAddOption(cmd, glob); \ @@ -12,12 +17,12 @@ XMPPSetFormTitle(cmd, "Admin addition form"); \ XMPPSetFormInstruction(cmd, "Select a glob pattern to add as an admin"); \ }) \ - XMPP_COMMAND(DelAdminCallback, "del-admin", "Removes glob from being admin", { \ + XMPP_COMMAND(DelAdminCallback, XMPPCMD_ADMINS, "del-admin", "Removes glob from being admin", { \ XMPPCmdOptionsCreator(cmd, FormDelAdminCallback); \ XMPPSetFormTitle(cmd, "Admin removal form"); \ XMPPSetFormInstruction(cmd, "Select a glob pattern to remove as an admin"); \ }) \ - XMPP_COMMAND(AddNoflyCallback, "add-nofly", "Adds user to nofly", { \ + XMPP_COMMAND(AddNoflyCallback, XMPPCMD_ADMINS, "add-nofly", "Adds user to nofly", { \ XMPPOption *entity = XMPPCreateText(true, "entity", ""); \ XMPPOption *reason = XMPPCreateText(false, "reason", "Not behaving"); \ XMPPSetDescription(entity, "Entity(glob) to no-fly"); \ @@ -28,8 +33,8 @@ XMPPSetFormTitle(cmd, "No-fly addition form"); \ XMPPSetFormInstruction(cmd, "Select a glob pattern to add to the nofly"); \ }) \ - XMPP_COMMAND(ClearWhitelistCallback, "clear-wl", "Removes the chat whitelist", {}) \ - XMPP_COMMAND(AddWhitelistCallback, "add-wl", "Adds server to chat whitelist", { \ + XMPP_COMMAND(ClearWhitelistCallback, XMPPCMD_ADMINS, "clear-wl", "Removes the chat whitelist", {}) \ + XMPP_COMMAND(AddWhitelistCallback, XMPPCMD_ADMINS, "add-wl", "Adds server to chat whitelist", { \ XMPPOption *serv = XMPPCreateText(true, "entity", ""); \ XMPPSetDescription(serv, "Server to mark as admin"); \ XMPPAddOption(cmd, serv); \ @@ -37,6 +42,6 @@ XMPPSetFormTitle(cmd, "Chatlist addition form"); \ XMPPSetFormInstruction(cmd, "Add a server to whitelist"); \ }) \ - XMPP_COMMAND(WhitelistCallback, "wl", "Get Parsee's chat whitelist", {}) \ + XMPP_COMMAND(WhitelistCallback, XMPPCMD_ADMINS, "wl", "Get Parsee's chat whitelist", {}) \ XMPPCOMMANDS