From fbf169a080d517292010a49f2dcf1ec55f2fcdb2 Mon Sep 17 00:00:00 2001 From: LDA Date: Thu, 27 Jun 2024 20:09:52 +0200 Subject: [PATCH] [ADD/WIP] Bridge media Matrix->XMPP We're on that Reverse Ideology phase. --- src/MatrixEventHandler.c | 7 ++++-- src/Parsee/Config.c | 3 +++ src/Parsee/User.c | 40 ++++++++++++++++++++++++++++++++ src/Routes/Media.c | 49 ++++++++++++++++++++++++++++++++++++++++ src/XMPP/Stanza.c | 16 +++++++++++-- src/XMPPThread.c | 1 + src/include/Parsee.h | 3 +++ src/include/Routes.h | 3 ++- src/include/XMPP.h | 2 +- 9 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 src/Routes/Media.c diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 2e6a118..3475a7d 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -93,7 +93,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) char *user = GrabString(json, 1, "xmpp_user"); char *local = ParseeEncodeMXID(sender); - XMPPSendPlain(jabber, local, user, body, NULL, NULL, NULL, ev_id); + XMPPSendPlain(jabber, local, user, body, NULL, NULL, NULL, ev_id, NULL); Free(local); Free(reply_id); @@ -116,6 +116,8 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) char *name = ASGetName(data->config, NULL, sender); char *rev = StrConcat(3, muc_id, "/", name); char *stanza = NULL, *sender = NULL; + char *url = GrabString(event, 2, "content", "url"); + char *unauth = ParseeToUnauth(data, url); if (reply_id) { ParseeGetStanzaInfo(data, chat_id, reply_id, &stanza, &sender); @@ -124,12 +126,13 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) XMPPSendPlain( jabber, jid, muc_id, xepd ? xepd : body, "groupchat", - stanza, sender, ev_id + stanza, sender, ev_id, unauth ); Free(rev); Free(name); Free(stanza); Free(sender); + Free(unauth); } Free(chat_id); Free(muc_id); diff --git a/src/Parsee/Config.c b/src/Parsee/Config.c index dc7347b..dbf4590 100644 --- a/src/Parsee/Config.c +++ b/src/Parsee/Config.c @@ -75,6 +75,8 @@ ParseeConfigLoad(char *conf) CopyToStr(component_host, "component_host"); CopyToStr(shared_comp_secret, "shared_secret"); + CopyToStr(media_base, "media_base"); + CopyToStr(db_path, "db"); JsonFree(json); @@ -125,6 +127,7 @@ ParseeConfigFree(void) Free(config->hs_token); Free(config->sender_localpart); Free(config->namespace_base); + Free(config->media_base); Free(config->listen_as); Free(config); } diff --git a/src/Parsee/User.c b/src/Parsee/User.c index d91d8b1..259ba07 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -602,3 +602,43 @@ end: DbUnlock(data->db, ref); return ret; } + +#include +char * +ParseeToUnauth(ParseeData *data, char *mxc) +{ + Uri *url = NULL; + char *ret; +#define PAT "%s/_matrix/client/v1/media/download/%s%s" + size_t l; + if (!data || !mxc) + { + return NULL; + } + url = UriParse(mxc); + if (!url) + { + Log(LOG_ERR, "cant parse %s", mxc); + return NULL; + } + if (!StrEquals(url->proto, "mxc")) + { + UriFree(url); + return NULL; + } + + /* TODO: HTTPS */ + l = snprintf(NULL, 0, + PAT, + data->config->media_base, + url->host, url->path + ); + ret = Malloc(l + 3); + snprintf(ret, l + 1, + PAT, + data->config->media_base, + url->host, url->path + ); + UriFree(url); + return ret; +} diff --git a/src/Routes/Media.c b/src/Routes/Media.c new file mode 100644 index 0000000..e39a602 --- /dev/null +++ b/src/Routes/Media.c @@ -0,0 +1,49 @@ +#include + +#include +#include + +#include +#include +#include + +RouteHead(RouteMedia, arr, argp) +{ + ParseeHttpArg *args = argp; + HttpClientContext *cctx; + HashMap *reqh; + char *server = ArrayGet(arr, 0); + char *identi = ArrayGet(arr, 1); + char *path, *key, *val; + + /* TODO: Make it check the DB for its validicity. "Purging" would be useful. + */ + if (!server || !identi) + { + HttpResponseStatus(args->ctx, HTTP_BAD_REQUEST); + return MatrixCreateError("M_NOT_YET_UPLOADED", "No server/identifier"); + } + + server = HttpUrlEncode(server); + identi = HttpUrlEncode(identi); + path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi); + cctx = ParseeCreateRequest(args->data->config, HTTP_GET, path); + ASAuthenticateRequest(args->data->config, cctx); + Free(path); + + HttpRequestSendHeaders(cctx); + HttpRequestSend(cctx); + reqh = HttpResponseHeaders(cctx); + while (HashMapIterate(reqh, &key, (void **) &val)) + { + HttpResponseHeader(args->ctx, key, val); + } + HttpSendHeaders(args->ctx); + StreamCopy(HttpClientStream(cctx), HttpServerStream(args->ctx)); + + HttpClientContextFree(cctx); + Free(server); + Free(identi); + + return NULL; +} diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index b47c4e4..56be1a6 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -8,7 +8,7 @@ #include void -XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id) +XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob) { XMLElement *message, *body, *data, *parsee; char *from; @@ -25,7 +25,7 @@ XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, ch XMLAddAttr(message, "type", type); body = XMLCreateTag("body"); - data = XMLCreateText(msg); + data = XMLCreateText(oob ? oob : msg); /* TODO: Add Parsee specific fields here */ parsee = XMLCreateTag("x-parsee"); @@ -65,6 +65,18 @@ XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, ch /* TODO: Add custom fields depending on the caller's wishes */ } + if (oob) + { + XMLElement *xoob, *oob_data, *oob_url; + + xoob = XMLCreateTag("x"); + XMLAddAttr(xoob, "xmlns", "jabber:x:oob"); + oob_url = XMLCreateTag("url"); + oob_data = XMLCreateText(oob); + XMLAddChild(oob_url, oob_data); + XMLAddChild(xoob, oob_url); + XMLAddChild(message, xoob); + } if (rst && rse) { int off = ParseeFindDatastart(msg); diff --git a/src/XMPPThread.c b/src/XMPPThread.c index a4ba583..7eb6ef9 100644 --- a/src/XMPPThread.c +++ b/src/XMPPThread.c @@ -208,6 +208,7 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza) AdvertiseSimple("urn:xmpp:styling:0"); AdvertiseSimple("urn:xmpp:reply:0"); AdvertiseSimple("urn:xmpp:sid:0"); + AdvertiseSimple("jabber:x:oob"); AdvertiseSimple("urn:parsee:x-parsee:0"); AdvertiseSimple("urn:parsee:jealousy:0"); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index d34c942..0953a21 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -19,6 +19,7 @@ typedef struct ParseeConfig { char *namespace_base; char *listen_as; + char *media_base; int port; /* Homeserver port info */ @@ -171,4 +172,6 @@ extern char * ParseeXMPPify(HashMap *event); /* Finds an event ID from an ID in the stanza's attributes */ extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id); extern char * ParseeEventFromSID(ParseeData *d, char *c_id, char *ori_id); + +extern char * ParseeToUnauth(ParseeData *data, char *mxc); #endif diff --git a/src/include/Routes.h b/src/include/Routes.h index 6e69c58..b8bb474 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -13,7 +13,8 @@ typedef struct ParseeHttpArg { X_ROUTE("/_matrix/app/v1/transactions/(.*)", RouteTxns) \ X_ROUTE("/_matrix/app/v1/ping", RoutePing) \ X_ROUTE("/_matrix/app/v1/users/(.*)", RouteUserAck) \ - X_ROUTE("/_matrix/app/v1/rooms/(.*)", RouteRoomAck) + X_ROUTE("/_matrix/app/v1/rooms/(.*)", RouteRoomAck) \ + X_ROUTE("/_matrix/client/v1/media/download/(.*)/(.*)", RouteMedia) #define X_ROUTE(path, name) extern void * name(Array *, void *); ROUTES diff --git a/src/include/XMPP.h b/src/include/XMPP.h index d23b850..8c8adff 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -28,7 +28,7 @@ extern bool XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared); extern void XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc); /* 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); +extern void XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob); /* Closes a raw component stream. */ extern void XMPPEndCompStream(XMPPComponent *stream);