From bb836789b24a95c0906eafb404c72bd80d36f534 Mon Sep 17 00:00:00 2001 From: LDA Date: Fri, 5 Jul 2024 20:24:15 +0200 Subject: [PATCH] [FIX/WIP] Plumbing, -Werror, death to hitmen Suspend killers are now no more. Except the actual killers to be gone eventually. Plumbing is also very basic as of now, but it "works". --- Makefile | 4 +- README.MD | 7 +- src/AS.c | 42 +++++++--- src/Commands/BanUser.c | 57 +++++--------- src/Commands/Help.c | 2 +- src/Commands/Plumb.c | 89 ++++++++++++++++++++++ src/Commands/Stats.c | 5 +- src/Commands/UnlinkMUC.c | 1 - src/Main.c | 2 - src/MatrixEventHandler.c | 13 ++-- src/Parsee/Data.c | 18 +++-- src/Parsee/User.c | 2 +- src/Routes/UserAck.c | 14 +++- src/Signal.c | 6 +- src/XML/SAX.c | 15 ++-- src/XMPP/Component.c | 2 + src/XMPP/MUC.c | 14 +--- src/XMPP/Stanza.c | 2 +- src/XMPPThread.c | 160 +++++++++++++++++++++++++-------------- src/include/AS.h | 2 +- src/include/Bot.h | 6 ++ src/include/Parsee.h | 6 +- src/include/Routes.h | 6 ++ 23 files changed, 310 insertions(+), 165 deletions(-) create mode 100644 src/Commands/Plumb.c diff --git a/Makefile b/Makefile index c6047dc..781f01d 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,8 @@ SOURCE=src OBJECT=build INCLUDES=src/include CC=cc -CFLAGS=-I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -O3 -LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -Wl,--export-dynamic -O3 +CFLAGS=-I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -O3 -g -ggdb -Wall -Werror +LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -Wl,--export-dynamic -O3 -g -ggdb BINARY=parsee # ============================ Compilation ================================= SRC_FILES:=$(shell find $(SOURCE) -name '*.c') diff --git a/README.MD b/README.MD index d61e9d5..e7c195e 100644 --- a/README.MD +++ b/README.MD @@ -42,8 +42,11 @@ TODO - Look at XEPS-TBD.TXT for XEPs to be done - Achievements ### Why? -> "[...] and it [BoVeX] has an achievement system, because another thing I don't like is when software will not acknowledge that you've reached an obscure error state, essentially outsmarting it." - - Tom Murphy VII +> "[...] and it [BoVeX] has an achievement system, because another thing I don't like is +> when software will not acknowledge that you've reached an obscure error state, essentially +> outsmarting it." + + - [Tom Murphy VII](https://tom7.org) ## DONATING/CONTRIBUTING If you know things about XMPP or Matrix, yet aren't familiar with C99, or just diff --git a/src/AS.c b/src/AS.c index 3fc4d12..211d429 100644 --- a/src/AS.c +++ b/src/AS.c @@ -222,19 +222,33 @@ ASKick(const ParseeConfig *conf, char *id, char *banned) HttpClientContextFree(ctx); JsonFree(json); } -void +char * ASJoin(const ParseeConfig *conf, char *id, char *masquerade) { HttpClientContext *ctx = NULL; HashMap *json = NULL; - char *path; - if (!conf || !id || !masquerade) + char *path, *ret; + if (!conf || !id) { - return; + return NULL; } + if (!masquerade) + { + char *raw = StrConcat(4, + "@", conf->sender_localpart, + ":", conf->homeserver_host + ); + masquerade = HttpUrlEncode(raw); + Free(raw); + } + else + { + masquerade = HttpUrlEncode(masquerade); + } + id = HttpUrlEncode(id); path = StrConcat(5, - "/_matrix/client/v3/rooms/", id, "/join?", + "/_matrix/client/v3/join/", id, "?", "user_id=", masquerade ); @@ -246,15 +260,23 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade) json = HashMapCreate(); ASAuthenticateRequest(conf, ctx); ParseeSetRequestJSON(ctx, json); + JsonFree(json); + + json = JsonDecode(HttpClientStream(ctx)); + ret = StrDuplicate(GrabString(json, 1, "room_id")); + JsonFree(json); HttpClientContextFree(ctx); - JsonFree(json); + Free(masquerade); + Free(id); + + return ret; } void ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state) { HttpClientContext *ctx = NULL; - char *path, *params; + char *path; if (!conf || !id || !type || !mask || !state) { JsonFree(state); @@ -278,7 +300,7 @@ char * ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c) { HttpClientContext *ctx = NULL; - char *path, *params; + char *path; char *txn, *ret; HashMap *reply; if (!conf || !id || !type || !user || !c) @@ -626,7 +648,6 @@ ASReupload(const ParseeConfig *c, char *from, char **mime) HttpClientContext *ctx; unsigned short port; int size = 0, flags = HTTP_FLAG_NONE; - int i; char *ret, *content_len; if (!c || !from) @@ -685,7 +706,7 @@ ASType(const ParseeConfig *c, char *user, char *room, bool status) { HttpClientContext *ctx = NULL; HashMap *json; - char *path, *full; + char *path; if (!c || !user || !room) { return; @@ -760,7 +781,6 @@ ASSetUserConfig(const ParseeConfig *c, char *user, char *key, HashMap *map) { HttpClientContext *ctx = NULL; - HashMap *json; char *path; if (!c || !key || !map) { diff --git a/src/Commands/BanUser.c b/src/Commands/BanUser.c index fb5e29b..6e42356 100644 --- a/src/Commands/BanUser.c +++ b/src/Commands/BanUser.c @@ -4,6 +4,7 @@ #include #include +#include #include CommandHead(CmdBanUser, cmd, argp) @@ -13,32 +14,21 @@ CommandHead(CmdBanUser, cmd, argp) HashMap *event = args->event; char *user = HashMapGet(cmd->arguments, "user"); char *room = HashMapGet(cmd->arguments, "room"); - char *msg; - char *msgtype = GrabString(event, 2, "content", "msgtype"); - char *body = GrabString(event, 2, "content", "body"); - char *id = GrabString(event, 1, "room_id"); - char *ev_id = GrabString(event, 1, "event_id"); - char *sender = GrabString(event, 1, "sender"); - char *profile = StrConcat(4, - "@", data->config->sender_localpart, - ":", data->config->homeserver_host - ); + + BotInitialise(); if (!user || !room) { - Free(profile); + BotDestroy(); return; } ASBan(data->config, room, user); - msg = StrConcat(3, "Banning '", user, "'..."); - Free(ASSend( - data->config, id, profile, - "m.room.message", - MatrixCreateNotice(msg) - )); - Free(msg); - Free(profile); + ReplySprintf("Banning %s from '%s'...", + user, room + ); + + BotDestroy(); } CommandHead(CmdNoFlyList, cmd, argp) { @@ -46,30 +36,19 @@ CommandHead(CmdNoFlyList, cmd, argp) ParseeData *data = args->data; HashMap *event = args->event; char *user = HashMapGet(cmd->arguments, "user"); - char *msg; - char *msgtype = GrabString(event, 2, "content", "msgtype"); - char *body = GrabString(event, 2, "content", "body"); - char *id = GrabString(event, 1, "room_id"); - char *ev_id = GrabString(event, 1, "event_id"); - char *sender = GrabString(event, 1, "sender"); - char *profile = StrConcat(4, - "@", data->config->sender_localpart, - ":", data->config->homeserver_host - ); + char *reason = HashMapGet(cmd->arguments, "reason"); + BotInitialise(); if (!user) { - Free(profile); + BotDestroy(); return; } - msg = StrConcat(3, "Banning '", user, "'..."); - Free(ASSend( - data->config, id, profile, - "m.room.message", - MatrixCreateNotice(msg) - )); - Free(msg); - ParseeGlobalBan(data, user); - Free(profile); + ReplySprintf("Banning %s for '%s'", + user, reason ? reason : "[no reason specified]" + ); + ParseeGlobalBan(data, user, reason); + + BotDestroy(); } diff --git a/src/Commands/Help.c b/src/Commands/Help.c index 66e572b..f60980c 100644 --- a/src/Commands/Help.c +++ b/src/Commands/Help.c @@ -12,7 +12,7 @@ CommandHead(CmdHelp, cmd, argp) { ParseeCmdArg *args = argp; ParseeData *data = args->data; - HashMap *json, *event = args->event; + HashMap *event = args->event; BotInitialise(); ReplyBasic("Commands: "); diff --git a/src/Commands/Plumb.c b/src/Commands/Plumb.c new file mode 100644 index 0000000..de2e417 --- /dev/null +++ b/src/Commands/Plumb.c @@ -0,0 +1,89 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +CommandHead(CmdPlumb, cmd, argp) +{ + ParseeCmdArg *args = argp; + ParseeData *data = args->data; + HashMap *event = args->event; + char *muc = NULL, *room = NULL, *chat_id = NULL; + char *room_id = NULL; + MUCInfo info = { .exists = false }; + + BotInitialise(); + + BotRequired(muc); + BotRequired(room); + + /* Check MUC viability */ + Log(LOG_INFO, "BAR1"); + if (ParseeManageBan(args->data, muc, NULL)) + { + ReplySprintf("MUC '%s' is not allowed on this bridge.", muc); + Log(LOG_INFO, "BAR1F"); + goto end; + } + Log(LOG_INFO, "BAR2"); + if (!XMPPQueryMUC(args->data->jabber, muc, &info)) + { + ReplySprintf("MUC '%s' does not exist.", muc); + Log(LOG_INFO, "BAR2F"); + goto end; + } + Log(LOG_INFO, "BAR3"); + if ((chat_id = ParseeGetFromMUCID(args->data, muc))) + { + Free(chat_id); + chat_id = NULL; + ReplySprintf("MUC '%s' is already mapped.", muc); + Log(LOG_INFO, "BAR3F"); + goto end; + } + + Log(LOG_INFO, "FOO"); + + /* Check room viability */ + room_id = ASJoin(args->data->config, room, NULL); + if (!room_id) + { + ReplySprintf("Room '%s' does not exist.", room); + Log(LOG_INFO, "FOO2"); + goto end; + } + Log(LOG_INFO, "FOO3"); + if ((chat_id = ParseeGetFromRoomID(args->data, room_id))) + { + Free(chat_id); + chat_id = NULL; + ReplySprintf("Room '%s' is already mapped.", room); + Log(LOG_INFO, "FOO4"); + goto end; + } + + Log(LOG_INFO, "FOO5"); + chat_id = ParseePushMUC(args->data, room_id, muc); + if (chat_id) + { + char *rev = StrConcat(2, muc, "/parsee"); + XMPPJoinMUC(args->data->jabber, "parsee", rev); + + Free(rev); + } + Log(LOG_INFO, "FOO6"); + + ReplySprintf("Plumbed '%s'", muc); +end: + Log(LOG_INFO, "End."); + BotDestroy(); + Free(chat_id); + Free(room_id); + XMPPFreeMUCInfo(info); +} diff --git a/src/Commands/Stats.c b/src/Commands/Stats.c index 2903653..b559bf5 100644 --- a/src/Commands/Stats.c +++ b/src/Commands/Stats.c @@ -12,8 +12,7 @@ CommandHead(CmdStats, cmd, argp) { ParseeCmdArg *args = argp; ParseeData *data = args->data; - HashMap *json, *event = args->event; - char *msg; + HashMap *event = args->event; size_t alloc = MemoryAllocated(); size_t kb = alloc >> 10; size_t mb = kb >> 10; @@ -30,7 +29,7 @@ CommandHead(CmdStats, cmd, argp) ); ReplySprintf("- Memory used: %d%s (reported by Cytoplasm)", - min, unit + (int) min, unit ); ReplySprintf("- Source code and licensing information: %s", REPOSITORY diff --git a/src/Commands/UnlinkMUC.c b/src/Commands/UnlinkMUC.c index a35dc57..3e2ae81 100644 --- a/src/Commands/UnlinkMUC.c +++ b/src/Commands/UnlinkMUC.c @@ -16,7 +16,6 @@ CommandHead(CmdUnlinkMUC, cmd, argp) HashMap *json, *event = args->event, *mucs; DbRef *ref; char *muc = NULL, *chat_id = NULL, *room = NULL; - JsonValue *val; BotInitialise(); diff --git a/src/Main.c b/src/Main.c index dd14916..0402394 100644 --- a/src/Main.c +++ b/src/Main.c @@ -20,12 +20,10 @@ static HttpServer *server = NULL; static pthread_t xmpp_thr; static XMPPComponent *jabber = NULL; -extern int ParseeFindDatastart(char *data); int Main(void) { HttpServerConfig conf; - ParseeData *data = NULL; const ParseeConfig *parsee_conf; Stream *yaml; Cron *cron = NULL; diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index c867977..442677a 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -28,7 +28,7 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) char *jid; bool direct = GrabBoolean(event, 2, "content", "is_direct"); bool bot = !strncmp(sender + 1, local, strlen(local)); - ASJoin(conf, room_id, state_key); + Free(ASJoin(conf, room_id, state_key)); jid = ParseeDecodeLocalJID(conf, state_key); @@ -70,7 +70,6 @@ ParseeBotHandler(ParseeData *data, HashMap *event) char *msgtype = GrabString(event, 2, "content", "msgtype"); char *body = GrabString(event, 2, "content", "body"); char *id = GrabString(event, 1, "room_id"); - char *ev_id = GrabString(event, 1, "event_id"); char *sender = GrabString(event, 1, "sender"); char *profile = StrConcat(4, "@", data->config->sender_localpart, @@ -117,7 +116,7 @@ ParseeBotHandler(ParseeData *data, HashMap *event) RouteCommand(data->handler, cmd, &arg); } -end: + Free(profile); CommandFree(cmd); } @@ -128,16 +127,14 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) DbRef *ref = NULL; HashMap *json; - char *msgtype = GrabString(event, 2, "content", "msgtype"); char *body = GrabString(event, 2, "content", "body"); char *id = GrabString(event, 1, "room_id"); char *ev_id = GrabString(event, 1, "event_id"); char *m_sender = GrabString(event, 1, "sender"); - char *chat_id, *muc_id, *jid; + char *chat_id, *muc_id; char *reply_id = MatrixGetReply(event); char *xepd = ParseeXMPPify(event); - char *cmd_lp = data->config->sender_localpart; - char *type, *user, *xmppified_user = NULL, *to; + char *type, *user, *xmppified_user = NULL, *to = NULL; char *unauth = NULL; char *origin_id = NULL, *stanza = NULL; char *sender = NULL; @@ -189,7 +186,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) } else { - char *name, *rev, *stanza; + char *name, *rev; /* Try to find the chat ID */ muc_id = ParseeGetMUCID(data, chat_id); if (!chat_id) diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index f96a371..3241981 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -369,14 +369,16 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) } } break; + default: + break; } return xepd; } char * ParseeXMPPify(HashMap *event) { - char *cntr, *type, *format, *html; - char *xepd = NULL, *tmp; + char *type, *format, *html; + char *xepd = NULL; XMLElement *elem; XMPPFlags flags; @@ -756,10 +758,10 @@ end: } void -ParseeGlobalBan(ParseeData *data, char *user) +ParseeGlobalBan(ParseeData *data, char *user, char *reason) { DbRef *ref; - HashMap *j; + HashMap *j, *obj; if (!data || !user) { return; @@ -773,7 +775,13 @@ ParseeGlobalBan(ParseeData *data, char *user) j = DbJson(ref); - JsonValueFree(HashMapSet(j, user, JsonValueObject(HashMapCreate()))); + obj = HashMapCreate(); + if (reason) + { + HashMapSet(obj, "reason", JsonValueString(reason)); + } + HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis())); + JsonValueFree(HashMapSet(j, user, JsonValueObject(obj))); DbUnlock(data->db, ref); } diff --git a/src/Parsee/User.c b/src/Parsee/User.c index 39b065b..9c44acf 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -529,7 +529,7 @@ ParseeSendPresence(ParseeData *data) DbRef *ref; HashMap *j, *mucs; JsonValue *val; - char *chatid = NULL, *muc; + char *muc; if (!data) { return; diff --git a/src/Routes/UserAck.c b/src/Routes/UserAck.c index b31bea1..ac31a21 100644 --- a/src/Routes/UserAck.c +++ b/src/Routes/UserAck.c @@ -13,8 +13,6 @@ RouteHead(RouteUserAck, arr, argp) ParseeHttpArg *args = argp; HashMap *request = NULL; HashMap *response = NULL; - Array *events; - size_t i; char *user = ArrayGet(arr, 0); response = ASVerifyRequest(args); @@ -44,8 +42,6 @@ RouteHead(RouteRoomAck, arr, argp) ParseeHttpArg *args = argp; HashMap *request = NULL; HashMap *response = NULL; - Array *events; - size_t i; MUCInfo info = { .exists = false }; char *room = ArrayGet(arr, 0), *muc = NULL, *id = NULL; @@ -119,6 +115,16 @@ RouteHead(RouteRoomAck, arr, argp) /* Creates a mapping */ chatid = ParseePushMUC(args->data, id, muc); + /* Invite the Parsee listener, so that we can have some + * events right up */ + { + char *rev = StrConcat(2, muc, "/parsee"); + Log(LOG_NOTICE, "Sending presence to %s", rev); + XMPPJoinMUC(args->data->jabber, "parsee", rev); + + Free(rev); + } + response = HashMapCreate(); end: if (chatid) diff --git a/src/Signal.c b/src/Signal.c index 7c11ad0..dd31a51 100644 --- a/src/Signal.c +++ b/src/Signal.c @@ -14,10 +14,6 @@ static XMPPComponent *jabber = NULL; static void SignalHandler(int signal) { - size_t i; - XMLElement *message, *body, *data; - char *from; - switch (signal) { case SIGPIPE: @@ -32,6 +28,8 @@ SignalHandler(int signal) return; } /* Create a loopback stanza, forcing the thread to die */ + + /* TODO: Better way to break out. */ Log(LOG_INFO, "Killing thread..."); XMPPKillThread(jabber, "killer"); pthread_join(xmpp_thr, NULL); diff --git a/src/XML/SAX.c b/src/XML/SAX.c index eb51c27..633c34b 100644 --- a/src/XML/SAX.c +++ b/src/XML/SAX.c @@ -39,7 +39,6 @@ struct XMLexer { static bool XMLookahead(XMLexer *lexer, const char *str, bool skip); /* Parses an XML "name" */ -static bool XMLIsStart(XMLexer *lexer); static char * XMLParseName(XMLexer *lexer); static bool XMLSkipSpace(XMLexer *lexer); static char * XMLParseAttValue(XMLexer *lexer); @@ -122,11 +121,8 @@ XMLEvent * XMLCrank(XMLexer *lexer) { XMLEvent *event = NULL; - char c; - int ch; char *attrname; HashMap *props; - char *key, *val; char cbuf[2] = { 0 }; char *tmp; if (!lexer) @@ -259,6 +255,9 @@ XMLCrank(XMLexer *lexer) XMLFreeEvent(event); event = XMLCreateEnd(lexer, attrname); break; + default: + /* TODO */ + break; } /* TODO: Crank our XML parser. */ return event; @@ -608,7 +607,7 @@ static char * XMLDecodeString(char *s) { char *ret = NULL, *tmp; - char c, cs[2] = { 0 }; + char cs[2] = { 0 }; while (*s) { @@ -724,10 +723,9 @@ XMLParseAttQuote(XMLexer *lexer) while ((c = XMLGetc(lexer))) { - //Log(LOG_INFO, "E1=%c", c); if (c == '&') { - char *code = NULL; + //char *code = NULL; int c2; int p2 = XMLInitialiseBuffer(lexer); int j = 0; @@ -774,7 +772,6 @@ XMLParseAttDouble(XMLexer *lexer) //Log(LOG_INFO, "E2=%c", c); if (c == '&') { - char *code = NULL; int c2; int p2 = XMLInitialiseBuffer(lexer); int j = 0; @@ -811,7 +808,7 @@ XMLParseAttDouble(XMLexer *lexer) static char * XMLParseAttValue(XMLexer *lexer) { - ssize_t point = XMLInitialiseBuffer(lexer); + XMLInitialiseBuffer(lexer); if (XMLookahead(lexer, "'", true)) { diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index 6d186f3..a2b405c 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -78,6 +78,8 @@ XMPPInitialiseCompStream(char *host, int port) comp->stream = stream; pthread_mutex_init(&comp->write_lock, NULL); + (void) error; + return comp; } diff --git a/src/XMPP/MUC.c b/src/XMPP/MUC.c index ba2e5cf..1f9ad1e 100644 --- a/src/XMPP/MUC.c +++ b/src/XMPP/MUC.c @@ -15,6 +15,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out) char *uuid, *from; if (!jabber || !muc) { + Log(LOG_WARNING, ":("); return false; } @@ -32,25 +33,19 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out) XMLAddChild(iq_query, query); Free(from); - Free(uuid); - /* Pause the XMPP thread */ - XMPPKillThread(jabber, "suspend"); - UtilSleepMillis(500); { XMLElement *identity; - /* -- WE ARE ON OUR OWN HERE WITH STANZAS -- */ XMLEncode(jabber->stream, iq_query); StreamFlush(jabber->stream); XMLFreeElement(iq_query); /* Except an IQ reply */ - iq_query = XMLDecode(jabber->stream, false); - /* TODO: I've spotted presence requests spawning there. */ + iq_query = ParseeAwaitStanza(uuid); + Free(uuid); if (!iq_query || !StrEquals(iq_query->name, "iq")) { XMLFreeElement(iq_query); - ParseeWakeupThread(); pthread_mutex_unlock(&jabber->write_lock); return false; } @@ -62,7 +57,6 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out) "conference")) { XMLFreeElement(iq_query); - ParseeWakeupThread(); pthread_mutex_unlock(&jabber->write_lock); return false; } @@ -79,9 +73,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out) XMLFreeElement(iq_query); } } - /* Wake it up once we're done */ pthread_mutex_unlock(&jabber->write_lock); - ParseeWakeupThread(); return true; } diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index 06a5050..f9f483f 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -198,7 +198,7 @@ XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc) void XMPPKillThread(XMPPComponent *jabber, char *killer) { - XMLElement *message, *body, *data; + XMLElement *message, *body; char *from; if (!jabber || !killer) diff --git a/src/XMPPThread.c b/src/XMPPThread.c index 4ed0360..a15ac4e 100644 --- a/src/XMPPThread.c +++ b/src/XMPPThread.c @@ -49,8 +49,8 @@ typedef struct XMPPIdentity { static int ICollate(unsigned char *cata, unsigned char *catb) { - size_t al = cata ? strlen(cata) : 0; - size_t bl = catb ? strlen(catb) : 0; + size_t al = cata ? strlen((char *) cata) : 0; + size_t bl = catb ? strlen((char *) catb) : 0; if (!al && !bl) { @@ -89,8 +89,8 @@ IdentitySort(void *idap, void *idbp) { XMPPIdentity *ida = idap; XMPPIdentity *idb = idbp; - unsigned char *cata = ida->category; - unsigned char *catb = idb->category; + unsigned char *cata = (unsigned char *) ida->category; + unsigned char *catb = (unsigned char *) idb->category; return ICollate(cata, catb); } @@ -144,7 +144,7 @@ XMPPGenerateVer(void) Sha = Sha1(S); Free(S); - S = Base64Encode(Sha, 20); + S = Base64Encode((const char *) Sha, 20); Free(Sha); ArrayFree(features); @@ -159,17 +159,6 @@ XMPPGenerateVer(void) return S; } -static pthread_mutex_t cond_var_lock = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER; - -void -ParseeWakeupThread(void) -{ - pthread_mutex_lock(&cond_var_lock); - pthread_cond_signal(&cond_var); - pthread_mutex_unlock(&cond_var_lock); -} - static char * ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza) { @@ -222,7 +211,6 @@ ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza) XMLElement *reactions = XMLookForTKV(stanza, "reactions", "xmlns", "urn:xmpp:reactions:0" ); - char *from = (HashMapGet(stanza->attrs, "from")); char *reacted_id = reactions ? HashMapGet(reactions->attrs, "id") : NULL; return ParseeGetEventFromID(data, stanza, reacted_id); @@ -237,7 +225,6 @@ ParseePushAllStanza(ParseeData *args, XMLElement *stanza, char *event) char *id_str = HashMapGet(stanza->attrs, "id"); char *s_id_str = XMPPGetStanzaID(stanza); - char *o_id_str = XMPPGetOriginID(stanza); if (!chat_id) { @@ -389,6 +376,8 @@ ManageProfileItem(ParseeData *args, XMLElement *item, XMLElement *stanza, XMPPTh pthread_mutex_unlock(&jabber->write_lock); XMLFreeElement(request); + + (void) url; /* TODO */ } end: @@ -406,7 +395,7 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) XMLElement *event = NULL; char *to, *room, *from, *from_matrix, *decode_from; - char *chat_id = NULL, *mroom_id = NULL; + char *mroom_id = NULL; size_t i; to = NULL; @@ -506,9 +495,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) { char *res = ParseeGetResource(from); char *encoded = ParseeEncodeJID(args->config, decode_from, false); - char *s_id_str = XMPPGetStanzaID(stanza); - char *o_id_str = XMPPGetOriginID(stanza); - char *id_str = HashMapGet(stanza->attrs, "id"); char *event_id = NULL; char *replaced = XMPPGetReplacedID(stanza); char *reply_to = XMPPGetReply(stanza); @@ -548,7 +534,7 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) ASSetName(args->config, encoded, res); } ASInvite(args->config, mroom_id, encoded); - ASJoin(args->config, mroom_id, encoded); + Free(ASJoin(args->config, mroom_id, encoded)); /* Check if it is a media link */ oob = XMLookForTKV(stanza, "x", "xmlns", "jabber:x:oob"); @@ -574,7 +560,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) } else if (reactions) { - char *reacted_id = HashMapGet(reactions->attrs, "id"); Array *react_child = reactions->children; size_t reacts = ArraySize(react_child); event_id = ParseeGetReactedEvent(args, stanza); @@ -642,8 +627,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) XMLElement *parsee = XMLookForUnique(stanza, "x-parsee"); XMLElement *event = XMLookForUnique(parsee, "event-id"); XMLElement *e_d = ArrayGet(event ? event->children : NULL, 0); - char *s_id_str = XMPPGetStanzaID(stanza); - char *id_str = HashMapGet(stanza->attrs, "id"); ParseePushAllStanza(args, stanza, e_d->data); } @@ -752,7 +735,6 @@ TrimBase64(char *b64) static void IQResult(ParseeData *args, XMLElement *stanza) { - XMPPComponent *jabber = args->jabber; XMLElement *vcard = XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"); XMLElement *event = XMLookForTKV(stanza, "pubsub", @@ -776,7 +758,7 @@ IQResult(ParseeData *args, XMLElement *stanza) char *id = HashMapGet(item->attrs, "id"); char *from = HashMapGet(stanza->attrs, "from"); char *base64; - unsigned char *bdata; + char *bdata; size_t length, b64len; Stream *datastream; char *mxc, *from_matrix, *jid; @@ -804,8 +786,8 @@ IQResult(ParseeData *args, XMLElement *stanza) b64len = base64 ? strlen(base64) : 0; length = Base64DecodedSize(base64, b64len); - /* TODO: Bound checks! */ - bdata = Base64Decode(base64, b64len); + /* TODO: Bound checks for a size limit. */ + bdata = (char *) Base64Decode(base64, b64len); datastream = StrStreamReaderN(bdata, length); mxc = ASUpload(args->config, datastream, length); @@ -837,13 +819,16 @@ IQResult(ParseeData *args, XMLElement *stanza) if (nickname) { XMLElement *data = ArrayGet(nickname->children, 0); + /* TODO: Use the nickname for something. + * And the rest of the vCard, somewhere. */ + (void) data; } if (photo) { XMLElement *binval = XMLookForUnique(photo, "BINVAL"); XMLElement *data = ArrayGet(binval->children, 0); char *base64; - unsigned char *bdata; + char *bdata; size_t length, b64len; Stream *datastream; char *mxc, *from_matrix, *jid; @@ -885,7 +870,7 @@ IQGet(ParseeData *args, XMLElement *stanza) else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version")) { XMLElement *iq_reply, *query; - XMLElement *name, *version, *val; + XMLElement *name, *version; char *from = HashMapGet(stanza->attrs, "from"); char *to = HashMapGet(stanza->attrs, "to"); char *id = HashMapGet(stanza->attrs, "id"); @@ -980,24 +965,22 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS))) { XMLElement *item = XMLookForUnique(user_info, "item"); - XMPPComponent *jabber = args->jabber; char *jid = item ? HashMapGet(item->attrs, "jid") : NULL; - char *from, *best = jid ? jid : oid; + char *from; char *type = HashMapGet(stanza->attrs, "type"); if (StrEquals(type, "unavailable")) { - /* TODO: Treat as a ban if the role is outcast */ + /* TODO: Treat as a ban if the role is outcast. + * Modify the code to be more accurate later. */ char *room = ParseeGetBridgedRoom(args, stanza); char *decode_from = ParseeLookupJID(oid); char *from_matrix = ParseeDecodeMXID(decode_from); char *affiliation = HashMapGet(item->attrs, "affiliation"); - if (!from_matrix || *from_matrix != '@') - { - Free(from_matrix); - from_matrix = ParseeEncodeJID(args->config, oid, true); - } + + Log(LOG_INFO, "Acting on %s", from_matrix); + Log(LOG_INFO, "OID=%s DF=%s", oid, decode_from); if (StrEquals(affiliation, "outcast")) { @@ -1097,11 +1080,11 @@ XMPPDispatcher(void *argp) { XMPPThread *thread = argp; ParseeData *args = thread->info->args; - XMPPComponent *jabber = thread->info->jabber; while (thread->info->running) { XMLElement *stanza = RetrieveStanza(thread); + if (!stanza) { UtilSleepMillis(1); @@ -1116,7 +1099,6 @@ XMPPDispatcher(void *argp) } else if (StrEquals(stanza->name, "message")) { - size_t i; if (!MessageStanza(args, stanza, thread)) { continue; @@ -1135,8 +1117,19 @@ XMPPDispatcher(void *argp) } XMLFreeElement(stanza); } + + return NULL; } +typedef struct XMPPAwait { + pthread_mutex_t cond_lock; + pthread_cond_t condition; + + XMLElement *stanza; +} XMPPAwait; +static pthread_mutex_t await_lock = PTHREAD_MUTEX_INITIALIZER; +static HashMap *await_table = NULL; + void * ParseeXMPPThread(void *argp) { @@ -1144,9 +1137,10 @@ ParseeXMPPThread(void *argp) XMPPComponent *jabber = args->jabber; XMLElement *stanza = NULL; XMPPThreadInfo info; - pthread_mutex_t stanzas_lock = PTHREAD_MUTEX_INITIALIZER; - size_t i, j = 0; + size_t i; + /* Initialise the await table */ + await_table = HashMapCreate(); /* Initialise the FIFO */ info.stanzas = ArrayCreate(); @@ -1174,9 +1168,7 @@ ParseeXMPPThread(void *argp) while (true) { - char *to, *room, *from, *from_matrix; - char *chat_id, *mroom_id; - ssize_t cntr; + char *from, *id; stanza = XMLDecode(jabber->stream, false); if (!stanza) @@ -1184,25 +1176,37 @@ ParseeXMPPThread(void *argp) continue; } + id = HashMapGet(stanza->attrs, "id"); + if (id) + { + XMPPAwait *await; + /* Lock out the table to see if we're awaiting. */ + pthread_mutex_lock(&await_lock); + if ((await = HashMapGet(await_table, id))) + { + await->stanza = stanza; + pthread_mutex_lock(&await->cond_lock); + pthread_cond_signal(&await->condition); + pthread_mutex_unlock(&await->cond_lock); + + HashMapDelete(await_table, id); + + pthread_mutex_unlock(&await_lock); + continue; + } + pthread_mutex_unlock(&await_lock); + } + + if (StrEquals(stanza->name, "message") && XMPPIsKiller(stanza)) { const char *killer = "killer"; - const char *suspend="suspend"; from = HashMapGet(stanza->attrs, "from"); if (!strncmp(from, killer, strlen(killer))) { XMLFreeElement(stanza); break; } - else if (!strncmp(from, suspend, strlen(suspend))) - { - /* TODO */ - XMLFreeElement(stanza); - pthread_mutex_lock(&cond_var_lock); - pthread_cond_wait(&cond_var, &cond_var_lock); - pthread_mutex_unlock(&cond_var_lock); - continue; - } } PushStanza(&info, stanza); @@ -1222,6 +1226,48 @@ ParseeXMPPThread(void *argp) } ArrayFree(info.stanzas); + HashMapFree(await_table); + pthread_mutex_destroy(&info.lock); return NULL; } +XMLElement * +ParseeAwaitStanza(char *identifier) +{ + XMPPAwait awa, *await; + XMLElement *stanza; + if (!identifier) + { + return NULL; + } + + /* TODO: Pthreads HATE me using Malloc here, so I'm abusing stackspace. + * Not *too much* of a problem, just a weird oddity. If anyone has a clue + * on why that happens (at least on ARM64 with a Pi4 running Debian), let + * me know! */ + await = &awa; + pthread_mutex_lock(&await_lock); + + pthread_cond_init(&await->condition, NULL); + pthread_mutex_init(&await->cond_lock, NULL); + await->stanza = NULL; + + HashMapSet(await_table, identifier, await); + pthread_mutex_unlock(&await_lock); + + pthread_mutex_lock(&await->cond_lock); + while (!await->stanza) + { + pthread_cond_wait(&await->condition, &await->cond_lock); + } + + stanza = await->stanza; + pthread_mutex_lock(&await_lock); + pthread_mutex_unlock(&await_lock); + + pthread_mutex_unlock(&await->cond_lock); + + pthread_cond_destroy(&await->condition); + pthread_mutex_destroy(&await->cond_lock); + return stanza; +} diff --git a/src/include/AS.h b/src/include/AS.h index ca8b38c..886e04a 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -24,7 +24,7 @@ extern void ASPing(const ParseeConfig *); /* Joins a room from an ID and a given user we want to masquerade * as. */ -extern void ASJoin(const ParseeConfig *, char *, char *); +extern char * ASJoin(const ParseeConfig *, char *, char *); /* Bans from a room a specific user */ extern void ASBan(const ParseeConfig *, char *, char *); diff --git a/src/include/Bot.h b/src/include/Bot.h index 9779dd5..12575a2 100644 --- a/src/include/Bot.h +++ b/src/include/Bot.h @@ -15,6 +15,12 @@ ); \ char *id = GrabString(event, 1, "room_id") +#define BotRequired(prop) prop = HashMapGet(cmd->arguments, #prop); \ + if (!prop) \ + { \ + ReplyBasic("Field `" #prop "` REQUIRED."); \ + } + #define BotDestroy() Free(profile) #define ReplyBasic(rep) do \ diff --git a/src/include/Parsee.h b/src/include/Parsee.h index bf415b4..b9f46f1 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -119,8 +119,8 @@ extern void ParseeRequest(HttpServerContext *, void *); /* A pthread callback used for listening to a component */ extern void * ParseeXMPPThread(void *data); -/* Wakes up the XMPP thread from a "suspend" killer stanza */ -extern void ParseeWakeupThread(void); +/* Wait for a specific stanza with an ID */ +extern XMLElement * ParseeAwaitStanza(char *identifier); /* Finds the room a DM is associated to, from a Matrix user and a Jabber * ID. */ @@ -204,7 +204,7 @@ extern void ParseeDestroyJIDTable(void); /* Globally bans a Matrix user from ever interacting with Parsee, and bans * them from bridged rooms where the bot has administrator. */ -extern void ParseeGlobalBan(ParseeData *, char *user); +extern void ParseeGlobalBan(ParseeData *, char *user, char *reason); /* Verifies if a user was globally banned. If so, then apply actions to the * room ID */ diff --git a/src/include/Routes.h b/src/include/Routes.h index 9243d67..98e5122 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -40,6 +40,12 @@ typedef struct ParseeCmdArg { "Sets the power level to send a specific event " \ "in a Parsee room" \ ) \ + X_COMMAND( \ + "plumb-muc", CmdPlumb, \ + "Plumbs an existing Matrix room to a MUC." \ + "You'll need to give the Parsee bot enough " \ + "privileges, however. " \ + ) \ X_COMMAND( \ "help", CmdHelp, \ "Shows the command list" \