diff --git a/XEPS-TBD.TXT b/XEPS-TBD.TXT new file mode 100644 index 0000000..1a3444f --- /dev/null +++ b/XEPS-TBD.TXT @@ -0,0 +1,17 @@ +XEPs current supported are in src/XMPPThread.c, at the IQ disco advertising. + +For future XEPs: + - https://xmpp.org/extensions/xep-0444.html + This allows reactions, which Matrix also has support to. The two + systems don't seem *too* restrictive on one-another (unlike some + IM platforms I won't mention), so this doesn't sound too bad. + - https://xmpp.org/extensions/xep-0118.html + Informations on what a user is listening to. Matrix doesn't have + good support for status, to be frank. Clients (including KappaChat) + should consider having more support for those, rather than it being + stuck as a FluffyChat/Nheko feature. + If any client devs hear this, please consider adding these, + (especially if you're a smElement employee!) + - https://xmpp.org/extensions/xep-0084.html + Avatar support would be extremely useful, if just a QoL improvment. + Matrix and XMPP both have support for these. diff --git a/src/AS.c b/src/AS.c index 367b058..ee94fc8 100644 --- a/src/AS.c +++ b/src/AS.c @@ -289,6 +289,37 @@ ASSetName(const ParseeConfig *conf, char *user, char *name) JsonFree(json); Free(user); } +HashMap * +ASFind(const ParseeConfig *c, char *room, char *event) +{ + HttpClientContext *ctx = NULL; + HashMap *json; + char *path, *user; + if (!c || !room || !event) + { + return NULL; + } + + user = StrConcat(4, "@", c->sender_localpart, ":", c->homeserver_host); + path = StrConcat(7, + "/_matrix/client/v3/rooms/", + room, "/event/", event, "?", + "user_id=", user + ); + + ctx = ParseeCreateRequest(c, HTTP_GET, path); + Free(path); + ASAuthenticateRequest(c, ctx); + HttpRequestSendHeaders(ctx); + HttpRequestSend(ctx); + + json = JsonDecode(HttpClientStream(ctx)); + + HttpClientContextFree(ctx); + Free(user); + + return json; +} char * ASGetName(const ParseeConfig *c, char *room, char *user) { diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index a9d9b81..9efcc5b 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -9,6 +9,7 @@ #include #include +#include ParseeData * ParseeInitData(XMPPComponent *comp) @@ -82,7 +83,7 @@ ParseeCleanup(void *datp) uint64_t age = JsonValueAsInteger(HashMapGet(obj, "age")); uint64_t dur = ts - age; - if ((dur > (5 MINUTES))) + if ((dur > (30 MINUTES))) { ArrayAdd(to_delete, StrDuplicate(stanza)); } @@ -134,18 +135,44 @@ ParseeFindDatastart(char *data) } #include - +typedef struct XMPPFlags { + bool quote; +} XMPPFlags; static char * -XMPPifyElement(XMLElement *elem) +XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) { char *xepd = NULL, *tmp = NULL; size_t i; XMLElement *child; + char *reply_id = JsonValueAsString( + JsonGet(event, 4, + "content", "m.relates_to", "m.in_reply_to", "event_id" + )); + char *room_id = JsonValueAsString(HashMapGet(event, "room_id")); + HashMap *referenced; char *subxep; -#define Concat(strp) tmp = xepd; \ - xepd = StrConcat(2, xepd, strp); \ - Free(tmp) +#define Concat(strp) do \ + { \ + size_t cidx; \ + for (cidx = 0; cidx < strlen(strp); cidx++) \ + { \ + char cch[2] = { strp[cidx], 0 }; \ + char nch = *cch ? strp[cidx+1] : '\0'; \ + bool c = *cch == '\n' && nch != '>'; \ + if (c && flags.quote) \ + { \ + tmp = xepd; \ + xepd = StrConcat(2, xepd, "\n>"); \ + Free(tmp); \ + continue; \ + } \ + tmp = xepd; \ + xepd = StrConcat(2, xepd, cch); \ + Free(tmp); \ + } \ + } \ + while (0) switch (elem->type) { case XML_ELEMENT_DATA: @@ -158,7 +185,7 @@ XMPPifyElement(XMLElement *elem) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(child); + subxep = XMPPifyElement(event, child, flags); Concat(subxep); Free(subxep); @@ -171,7 +198,7 @@ XMPPifyElement(XMLElement *elem) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(child); + subxep = XMPPifyElement(event, child, flags); Concat(subxep); Free(subxep); @@ -184,24 +211,45 @@ XMPPifyElement(XMLElement *elem) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(child); + subxep = XMPPifyElement(event, child, flags); Concat(subxep); Free(subxep); } Concat("`"); } + else if (StrEquals(elem->name, "mx-reply")) + { + char *str; + referenced = ASFind(ParseeConfigGet(), room_id, reply_id); + str = JsonValueAsString( + JsonGet(referenced, 2, "content", "body") + ); + if (!str) + { + JsonFree(referenced); + return xepd; + } + Concat(">"); + flags.quote = true; + Concat(str); + flags.quote = false; + Concat("\n"); + JsonFree(referenced); + } else if (StrEquals(elem->name, "blockquote")) { Concat(">"); + flags.quote = true; for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(child); + subxep = XMPPifyElement(event, child, flags); Concat(subxep); Free(subxep); } + flags.quote = false; Concat("\n"); } else if (StrEquals(elem->name, "br")) @@ -211,11 +259,12 @@ XMPPifyElement(XMLElement *elem) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(child); + subxep = XMPPifyElement(event, child, flags); Concat(subxep); Free(subxep); } + Concat("\n"); } else if (StrEquals(elem->name, "a")) { @@ -224,7 +273,7 @@ XMPPifyElement(XMLElement *elem) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(child); + subxep = XMPPifyElement(event, child, flags); Concat(subxep); Free(subxep); @@ -238,7 +287,7 @@ XMPPifyElement(XMLElement *elem) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(child); + subxep = XMPPifyElement(event, child, flags); Concat(subxep); Free(subxep); @@ -254,6 +303,8 @@ ParseeXMPPify(HashMap *event) char *cntr, *type, *format, *html; char *xepd = NULL, *tmp; XMLElement *elem; + + XMPPFlags flags; if (!event) { return NULL; @@ -276,9 +327,10 @@ ParseeXMPPify(HashMap *event) html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body")); html = StrConcat(3, "", html, ""); - elem = XMLDecode(StrStreamReader(html), true); + elem = XMLCDecode(StrStreamReader(html), true, true); - xepd = XMPPifyElement(elem); + flags.quote = false; + xepd = XMPPifyElement(event, elem, flags); XMLFreeElement(elem); Free(html); diff --git a/src/XML/Parser.c b/src/XML/Parser.c index bf638ff..1b8d30a 100644 --- a/src/XML/Parser.c +++ b/src/XML/Parser.c @@ -6,7 +6,7 @@ #include XMLElement * -XMLDecode(Stream *stream, bool autofree) +XMLCDecode(Stream *stream, bool autofree, bool html) { #define push(x) ArrayAdd(stack, x) #define pop(x) ArrayDelete(stack, ArraySize(stack) - 1) @@ -45,7 +45,12 @@ XMLDecode(Stream *stream, bool autofree) * going to be our top of the stack */ ret = top; } - push(top); + + /* HACK!!! */ + if (!StrEquals(event->element, "br") || !html) + { + push(top); + } break; case XML_LEXER_ELEM: /* Create a new element that will populated. */ diff --git a/src/include/AS.h b/src/include/AS.h index e1bd679..11f21d4 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -26,6 +26,9 @@ extern void ASPing(const ParseeConfig *); * as. */ extern void ASJoin(const ParseeConfig *, char *, char *); +/* Finds an event from a room ID and event ID */ +extern HashMap * ASFind(const ParseeConfig *, char *, char *); + /* Sends a message event with a specific type and body. * Said body is freed during the function's execution. */ extern char * ASSend(const ParseeConfig *, char *, char *, char *, HashMap *); diff --git a/src/include/XML.h b/src/include/XML.h index ec7b4ca..ebf8eb7 100644 --- a/src/include/XML.h +++ b/src/include/XML.h @@ -56,7 +56,8 @@ typedef struct XMLElement { } XMLElement; /* Decodes 1 element off a stream. */ -extern XMLElement * XMLDecode(Stream *stream, bool autofree); +extern XMLElement * XMLCDecode(Stream *stream, bool autofree, bool html); +#define XMLDecode(stream, autofree) XMLCDecode(stream, autofree, false) extern XMLElement * XMLCreateTag(char *name); extern XMLElement * XMLCreateText(char *data);