From 0fc0fdd6f420d612fa2dc1ae4b20540fd9cc24f3 Mon Sep 17 00:00:00 2001 From: LDA Date: Sat, 6 Jul 2024 00:38:10 +0200 Subject: [PATCH] [ADD/WIP] Start receiptwerk --- XEPS-TBD.TXT | 2 ++ src/AS.c | 32 +++++++++++++++++++++ src/Main.c | 4 ++- src/MatrixEventHandler.c | 6 +++- src/Parsee/JIDTable.c | 60 ++++++++++++++++++++++++++++++++++++++++ src/XMPP/Stanza.c | 6 ++++ src/XMPPThread.c | 36 ++++++++++++++++++++---- src/include/AS.h | 1 + src/include/Parsee.h | 5 ++++ 9 files changed, 144 insertions(+), 8 deletions(-) diff --git a/XEPS-TBD.TXT b/XEPS-TBD.TXT index 61c04fe..d8ae43c 100644 --- a/XEPS-TBD.TXT +++ b/XEPS-TBD.TXT @@ -9,6 +9,8 @@ Somewhat implemented XEPs: systems don't seem *too* restrictive on one-another (unlike some 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 :((((((((((((( For future XEPs: - https://xmpp.org/extensions/xep-0421.html diff --git a/src/AS.c b/src/AS.c index 211d429..bc72892 100644 --- a/src/AS.c +++ b/src/AS.c @@ -732,6 +732,38 @@ ASType(const ParseeConfig *c, char *user, char *room, bool status) Free(user); } +void +ASPresence(const ParseeConfig *c, char *user, char *room, char *ev) +{ + HttpClientContext *ctx = NULL; + HashMap *json; + char *path; + if (!c || !user || !room || !ev) + { + return; + } + + user = HttpUrlEncode(user); + room = HttpUrlEncode(room); + ev = HttpUrlEncode(ev); + path = StrConcat(6, + "/_matrix/client/v3/rooms/", + room, "/receipt/m.read/", ev, + "?user_id=", user + ); + + json = HashMapCreate(); + ctx = ParseeCreateRequest(c, HTTP_POST, path); + Free(path); + ASAuthenticateRequest(c, ctx); + ParseeSetRequestJSON(ctx, json); + JsonFree(json); + + HttpClientContextFree(ctx); + Free(user); + Free(room); + Free(ev); +} HashMap * ASGetUserConfig(const ParseeConfig *c, char *user, char *key) diff --git a/src/Main.c b/src/Main.c index 0402394..4f09923 100644 --- a/src/Main.c +++ b/src/Main.c @@ -62,8 +62,9 @@ Main(void) } } - Log(LOG_NOTICE, "Creating JID table..."); + Log(LOG_NOTICE, "Creating volatile tables..."); ParseeInitialiseJIDTable(); + ParseeInitialiseHeadTable(); Log(LOG_NOTICE, "Setting up local Matrix user..."); ASRegisterUser(parsee_conf, parsee_conf->sender_localpart); @@ -112,6 +113,7 @@ end: CronStop(cron); CronFree(cron); ParseeFreeData(conf.handlerArgs); + ParseeDestroyHeadTable(); ParseeDestroyJIDTable(); return 0; } diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 442677a..9289436 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -266,13 +266,17 @@ end: void ParseeEventHandler(ParseeData *data, HashMap *event) { - char *event_type; + char *event_type, *event_id, *room_id; if (!data || !event) { return; } event_type = GrabString(event, 1, "type"); + event_id = GrabString(event, 1, "event_id"); + room_id = GrabString(event, 1, "room_id"); + ParseePushHeadTable(room_id, event_id); + if (StrEquals(event_type, "m.room.member")) { ParseeMemberHandler(data, event); diff --git a/src/Parsee/JIDTable.c b/src/Parsee/JIDTable.c index 765b1d6..c2b98de 100644 --- a/src/Parsee/JIDTable.c +++ b/src/Parsee/JIDTable.c @@ -69,3 +69,63 @@ ParseeDestroyJIDTable(void) pthread_mutex_unlock(&lock); pthread_mutex_destroy(&lock); } + +static pthread_mutex_t head_lock; +static HashMap *head_table = NULL; +void +ParseeInitialiseHeadTable(void) +{ + if (head_table) + { + return; + } + pthread_mutex_init(&head_lock, NULL); + pthread_mutex_lock(&head_lock); + head_table = HashMapCreate(); + pthread_mutex_unlock(&head_lock); +} +void +ParseePushHeadTable(char *room, char *event) +{ + if (!room || !event || !head_table) + { + return; + } + pthread_mutex_lock(&head_lock); + event = StrDuplicate(event); + Free(HashMapSet(head_table, room, event)); + pthread_mutex_unlock(&head_lock); +} +char * +ParseeLookupHead(char *room) +{ + char *event; + if (!room || !head_table) + { + return NULL; + } + pthread_mutex_lock(&head_lock); + event = StrDuplicate(HashMapGet(head_table, room)); + pthread_mutex_unlock(&head_lock); + + return event; +} +void +ParseeDestroyHeadTable(void) +{ + char *key; + void *val; + if (!head_table) + { + return; + } + pthread_mutex_lock(&head_lock); + while (HashMapIterate(head_table, &key, &val)) + { + Free(val); + } + HashMapFree(head_table); + head_table = NULL; + pthread_mutex_unlock(&head_lock); + pthread_mutex_destroy(&head_lock); +} diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index f9f483f..c4c861d 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -82,6 +82,12 @@ XMPPSendPlainID(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, XMLAddChild(message, xedit); } + { + XMLElement *request = XMLCreateTag("request"); + XMLAddAttr(request, "xmlns", "urn:xmpp:receipts"); + XMLAddChild(message, request); + } + if (oob) { XMLElement *xoob, *oob_data, *oob_url; diff --git a/src/XMPPThread.c b/src/XMPPThread.c index a15ac4e..99ae001 100644 --- a/src/XMPPThread.c +++ b/src/XMPPThread.c @@ -29,6 +29,7 @@ AdvertiseSimple("urn:xmpp:message-correct:0") \ AdvertiseSimple("urn:xmpp:reactions:0") \ AdvertiseSimple("urn:xmpp:styling:0") \ + AdvertiseSimple("urn:xmpp:receipts") \ AdvertiseSimple("urn:xmpp:reply:0") \ AdvertiseSimple("jabber:x:oob") \ AdvertiseSimple("vcard-temp") \ @@ -443,21 +444,48 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) decode_from = NULL; from_matrix = NULL; } - else if (XMLookForTKV(stanza, "active", "xmlns", CHAT_STATES)) + if (XMLookForTKV(stanza, "active", "xmlns", CHAT_STATES)) { + char *latest = NULL; decode_from = ParseeLookupJID(from); from_matrix = ParseeEncodeJID(args->config, decode_from, true); mroom_id = ParseeGetBridgedRoom(args, stanza); - + + latest = ParseeLookupHead(mroom_id); + ASType(args->config, from_matrix, mroom_id, false); + ASPresence(args->config, from_matrix, mroom_id, latest); Free(decode_from); Free(from_matrix); + Free(latest); Free(mroom_id); mroom_id = NULL; decode_from = NULL; from_matrix = NULL; } + if (XMLookForTKV(stanza, "paused", "xmlns", CHAT_STATES) || + XMLookForTKV(stanza, "received", "xmlns", "urn:xmpp:receipts")) + { + /* TODO: Use stanza ID if possible */ + char *latest = NULL; + decode_from = ParseeLookupJID(from); + from_matrix = ParseeEncodeJID(args->config, decode_from, true); + mroom_id = ParseeGetBridgedRoom(args, stanza); + + latest = ParseeLookupHead(mroom_id); + + ASPresence(args->config, from_matrix, mroom_id, latest); + + Free(decode_from); + Free(from_matrix); + Free(latest); + Free(mroom_id); + mroom_id = NULL; + decode_from = NULL; + from_matrix = NULL; + + } #undef CHAT_STATES body = XMLookForUnique(stanza, "body"); if (!body) @@ -978,10 +1006,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) char *from_matrix = ParseeDecodeMXID(decode_from); char *affiliation = HashMapGet(item->attrs, "affiliation"); - - Log(LOG_INFO, "Acting on %s", from_matrix); - Log(LOG_INFO, "OID=%s DF=%s", oid, decode_from); - if (StrEquals(affiliation, "outcast")) { ASBan(args->config, room, from_matrix); diff --git a/src/include/AS.h b/src/include/AS.h index 886e04a..bbfef07 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -40,6 +40,7 @@ extern HashMap * ASFind(const ParseeConfig *, char *, char *); * Said body is freed during the function's execution. */ extern char * ASSend(const ParseeConfig *, char *, char *, char *, HashMap *); extern void ASType(const ParseeConfig *, char *, char *, bool); +extern void ASPresence(const ParseeConfig *, char *, char *, char *); /* Sets a state event with a specific type and body */ extern void ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *event); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index b9f46f1..7deba6d 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -202,6 +202,11 @@ extern void ParseePushJIDTable(char *muc, char *bare); extern char *ParseeLookupJID(char *muc); extern void ParseeDestroyJIDTable(void); +extern void ParseeInitialiseHeadTable(void); +extern void ParseePushHeadTable(char *room, char *id); +extern char *ParseeLookupHead(char *room); +extern void ParseeDestroyHeadTable(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, char *reason);