From 3c26ee6d22501a8da97191f988ff751e839d562a Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 8 Jul 2024 14:36:52 +0200 Subject: [PATCH] [ADD/WIP] Bridging Matrix redacts I'll still need to consider making redacts from other people count as "moderations", whatever that is to Gajim, instead of a "self-redact". Also, _please_, support it if you _ever_ wish adoption, XMPP users! --- README.MD | 2 + src/MatrixEventHandler.c | 103 ++++++++++++++++++++++++++++++++++++++- src/XMPP/Stanza.c | 68 ++++++++++++++++++++++++++ src/XMPPThread.c | 2 + src/include/XMPP.h | 1 + 5 files changed, 174 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index d92fcfa..58f7ebc 100644 --- a/README.MD +++ b/README.MD @@ -35,6 +35,8 @@ 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. - PROPER FUCKING AVATARS XEP-0084 IS THE WORST PIECE OF SHIT KNOWN TO MAN. If any Jabberbros want to look at terrible code/XML and suggest things to have *proper* avatar support, diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 6500c61..359ab84 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -149,6 +149,77 @@ ParseeBotHandler(ParseeData *data, HashMap *event) Free(profile); CommandFree(cmd); } + +static const char * +GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) +{ + const char *type = NULL; + + char *room_id = GrabString(event, 1, "room_id"); + char *matrix_sender = GrabString(event, 1, "sender"); + char *chat_id = NULL, *muc_id = NULL; + char *user; + + DbRef *room_data; + HashMap *data_json; + + XMPPComponent *jabber = data ? data->jabber : NULL; + + bool direct = false; + if (!data || !event || !from || !to) + { + return NULL; + } + + *from = NULL; + *to = NULL; + + chat_id = ParseeGetFromRoomID(data, room_id); + + room_data = DbLock(data->db, 3, "rooms", room_id, "data"); + data_json = DbJson(room_data); + direct = GrabBoolean(data_json, 1, "is_direct"); + type = direct ? "chat" : "groupchat"; + + user = GrabString(data_json, 1, "xmpp_user"); + + *from = ParseeEncodeMXID(matrix_sender); + + if (direct) + { + *to = StrDuplicate(user); + Free(chat_id); + } + else + { + char *matrix_name, *muc_join_as; + + muc_id = ParseeGetMUCID(data, chat_id); + if (!chat_id) + { + /* muc_id is already implied to be freed by this point */ + + Free(*from); + *from = NULL; + DbUnlock(data->db, room_data); + return NULL; + } + + matrix_name = ASGetName(data->config, room_id, matrix_sender); + muc_join_as = StrConcat(4, muc_id, "/", matrix_name, "[p]"); + + XMPPJoinMUC(jabber, *from, muc_join_as); + + *to = muc_id; + + Free(matrix_name); + Free(muc_join_as); + } + + Free(chat_id); + DbUnlock(data->db, room_data); + return type; +} static void ParseeMessageHandler(ParseeData *data, HashMap *event) { @@ -316,9 +387,37 @@ ParseeEventHandler(ParseeData *data, HashMap *event) } else if (StrEquals(event_type, "m.room.redaction")) { - Log(LOG_WARNING, "Unsupported event redaction %s", event_id); + char *from, *to; + char *redacted = GrabString(event, 1, "redacts"); + char *redacted_stanza = NULL; + char *chat_id; + XMPPComponent *jabber = data->jabber; + + const char *type = GetXMPPInformation(data, event, &from, &to); + + chat_id = ParseeGetFromRoomID(data, room_id); + + if (!ParseeGetOrigin(data, chat_id, redacted, &redacted_stanza)) + { + ParseeGetDMOrigin(data, room_id, redacted, &redacted_stanza); + } + + /* Some clients don't support retractions *at all*, which smell. + * This therefore serves as a fallback, just in case that fails. */ + XMPPSendPlain( + jabber, from, to, + "[EDIT REDACT FROM PARSEE]", + (char *) type, + NULL, NULL, redacted, + NULL, redacted_stanza + ); + XMPPRetract(jabber, from, to, (char *) type, redacted_stanza); + + Free(redacted_stanza); + Free(chat_id); + Free(from); + Free(to); /* TODO: Implement Matrix->XMPP redactions. */ } - } diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index 1902a5f..b187b2c 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -15,6 +15,74 @@ XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, ch Free(ident); } +void +XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact) +{ + XMLElement *message; + char *ident = StrRandom(32), *from; + if (!comp || !fr || !to || !type || !redact) + { + Free(ident); + return; + } + + message = XMLCreateTag("message"); + XMLAddAttr(message, "from", (from = StrConcat(3, fr, "@", comp->host))); + XMLAddAttr(message, "to", to); + XMLAddAttr(message, "type", type); + XMLAddAttr(message, "id", ident); + + { + XMLElement *apply_to, *retract, *body, *txt, *fallback; + + apply_to = XMLCreateTag("apply-to"); + XMLAddAttr(apply_to, "xmlns", "urn:xmpp:fasten:0"); + XMLAddAttr(apply_to, "id", redact); + { + retract = XMLCreateTag("retract"); + XMLAddAttr(retract, "xmlns", "urn:xmpp:message-retract:0"); + XMLAddChild(apply_to, retract); + } + XMLAddChild(message, apply_to); + + retract = XMLCreateTag("retract"); + XMLAddAttr(retract, "xmlns", "urn:xmpp:message-retract:1"); + XMLAddAttr(retract, "id", redact); + XMLAddChild(message, retract); + + fallback = XMLCreateTag("fallback"); + XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0"); + XMLAddAttr(fallback, "for", "urn:xmpp:message-retract:0"); + XMLAddChild(message, fallback); + + fallback = XMLCreateTag("fallback"); + XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0"); + XMLAddAttr(fallback, "for", "urn:xmpp:message-retract:1"); + XMLAddChild(message, fallback); + + body = XMLCreateTag("body"); + txt = XMLCreateText( + "This person attempted to retract a previous message, " + "but your client does not yet support the 0.4.1 version of " + "XEP-0424.\n\n" + + "(_Parsee is jealous of being able to read such messages._)" + ); + XMLAddChild(body, txt); + XMLAddChild(message, body); + + } + + pthread_mutex_lock(&comp->write_lock); + XMLEncode(comp->stream, message); + StreamFlush(comp->stream); + XMLFreeElement(message); + + pthread_mutex_unlock(&comp->write_lock); + + Free(from); + Free(ident); +} void XMPPSendPlainID(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *edit, char *ident) { diff --git a/src/XMPPThread.c b/src/XMPPThread.c index 5c30248..26f9b20 100644 --- a/src/XMPPThread.c +++ b/src/XMPPThread.c @@ -48,6 +48,7 @@ typedef struct XMPPIdentity { char *category, *type, *lang, *name; } XMPPIdentity; +/* Generates the JID of the Parsee bridge user. */ static char * ParseeJID(ParseeData *data) { @@ -548,6 +549,7 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) { char *parsee = ParseeJID(args); + /* Subscribe to the sender's metadata node. */ XMLElement *ps = CreatePubsubRequest( parsee, decode_from, "urn:xmpp:avatar:metadata" ); diff --git a/src/include/XMPP.h b/src/include/XMPP.h index 7206978..064ce26 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -31,6 +31,7 @@ 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); extern void XMPPSendPlainID(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *id, char *sid); +extern void XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact); /* Finishes a component stream, and doesn't free it. */ extern void XMPPFinishCompStream(XMPPComponent *stream);