From 43b3f8aaf500968febdaeffa8331445f9b0967ea Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 7 Jul 2024 17:30:48 +0200 Subject: [PATCH] [ADD] Respect MIME, start bridging leaves/kicks Wow, this one _wasn't_ pushed at 3AM, Outstanding. --- XEPS-TBD.TXT | 6 +++- src/AS.c | 9 +++-- src/MatrixEventHandler.c | 34 ++++++++++++++++--- src/Routes/UserAck.c | 5 +-- src/XMPP/Stanza.c | 38 +++++++++++++++++++++ src/XMPPThread.c | 73 +++++++++++++++++++++++++++++++++------- src/include/AS.h | 2 +- src/include/XMPP.h | 3 +- 8 files changed, 146 insertions(+), 24 deletions(-) diff --git a/XEPS-TBD.TXT b/XEPS-TBD.TXT index bd8107c..11a08d4 100644 --- a/XEPS-TBD.TXT +++ b/XEPS-TBD.TXT @@ -10,7 +10,7 @@ Somewhat implemented XEPs: IM platforms I won't mention), so this doesn't sound too bad to do HALF-IMPLEMENTED: Removing reacts won't work. ~ https://xmpp.org/extensions/xep-0184.html - no one wants to send me receipts :((((((((((((( + Only Matrix->XMPP as of now For future XEPs: - https://xmpp.org/extensions/xep-0421.html @@ -31,6 +31,10 @@ For future XEPs: 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. + - https://xmpp.org/extensions/xep-0050.html + Ad-hoc commands that bridge maintainers can deal with XMPP-style are + also a nice to have. + ON STANDBY BECAUSE THESE HAVE BEEN TERRIBLE TO DEAL WITH AND WHO KEEPS WRITING THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT: (x) https://xmpp.org/extensions/xep-0084.html diff --git a/src/AS.c b/src/AS.c index ee012fb..c49b8dc 100644 --- a/src/AS.c +++ b/src/AS.c @@ -632,7 +632,7 @@ ASSetPL(const ParseeConfig *conf, char *id, HashMap *m) } char * -ASUpload(const ParseeConfig *c, Stream *from, unsigned int size) +ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime) { char *size_str, *path, *ret, *user; int i; @@ -640,7 +640,6 @@ ASUpload(const ParseeConfig *c, Stream *from, unsigned int size) HashMap *reply; if (!c || !from) { - Log(LOG_INFO, "Obvious upload fail"); return NULL; } @@ -655,6 +654,10 @@ ASUpload(const ParseeConfig *c, Stream *from, unsigned int size) { HttpRequestHeader(ctx, "Content-Length", size_str); } + if (mime) + { + HttpRequestHeader(ctx, "Content-Type", mime); + } HttpRequestSendHeaders(ctx); for (i = 0; i < size; i++) @@ -739,7 +742,7 @@ ASReupload(const ParseeConfig *c, char *from, char **mime) { size = strtol(content_len, NULL, 10); } - ret = ASUpload(c, HttpClientStream(ctx), size); + ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL); HttpClientContextFree(ctx); UriFree(uri); diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 4bdd23c..715103b 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -63,6 +63,35 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) Free(jid); Free(chat_id); } + else if (StrEquals(membership, "leave") && !ParseeIsPuppet(conf, state_key)) + { + /* TODO: Manage bans */ + XMPPComponent *jabber = data->jabber; + char *jid = ParseeEncodeMXID(state_key); + char *name = NULL, *rev = NULL, *muc_id = NULL; + char *reason = GrabString(event, 2, "content", "reason"); + + /* Try to find the chat ID */ + chat_id = ParseeGetFromRoomID(data, room_id); + muc_id = ParseeGetMUCID(data, chat_id); + if (!chat_id) + { + goto end; + } + + /* TODO: Check the name's validity */ + name = ASGetName(data->config, room_id, state_key); + rev = StrConcat(4, muc_id, "/", name, "[p]"); + + XMPPLeaveMUC(jabber, jid, rev, reason); + +end: + Free(chat_id); + Free(muc_id); + Free(name); + Free(rev); + Free(jid); + } } static void ParseeBotHandler(ParseeData *data, HashMap *event) @@ -95,7 +124,6 @@ ParseeBotHandler(ParseeData *data, HashMap *event) return; } - /* TODO: Make an improvment. This is the bare minimum */ if (!ParseeIsAdmin(data, sender)) { Free(ASSend( @@ -172,8 +200,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) return; } - - /* TODO: Reunite DM/MUC code to bring feature parity. */ type = direct ? "chat" : "groupchat"; user = GrabString(json, 1, "xmpp_user"); unauth = ParseeToUnauth(data, url); @@ -237,7 +263,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) stanza, sender, ev_id, unauth, origin_id, xmpp_ident ); - /* Culprit */ + if (direct) { ParseePushDMStanza( diff --git a/src/Routes/UserAck.c b/src/Routes/UserAck.c index ac31a21..0c2d548 100644 --- a/src/Routes/UserAck.c +++ b/src/Routes/UserAck.c @@ -48,6 +48,9 @@ RouteHead(RouteRoomAck, arr, argp) char *creator = NULL, *muc_name = NULL, *chatid = NULL; + /* TODO: If an ACK request maps to a MUC that has a chat ID, + * DO NOT create a new one, and instead make a new mapping to + * the previous one. */ response = ASVerifyRequest(args); if (response) { @@ -67,7 +70,6 @@ RouteHead(RouteRoomAck, arr, argp) if (ParseeManageBan(args->data, muc, NULL)) { HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED); - Log(LOG_INFO, "Nofly..."); response = MatrixCreateError( "M_NOT_FOUND", "XMPP MUC is banned from being accessed on this instance" @@ -94,7 +96,6 @@ RouteHead(RouteRoomAck, arr, argp) ); if (!id) { - Log(LOG_INFO, "No ID"); HttpResponseStatus(args->ctx, HTTP_INTERNAL_SERVER_ERROR); response = MatrixCreateError( "M_UNKNOWN", diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index 7892f72..be70c67 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -176,6 +176,44 @@ XMPPSendMUC(XMPPComponent *comp, char *fr, char *as, char *to, char *msg, char * pthread_mutex_unlock(&comp->write_lock); } void +XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason) +{ + XMLElement *presence; + char *from, *id; + if (!comp || !fr || !muc) + { + return; + } + + pthread_mutex_lock(&comp->write_lock); + + presence = XMLCreateTag("presence"); + XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host))); + XMLAddAttr(presence, "to", muc); + XMLAddAttr(presence, "id", (id = StrRandom(8))); + XMLAddAttr(presence, "type", "unavailable"); + + if (reason) + { + XMLElement *status = XMLCreateTag("status"); + XMLElement *string = XMLCreateText(reason); + + XMLAddChild(status, string); + XMLAddChild(presence, status); + } + + XMPPAnnotatePresence(presence); + + XMLEncode(comp->stream, presence); + StreamFlush(comp->stream); + + XMLFreeElement(presence); + Free(from); + Free(id); + + pthread_mutex_unlock(&comp->write_lock); +} +void XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc) { XMLElement *presence, *x; diff --git a/src/XMPPThread.c b/src/XMPPThread.c index 8ab6ada..8d20988 100644 --- a/src/XMPPThread.c +++ b/src/XMPPThread.c @@ -822,7 +822,7 @@ IQResult(ParseeData *args, XMLElement *stanza) /* TODO: Bound checks for a size limit. */ bdata = (char *) Base64Decode(base64, b64len); datastream = StrStreamReaderN(bdata, length); - mxc = ASUpload(args->config, datastream, length); + mxc = ASUpload(args->config, datastream, length, "image/png"); jid = ParseeLookupJID(from); from_matrix = ParseeEncodeJID(args->config, jid, false); @@ -859,34 +859,65 @@ IQResult(ParseeData *args, XMLElement *stanza) if (photo) { XMLElement *binval = XMLookForUnique(photo, "BINVAL"); - XMLElement *data = ArrayGet(binval->children, 0); + XMLElement *type = XMLookForUnique(photo, "TYPE"); + XMLElement *data = + binval ? ArrayGet(binval->children, 0) : NULL; + XMLElement *tdata = type ? ArrayGet(type->children, 0) : NULL; char *base64; char *bdata; size_t length, b64len; Stream *datastream; - char *mxc, *from_matrix, *jid; + char *mxc = NULL, *from_matrix = NULL, *jid = NULL; + char *room = NULL; if (!data || !data->data) { + Log(LOG_ERR, "%s NOT FOUND", HashMapGet(stanza->attrs, "from")); return; } + + /* Get the base64, decode it, and shove it in a nicely put + * MXC address */ base64 = data->data; b64len = base64 ? strlen(base64) : 0; length = Base64DecodedSize(base64, b64len); bdata = Base64Decode(base64, b64len); datastream = StrStreamReaderN(bdata, length); - mxc = ASUpload(args->config, datastream, length); - - jid = ParseeLookupJID(HashMapGet(stanza->attrs, "from")); - from_matrix = ParseeEncodeJID(args->config, jid, false); - ASSetAvatar(args->config, from_matrix, mxc); - - /* TODO: Check if already set. */ - + mxc = ASUpload( + args->config, + datastream, + length, + tdata ? tdata->data : NULL + ); Free(bdata); StreamClose(datastream); + + room = ParseeGetBridgedRoom(args, stanza); + jid = ParseeLookupJID(HashMapGet(stanza->attrs, "from")); + if (jid && !StrEquals(jid, HashMapGet(stanza->attrs, "from"))) + { + from_matrix = ParseeEncodeJID(args->config, jid, false); + ASSetAvatar(args->config, from_matrix, mxc); + } + else if (room) + { + char *mask = StrConcat(4, + "@", args->config->sender_localpart, + ":", args->config->homeserver_host + ); + HashMap *obj = HashMapCreate(); + + HashMapSet(obj, "url", JsonValueString(mxc)); + ASSetState( + args->config, + room, "m.room.avatar", "", + mask, obj + ); + Free(mask); + } Free(from_matrix); + Free(room); Free(jid); Free(mxc); } @@ -1117,7 +1148,7 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) json = DbJson(avatars); avatar_id = GrabString(json, 1, oid); - if (StrEquals(avatar_id, p_dat->data)) + if (avatar_id && StrEquals(avatar_id, p_dat->data)) { DbUnlock(args->db, avatars); return; @@ -1142,6 +1173,24 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) XMLFreeElement(vcard_request); Free(from); } + + /* TODO: Sending VCard on presence is slow and stalls the thread */ + if (0) + { + XMPPComponent *jabber = args->jabber; + char *from = StrConcat(2, "parsee@", args->config->component_host); + + XMLElement *vcard_request = CreateVCardRequest( + from, HashMapGet(stanza->attrs, "from") + ); + pthread_mutex_lock(&jabber->write_lock); + XMLEncode(jabber->stream, vcard_request); + StreamFlush(jabber->stream); + pthread_mutex_unlock(&jabber->write_lock); + + XMLFreeElement(vcard_request); + Free(from); + } #undef MUC_USER_NS } diff --git a/src/include/AS.h b/src/include/AS.h index 069d3ad..f029f9c 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -63,7 +63,7 @@ extern void ASSetAvatar(const ParseeConfig *c, char *user, char *mxc); extern char * ASGetName(const ParseeConfig *c, char *room, char *user); /* Uploads data to Matrix to be used later */ -extern char * ASUpload(const ParseeConfig *c, Stream *from, unsigned int size); +extern char * ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime); /* Reuploads a HTTP URL to Matrix, with an optional MIME type returned. */ extern char * ASReupload(const ParseeConfig *c, char *from, char **mime); diff --git a/src/include/XMPP.h b/src/include/XMPP.h index 51124f5..13e6c8e 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -24,8 +24,9 @@ extern XMPPComponent * XMPPInitialiseCompStream(char *host, int port); * after XMPPInitialiseCompStream. */ extern bool XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared); -/* Makes a user join a MUC */ +/* Makes a user join/leave a MUC */ extern void XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc); +extern void XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *r); /* TODO: XMPP stuff, I don't fucking know, I'm not a Jabbernerd. */ extern void XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *id);