From 91d373addb62e005518c71da32e66e88b3d1ec7c Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 14 Jul 2024 20:00:41 +0200 Subject: [PATCH] [ADD/MOD] Cleanup cmd, change Matrix user format --- README.MD | 6 ++- XEPS-TBD.TXT | 4 ++ src/Main.c | 9 ++++ src/MatrixEventHandler.c | 2 + src/Parsee/Data.c | 37 +++++++++++++++ src/Parsee/User.c | 93 +++++++++++++++++++------------------- src/XMPPCommand/Commands.c | 66 +++++++++++++++++++-------- src/XMPPCommand/Manager.c | 11 +++-- src/XMPPCommand/Options.c | 2 + src/XMPPCommands/Cleanup.c | 32 +++++++++++++ src/XMPPCommands/Status.c | 5 ++ src/include/Parsee.h | 11 +++-- src/include/XMPPCommand.h | 18 ++++++-- 13 files changed, 218 insertions(+), 78 deletions(-) create mode 100644 src/XMPPCommands/Cleanup.c diff --git a/README.MD b/README.MD index b1f5005..dcf69d7 100644 --- a/README.MD +++ b/README.MD @@ -35,13 +35,15 @@ TODO TODO ## TODOS -- Mess with Cytoplasm to make it have support for something like Berkeley DB as -an *optional* dependency. This should increase reliability and speed for anyone. +- Mess with Cytoplasm to make it have support for something like LMDB as an +*optional* dependency. This should increase reliability and speed for anyone. - Nesting might be an issue we'll need to deal with. libdb and Berkley DB seem to lack support for them. If we can shove entries at specific indices, we _might_ just manage to get some system that can at least emulate that, and hopefully be reasonably faster than the filesystem, with some added reliability. +- Starting a DM from XMPP to a Matrix user(and also get rid of the '?'-syntax and +use another invalid Matrix char/valid XMPP char ('$'?) for escaped) - PROPER FUCKING AVATARS XEP-0084 IS THE WORST PIECE OF SHIT KNOWN TO MAN. If any Jabberbros want to diff --git a/XEPS-TBD.TXT b/XEPS-TBD.TXT index 441326a..e24c0ff 100644 --- a/XEPS-TBD.TXT +++ b/XEPS-TBD.TXT @@ -28,6 +28,9 @@ For future XEPs: - https://xmpp.org/extensions/xep-0449.html Stickers are great. Matrix and XMPP somewhat has support for them, so might be a nice-to-have, and also to push over XMPP support. + A minor issue with that is pack management. XMPP requires a pack field + which is used along PEP, it seems, and meanwhile Matrix has ''support'' + for packs too, tracking them is between "annoyance" and "yeah, no.". - https://xmpp.org/extensions/xep-0050.html Ad-hoc commands that bridge maintainers can deal with XMPP-style are also a nice to have. @@ -46,6 +49,7 @@ THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT: Not XEPs, but ideas that _needs_ to be added: - "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d "also it [Bifrost] doesn't respect voice either" + - Standalone/Static Parsee, ideally as small as it can be(if not as APE). - https://www.youtube.com/watch?v=InL414iDZmY diff --git a/src/Main.c b/src/Main.c index e7a56c9..c3d5ecd 100644 --- a/src/Main.c +++ b/src/Main.c @@ -22,6 +22,13 @@ static HttpServer *server = NULL; static pthread_t xmpp_thr; static XMPPComponent *jabber = NULL; +static volatile uint64_t start; +uint64_t +ParseeUptime(void) +{ + return UtilTsMillis() - start; +} + int Main(void) { @@ -30,6 +37,8 @@ Main(void) Stream *yaml; Cron *cron = NULL; + start = UtilTsMillis(); + memset(&conf, 0, sizeof(conf)); Log(LOG_INFO, "%s - v%s (Cytoplasm %s)", diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 6c2c537..621cedd 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -60,6 +60,8 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) XMPPJoinMUC(data->jabber, jid, rev); Free(rev); Free(muc); + + /* TODO: XEP-0084 magic to advertise a new avatar if possible. */ } Free(jid); Free(chat_id); diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index 6ae39de..985d588 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -824,3 +824,40 @@ ParseeManageBan(ParseeData *data, char *user, char *room) return banned; } +char * +ParseeStringifyDate(uint64_t millis) +{ + uint64_t rest = millis; + uint64_t hours, minutes, seconds; + char *hs, *ms, *ss, *out; + + hours = rest / (1 HOURS); + rest = rest % (1 HOURS); + + minutes = rest / (1 MINUTES); + rest = rest % (1 MINUTES); + + seconds = rest / (1 SECONDS); + + hs = StrInt(hours); + ms = StrInt(minutes); + ss = StrInt(seconds); + + out = StrConcat(8, + hours ? hs : "", + hours ? " hours" : "", + (hours && minutes) ? ", " : "", + + minutes ? ms : "", + minutes ? " minutes" : "", + (minutes && seconds) ? ", " : "", + + seconds ? ss : "", + seconds ? " seconds" : "" + ); + Free(hs); + Free(ms); + Free(ss); + + return out; +} diff --git a/src/Parsee/User.c b/src/Parsee/User.c index a61c812..9cb26a5 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -39,22 +39,6 @@ ParseeIsPuppet(const ParseeConfig *conf, char *user) * room. */ return flag; } -bool -ParseeIsJabberPuppet(const ParseeConfig *config, char *jid) -{ - char *resource; - bool ret; - if (!config || !jid) - { - return false; - } - - resource = ParseeGetResource(jid); - ret = *resource == '%'; - Free(resource); - - return ret; -} char * ParseeDecodeJID(char *str, char term) { @@ -155,6 +139,7 @@ ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias) return ParseeDecodeJID(data_start, ':'); } +static const char *HEX = "0123456789abcdef"; char * ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim) { @@ -168,7 +153,6 @@ ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim) ret = StrConcat(2, c->namespace_base, "_l_"); for (i = 0; i < strlen(jid); i++) { - const char *HEX = "0123456789abcdef"; char cpy = jid[i]; char cs[4] = { 0 }; cs[0] = cpy; @@ -237,60 +221,75 @@ char * ParseeEncodeMXID(char *mxid) { char *ret; - size_t i; + size_t i, j; if (!mxid) { return NULL; } - ret = Malloc(strlen(mxid) + 1); - for (i = 0; i < strlen(mxid) + 1; i++) + + /* Worst case scenario of 3-bytes the char */ + ret = Malloc(strlen(mxid) * 3 + 1); + for (i = 0, j = 0; i < strlen(mxid); i++) { char src = mxid[i]; /* TODO: More robust system */ - if (src == '@') + if (src <= 0x20 || + (src == '"' || src == '&' || + src == '\'' || src == '/' || + src == ':' || src == '<' || + src == '>' || src == '@' || + src == 0x7F)) { - src = '%'; + ret[j++] = '?'; + ret[j++] = HEX[(src & 0xF0) >> 4]; + ret[j++] = HEX[(src & 0x0F) >> 0]; + continue; } - else if (src == ':') - { - src = '='; - } - ret[i] = src; + ret[j++] = src; } + ret[j] = '\0'; return ret; } char * ParseeDecodeMXID(char *mxid) { - char *ret; - size_t i; + char *out = NULL; if (!mxid) { return NULL; } - ret = Malloc(strlen(mxid) + 1); - for (i = 0; i < strlen(mxid) + 1; i++) +#define Okay(c) ((c) && (c != '@')) + while (Okay(*mxid)) { - char src = mxid[i]; - if (src == '%') + char c = *mxid; + char buf[3] = { 0 }; + char *tmp; + if (c == '?' && Okay(*(mxid + 1)) && Okay(*(mxid + 2))) { - src = '@'; - } - else if (src == '=') - { - src = ':'; - } - else if (src == '@') - { - ret[i] = '\0'; - break; - } - ret[i] = src; - } + mxid++; - return ret; + memcpy(buf, mxid, 2); + buf[0] = strtol(buf, NULL, 16); + buf[1] = '\0'; + + tmp = StrConcat(2, out, buf); + Free(out); + out = tmp; + + mxid += 2; + continue; + } + memcpy(buf, mxid, 1); + tmp = StrConcat(2, out, buf); + Free(out); + out = tmp; + + mxid++; + } +#undef Okay + return out; } char * ParseeGetDMID(char *mxid, char *jid) diff --git a/src/XMPPCommand/Commands.c b/src/XMPPCommand/Commands.c index d16863c..d2b8908 100644 --- a/src/XMPPCommand/Commands.c +++ b/src/XMPPCommand/Commands.c @@ -8,6 +8,10 @@ struct XMPPCommand { XMPPCmdCallback callback; char *node, *name; + char *form_instruction; + char *form_title; + + /* TODO: On-the-fly generation of options */ Array *options; }; @@ -25,12 +29,34 @@ XMPPBasicCmd(char *node, char *name, XMPPCmdCallback callback_funct) cmd->callback = callback_funct; cmd->node = StrDuplicate(node); cmd->name = StrDuplicate(name); + cmd->form_instruction = NULL; + cmd->form_title = NULL; /* No options -> no form required */ cmd->options = NULL; return cmd; } +void +XMPPSetFormTitle(XMPPCommand *cmd, char *title) +{ + if (!cmd) + { + return; + } + Free(cmd->form_title); + cmd->form_title = StrDuplicate(title); +} +void +XMPPSetFormInstruction(XMPPCommand *cmd, char *instruction) +{ + if (!cmd) + { + return; + } + Free(cmd->form_instruction); + cmd->form_instruction = StrDuplicate(instruction); +} void XMPPFreeCommand(XMPPCommand *cmd) { @@ -47,6 +73,8 @@ XMPPFreeCommand(XMPPCommand *cmd) } ArrayFree(cmd->options); + XMPPSetFormInstruction(cmd, NULL); + XMPPSetFormTitle(cmd, NULL); Free(cmd->node); Free(cmd->name); Free(cmd); @@ -80,6 +108,25 @@ XMPPFormifyCommand(XMPPCommand *cmd) x = XMLCreateTag("x"); XMLAddAttr(x, "xmlns", "jabber:x:data"); XMLAddAttr(x, "type", "form"); + + if (cmd->form_title) + { + XMLElement *instr_xml, *instr_txt; + + instr_xml = XMLCreateTag("title"); + instr_txt = XMLCreateText(cmd->form_title); + XMLAddChild(instr_xml, instr_txt); + XMLAddChild(x, instr_xml); + } + if (cmd->form_instruction) + { + XMLElement *instr_xml, *instr_txt; + + instr_xml = XMLCreateTag("instructions"); + instr_txt = XMLCreateText(cmd->form_instruction); + XMLAddChild(instr_xml, instr_txt); + XMLAddChild(x, instr_xml); + } /* TODO: Other fields */ for (i = 0; i < ArraySize(cmd->options); i++) @@ -117,23 +164,6 @@ XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLEleme cmd->callback(m, from, form, to); } -void -XMPPShoveOptions(XMPPCommand *cmd, XMLElement *form) -{ - size_t i; - if (!cmd || !form) - { - return; - } - - for (i = 0; i < ArraySize(cmd->options); i++) - { - XMPPOption *opt = ArrayGet(cmd->options, i); - XMLElement *xml = XMPPOptionToXML(opt); - - XMLAddChild(form, xml); - } -} bool XMPPVerifyForm(XMPPCommand *cmd, XMLElement *form) { @@ -157,7 +187,7 @@ XMPPVerifyForm(XMPPCommand *cmd, XMLElement *form) /* Required field not found; die. */ return false; } - /* TODO */ + /* TODO: Verify type, array membership, uniqueness, etc... */ } } diff --git a/src/XMPPCommand/Manager.c b/src/XMPPCommand/Manager.c index 9052072..49bb251 100644 --- a/src/XMPPCommand/Manager.c +++ b/src/XMPPCommand/Manager.c @@ -252,7 +252,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) { XMLElement *actions = XMLCreateTag("actions"); XMLElement *completed = XMLCreateTag("complete"); - XMLElement *x = XMLCreateTag("x"); + XMLElement *x; XMLAddChild(actions, completed); session_id = XMPPRegisterSession(m, to, from, node); @@ -271,9 +271,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) XMLAddChild(command_xml, actions); XMLAddChild(iq, command_xml); - XMLAddAttr(x, "xmlns", "jabber:x:data"); - XMLAddAttr(x, "type", "form"); - XMPPShoveOptions(cmd, x); + x = XMPPFormifyCommand(cmd); XMLAddChild(command_xml, x); pthread_mutex_lock(&jabber->write_lock); @@ -286,6 +284,11 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) } /* Doesn't need to be freed, as it lives with m. */ + + /* TODO: I've noticed memory leakages around here with a + * StrDuplicate'd session ID. I think StrDuplicate should + * just "rat out" the original source in cases of leaks + * like this, maybe with extra data beyond the NULL-terminator */ session_id = XMPPRegisterSession(m, to, from, node); /* No forms, we good. */ diff --git a/src/XMPPCommand/Options.c b/src/XMPPCommand/Options.c index 15cdc5f..6c67fe8 100644 --- a/src/XMPPCommand/Options.c +++ b/src/XMPPCommand/Options.c @@ -175,6 +175,8 @@ XMPPOptionToXML(XMPPOption *opt) data = XMLCreateText(opt->desc); XMLAddChild(desc, data); XMLAddChild(elem, desc); + + XMLAddAttr(elem, "label", opt->desc); } if (opt->type == XMPP_OPTION_LIST) diff --git a/src/XMPPCommands/Cleanup.c b/src/XMPPCommands/Cleanup.c new file mode 100644 index 0000000..4554f53 --- /dev/null +++ b/src/XMPPCommands/Cleanup.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void +CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) +{ + ParseeData *data = XMPPGetManagerCookie(m); + char *trimmed = ParseeTrimJID(from); + + if (!ParseeIsAdmin(data, trimmed)) + { + SetNote("error", "User is not authorised to execute command."); + + Free(trimmed); + return; + } + Free(trimmed); + + ParseeCleanup(data); + /* TODO: Cleanup old sessions? */ + SetNote("info", "Parsee data was sucessfully cleant up."); +} diff --git a/src/XMPPCommands/Status.c b/src/XMPPCommands/Status.c index f09d896..41a923a 100644 --- a/src/XMPPCommands/Status.c +++ b/src/XMPPCommands/Status.c @@ -22,6 +22,7 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement * size_t gb = mb >> 10; size_t min = gb ? gb : (mb ? mb : (kb ? kb : alloc)); char *unit = gb ? "GB" : (mb ? "MB" : (kb ? "KB" : "B")); + XMLElement *x; XMLElement *title, *txt; @@ -52,6 +53,7 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement * /* Report */ Report("mem-alloc", "Heap allocated with Cytoplasm"); Report("xml-congest", "Unprocessed stanzas(congestion)"); + Report("uptime", "Parsee uptime"); /* Set */ BeginItem(); @@ -59,13 +61,16 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement * char *min_str = StrInt(min); char *congest = StrInt(ParseeCongestion()); char *alloc = StrConcat(3, min_str, " ", unit); + char *up = ParseeStringifyDate(ParseeUptime()); SetField("mem-alloc", alloc); SetField("xml-congest", congest); + SetField("uptime", up); Free(congest); Free(min_str); Free(alloc); + Free(up); } EndItem(); } diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 0efa7ca..fe6bd98 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -53,7 +53,7 @@ typedef struct ParseeData { #define GrabObject(obj, ...) JsonValueAsObject(JsonGet(obj, __VA_ARGS__)) #define GrabArray(obj, ...) JsonValueAsArray(JsonGet(obj, __VA_ARGS__)) -/* Milliseconds to UNIT macros, to be used like 30 SECONDS and 1 MINUTE +/* Milliseconds to UNIT macros, to be used like 30 SECONDS and 1 MINUTES * in timestamps */ #define SECONDS * 1000 #define MINUTES * 60 SECONDS @@ -90,9 +90,6 @@ extern void ParseeEventHandler(ParseeData *, HashMap *); /* Verifies if a user is a Parsee puppet user. */ extern bool ParseeIsPuppet(const ParseeConfig *, char *); -/* Verifies if a user is a Parsee puppet user on XMPP. */ -extern bool ParseeIsJabberPuppet(const ParseeConfig *, char *); - /* Decodes a local JID for a user into a string. */ extern char * ParseeDecodeLocalJID(const ParseeConfig *, char *); @@ -230,4 +227,10 @@ extern bool ParseeVerifyDMStanza(ParseeData *data, char *room_id, char *id); /* Checks if any user is an admin */ extern bool ParseeIsAdmin(ParseeData *data, char *user); + +/* Measures Parsee's overall uptime */ +extern uint64_t ParseeUptime(void); + +/* Turns a date into a nice "X minutes, Y seconds" string */ +extern char * ParseeStringifyDate(uint64_t millis); #endif diff --git a/src/include/XMPPCommand.h b/src/include/XMPPCommand.h index cec5d08..d5e3226 100644 --- a/src/include/XMPPCommand.h +++ b/src/include/XMPPCommand.h @@ -31,6 +31,8 @@ extern void XMPPAddOption(XMPPCommand *cmd, XMPPOption *opt); extern XMLElement * XMPPFormifyCommand(XMPPCommand *cmd); extern char * XMPPGetCommandNode(XMPPCommand *cmd); extern char * XMPPGetCommandDesc(XMPPCommand *cmd); +extern void XMPPSetFormInstruction(XMPPCommand *cmd, char *instruction); +extern void XMPPSetFormTitle(XMPPCommand *cmd, char *title); extern bool XMPPCommandRequiresForm(XMPPCommand *cmd); extern void XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form); @@ -51,7 +53,6 @@ extern bool XMPPIsOptionRequired(XMPPOption *opt); extern char * XMPPOptionVar(XMPPOption *opt); extern XMLElement * XMPPOptionToXML(XMPPOption *opt); -extern void XMPPShoveOptions(XMPPCommand *cmd, XMLElement *form); extern void XMPPFreeOption(XMPPOption *opt); extern void XMPPFreeCommand(XMPPCommand *cmd); @@ -92,11 +93,16 @@ extern bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeD /* --------------------------------- COMMANDS --------------------------------- */ #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", { \ XMPPOption *glob = XMPPCreateText(true, "glob", ""); \ XMPPSetDescription(glob, "Glob pattern to set as admin"); \ XMPPAddOption(cmd, glob); \ + \ + XMPPSetFormTitle(cmd, "Admin addition form"); \ + XMPPSetFormInstruction(cmd, "Select a glob pattern to add as an admin"); \ }) \ XMPP_COMMAND(AddNoflyCallback, "add-nofly", "Adds user to nofly", { \ XMPPOption *entity = XMPPCreateText(true, "entity", ""); \ @@ -105,10 +111,16 @@ extern bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeD XMPPAddOption(cmd, entity); \ XMPPSetDescription(reason, "Reason for the no-fly"); \ XMPPAddOption(cmd, reason); \ + \ + XMPPSetFormTitle(cmd, "No-fly addition form"); \ + XMPPSetFormInstruction(cmd, "Select a glob pattern to add to the nofly"); \ }) \ - XMPP_COMMAND(NoflyCallback, "nofly", "Get Parsee nofly list", {}) -#define XMPP_COMMAND(f,n,t,s) extern void f(XMPPCommandManager *, char *, XMLElement *, XMLElement *); +#define XMPP_COMMAND(f,n,t,s) \ + extern void \ + f(XMPPCommandManager *, char *, XMLElement *, XMLElement *); + XMPPCOMMANDS + #undef XMPP_COMMAND #endif