From aa9b68e02d8ac14feabb4e25da09947168c44c71 Mon Sep 17 00:00:00 2001 From: LDA Date: Fri, 16 Aug 2024 16:38:21 +0200 Subject: [PATCH 001/220] [ADD/WIP] Parsee IDs, -v flag --- src/HttParsee.c | 9 ++++++--- src/Main.c | 5 ++++- src/MatrixEventHandler.c | 13 +++++++++---- src/Parsee/Data.c | 20 +++++++++++++++++++- src/XMPP/Component.c | 2 +- src/XMPPThread/ReListener.c | 19 ++++++++++++++++--- src/include/Parsee.h | 1 + 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/HttParsee.c b/src/HttParsee.c index d6fd86b..1f9ade9 100644 --- a/src/HttParsee.c +++ b/src/HttParsee.c @@ -29,14 +29,14 @@ ParseeRequest(HttpServerContext *ctx, void *argp) arg.stream = stream; - Log(LOG_NOTICE, "%s %s", + Log(LOG_DEBUG, "%s %s", HttpRequestMethodToString(HttpRequestMethodGet(ctx)), path ); if (!HttpRouterRoute(data->router, path, &arg, (void **) &response)) { - Log(LOG_NOTICE, "Couldn't route %s", path); + Log(LOG_DEBUG, "Couldn't route %s", path); HttpResponseStatus(ctx, HTTP_NOT_FOUND); JsonFree(response); @@ -56,8 +56,11 @@ ParseeRequest(HttpServerContext *ctx, void *argp) HttpSendHeaders(ctx); JsonEncode(response, stream, JSON_DEFAULT); JsonFree(response); - return; } + Log(LOG_DEBUG, "%s %s (%d)", + HttpRequestMethodToString(HttpRequestMethodGet(ctx)), + path, HttpResponseStatusGet(ctx) + ); } HttpClientContext * diff --git a/src/Main.c b/src/Main.c index 226a9e4..ced6c59 100644 --- a/src/Main.c +++ b/src/Main.c @@ -56,7 +56,7 @@ Main(Array *args, HashMap *env) int http = 8; ArgParseStateInit(&state); - while ((flag = ArgParse(&state, args, "gH:J:")) != -1) + while ((flag = ArgParse(&state, args, "vgH:J:")) != -1) { switch (flag) { @@ -73,6 +73,9 @@ Main(Array *args, HashMap *env) ParseeExportConfigYAML(yaml); StreamClose(yaml); goto end; + case 'v': + LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG); + break; } } ParseeSetThreads(xmpp, http); diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index ce24854..f8dddc5 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -247,6 +247,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) char *sender = NULL; char *unedited_id = MatrixGetEdit(event); char *url = GrabString(event, 2, "content", "url"); + char *encoded_from = NULL; bool direct = false; @@ -284,9 +285,13 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) type = direct ? "chat" : "groupchat"; user = GrabString(json, 1, "xmpp_user"); unauth = ParseeToUnauth(data, url); + + encoded_from = ParseeEncodeMXID(m_sender); + xmppified_user = StrConcat(3, + encoded_from, "@", jabber->host + ); if (direct) { - xmppified_user = ParseeEncodeMXID(m_sender); to = StrDuplicate(user); Free(chat_id); @@ -300,7 +305,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) { goto end; } - xmppified_user = ParseeEncodeMXID(m_sender); /* TODO: Check the name's validity. * Is there a good way to check for that that isn't @@ -308,7 +312,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) name = ASGetName(data->config, id, m_sender); rev = StrConcat(4, muc_id, "/", name, "[p]"); - XMPPJoinMUC(jabber, xmppified_user, rev); + XMPPJoinMUC(jabber, encoded_from, rev); to = muc_id; @@ -342,7 +346,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) char *xmpp_ident = StrRandom(32); builder = CreateStanzaBuilder(xmppified_user, to, xmpp_ident); SetStanzaType(builder, type); - SetStanzaBody(builder, xepd ? xepd : body); + SetStanzaBody(builder, unauth ? unauth : (xepd ? xepd : body)); SetStanzaReply(builder, stanza, sender); SetStanzaLink(builder, unauth); SetStanzaEdit(builder, origin_id); @@ -372,6 +376,7 @@ end: Free(sender); Free(unauth); Free(unedited_id); + Free(encoded_from); DbUnlock(data->db, ref); ref = NULL; diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index e92e3e4..855f401 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -17,6 +17,7 @@ ParseeData * ParseeInitData(XMPPComponent *comp) { ParseeData *data; + DbRef *ref; if (!ParseeConfigGet()) { return NULL; @@ -39,6 +40,18 @@ ParseeInitData(XMPPComponent *comp) data->db = DbOpen(data->config->db_path, 0); } + if (!(ref = DbLock(data->db, 1, "info"))) + { + char *id = StrRandom(8); + ref = DbCreate(data->db, 1, "info"); + HashMapSet(DbJson(ref), "identifier", JsonValueString(id)); + Free(id); + } + + data->id = StrDuplicate(GrabString(DbJson(ref), 1, "identifier")); + Log(LOG_DEBUG, "- Parsee ID: %s", data->id); + DbUnlock(data->db, ref); + #define X_ROUTE(path, func) do {\ if (!HttpRouterAdd(data->router, path, func))\ {\ @@ -61,6 +74,7 @@ ParseeFreeData(ParseeData *data) return; } + Free(data->id); XMPPEndCompStream(data->jabber); DbClose(data->db); HttpRouterFree(data->router); @@ -76,8 +90,9 @@ ParseeCleanup(void *datp) char *chat; size_t i; uint64_t ts = UtilTsMillis(); + size_t entries = 0; - Log(LOG_NOTICE, "Cleaning up..."); + Log(LOG_DEBUG, "Cleaning up..."); chats = DbList(data->db, 1, "chats"); @@ -121,6 +136,7 @@ ParseeCleanup(void *datp) if (cleaned > threshold) \ { \ DbDelete(data->db, 4, "chats", chat, #field"s", field); \ + entries++; \ } \ Free(field); \ } \ @@ -174,6 +190,7 @@ ParseeCleanup(void *datp) if (cleaned > threshold) \ { \ JsonValueFree(HashMapDelete(field##s, field)); \ + entries++; \ } \ Free(field); \ } \ @@ -188,6 +205,7 @@ ParseeCleanup(void *datp) DbUnlock(data->db, ref); } DbListFree(chats); + Log(LOG_DEBUG, "Cleant up %d entries...", entries); } int ParseeFindDatastart(char *data) diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index e32c921..abf255c 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -140,7 +140,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared) stream_id = StrDuplicate(HashMapGet(ev->attrs, "id")); handshake = ComputeHandshake(shared, stream_id); - Log(LOG_NOTICE, "- sID='%s'", stream_id); + Log(LOG_DEBUG, "- sID='%s'", stream_id); StreamPrintf(stream, "%s", handshake); StreamFlush(stream); XMLFreeEvent(ev); diff --git a/src/XMPPThread/ReListener.c b/src/XMPPThread/ReListener.c index 85bcc44..62ff10b 100644 --- a/src/XMPPThread/ReListener.c +++ b/src/XMPPThread/ReListener.c @@ -170,7 +170,10 @@ ParseeXMPPThread(void *argp) pthread_t *thr = &info.dispatchers[i].thr; info.dispatchers[i].info = &info; - pthread_create(thr, NULL, XMPPDispatcher, &info.dispatchers[i]); + if (pthread_create(thr, NULL, XMPPDispatcher, &info.dispatchers[i])) + { + Achievement("COULDNT INITIALISE XMPP DISPATCHERS", true); + } } while (true) @@ -213,15 +216,25 @@ ParseeXMPPThread(void *argp) } info.running = false; - Log(LOG_INFO, "Joining subthreads..."); for (i = 0; i < info.available_dispatchers; i++) { pthread_t thr = info.dispatchers[i].thr; - pthread_join(thr, NULL); + if (pthread_join(thr, NULL)) + { + Achievement("COULDNT JOIN XMPP DISPATCHER", true); + } + + Log(LOG_DEBUG, "- joined %d/%d...", + i + 1, info.available_dispatchers + ); } Log(LOG_INFO, "Joined subthreads..."); Free(info.dispatchers); + if (ArraySize(info.stanzas)) + { + Log(LOG_DEBUG, "(%d unprocessed stanzas)", ArraySize(info.stanzas)); + } for (i = 0; i < ArraySize(info.stanzas); i++) { XMLFreeElement(ArrayGet(info.stanzas, i)); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index a98913c..a7c6fe4 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -55,6 +55,7 @@ typedef struct ParseeData { XMPPComponent *jabber; Db *db; + char *id; } ParseeData; #define GrabString(obj, ...) JsonValueAsString(JsonGet(obj, __VA_ARGS__)) From c0c13883d17816b19ac8a17a84e1395cf9a77b04 Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 18 Aug 2024 10:13:07 +0200 Subject: [PATCH 002/220] [MOD] Wow, it can save the world! --- src/MatrixEventHandler.c | 3 --- src/Routes/Root.c | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index f8dddc5..cfd3ddf 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -262,9 +262,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) chat_id = ParseeGetFromRoomID(data, id); - /* TODO: This ref should be marked as read-only, - * as LMDB doesn't seem to like having two concurrent RW - * transactions running. */ ref = DbLockIntent(data->db, DB_HINT_READONLY, 3, "rooms", id, "data"); json = DbJson(ref); direct = JsonValueAsBoolean(HashMapGet(json, "is_direct")); diff --git a/src/Routes/Root.c b/src/Routes/Root.c index 578dc20..56e462e 100644 --- a/src/Routes/Root.c +++ b/src/Routes/Root.c @@ -41,6 +41,7 @@ GetRandomQuote(void) "We truly live in a " CODE "...", "You truly lack the Desire Drive for this!", "Eeh? Bifrost mode? Only bad servers use Bifrost mode!", + "'Wow parsee can save the world' - said a wise guy", "As small as a dwarf, and can run on your pie!", "It's all wicked unsafe, memory slow 🚀🚀🚀🚀", From 59cbd8b22d38d1ce0f3b7be1aa0cfd191a31a56c Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 18 Aug 2024 16:11:12 +0200 Subject: [PATCH 003/220] [MOD] Hash-based name conflict resolution Still basic, but should prevent basic name issues. --- src/Commands/Plumb.c | 2 +- src/Main.c | 16 +++++-- src/MatrixEventHandler.c | 53 +++++++++++++++++------ src/Parsee/Data.c | 2 +- src/Parsee/User.c | 2 +- src/Routes/UserAck.c | 2 +- src/XMPP/Component.c | 5 ++- src/XMPP/MUC.c | 84 ++++++++++++++++++++++++++++++++++++ src/XMPP/Stanza.c | 86 ------------------------------------- src/XMPPThread/Stanzas/IQ.c | 2 +- src/include/XMPP.h | 2 +- 11 files changed, 147 insertions(+), 109 deletions(-) diff --git a/src/Commands/Plumb.c b/src/Commands/Plumb.c index d7b3ba0..e3c4ff3 100644 --- a/src/Commands/Plumb.c +++ b/src/Commands/Plumb.c @@ -62,7 +62,7 @@ CommandHead(CmdPlumb, cmd, argp) if (chat_id) { char *rev = StrConcat(2, muc, "/parsee"); - XMPPJoinMUC(args->data->jabber, "parsee", rev); + XMPPJoinMUC(args->data->jabber, "parsee", rev, false); Free(rev); } diff --git a/src/Main.c b/src/Main.c index ced6c59..017a115 100644 --- a/src/Main.c +++ b/src/Main.c @@ -101,15 +101,25 @@ Main(Array *args, HashMap *env) ParseeInitialiseOIDTable(); ParseeInitialiseHeadTable(); - Log(LOG_NOTICE, "Setting up local Matrix user..."); - ASRegisterUser(parsee_conf, parsee_conf->sender_localpart); - conf.port = parsee_conf->port; conf.threads = parsee_conf->http_threads; conf.maxConnections = conf.threads << 2; conf.handlerArgs = ParseeInitData(jabber); conf.handler = ParseeRequest; + Log(LOG_NOTICE, "Setting up local Matrix user..."); + if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart)) + { + char *parsee = ParseeMXID(conf.handlerArgs); + ASSetAvatar(parsee_conf, + parsee, + "mxc://tedomum.net/" + "7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136" + ); + ASSetName(parsee_conf, parsee, "Parsee bridge"); + Free(parsee); + } + Log(LOG_NOTICE, "Starting up local cronjobs..."); cron = CronCreate( 10 SECONDS ); CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs); diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index cfd3ddf..8827245 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,38 @@ #include #include +static void +JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) +{ + char *sender = GrabString(event, 1, "sender"); + + char *rev = StrConcat(3, muc, "/", name); + int nonce = 0; + + while (!XMPPJoinMUC(data->jabber, jid, rev, true) && nonce < 20) + { + char *nonce_str = StrInt(nonce); + char *input = StrConcat(4, sender, name, data->id, nonce_str); + unsigned char *digest = Sha256(input); + char *hex = ShaToHex(digest); + + if (strlen(hex) >= 8) + { + hex[8] = '\0'; + } + + Free(rev); + rev = StrConcat(6, muc, "/", name, "[", hex, "]"); + nonce++; + + Free(nonce_str); + Free(digest); + Free(input); + Free(hex); + } + Free(rev); +} + static void ParseeMemberHandler(ParseeData *data, HashMap *event) { @@ -55,10 +88,10 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) if (chat_id) { char *muc = ParseeGetMUCID(data, chat_id); - char *rev = StrConcat(2, muc, "/parsee"); + char *name = ASGetName(data->config, room_id, state_key); - XMPPJoinMUC(data->jabber, jid, rev); - Free(rev); + JoinMUC(data, event, jid, muc, name); + Free(name); Free(muc); /* TODO: XEP-0084 magic to advertise a new avatar if possible. */ @@ -162,8 +195,6 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) DbRef *room_data; HashMap *data_json; - XMPPComponent *jabber = data ? data->jabber : NULL; - bool direct = false; if (!data || !event || !from || !to) { @@ -194,7 +225,7 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) } else { - char *matrix_name, *muc_join_as; + char *matrix_name; muc_id = ParseeGetMUCID(data, chat_id); if (!chat_id) @@ -208,18 +239,16 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) } matrix_name = ASGetName(data->config, room_id, matrix_sender); - muc_join_as = StrConcat(4, muc_id, "/", matrix_name, "[p]"); /* TODO: Manage name conflicts. That would have been an easy * task(try the original one, and use a counter if it fails), * but that'd involve modifying the rest of the code, which * I'm not doing at 01:39 ... */ - XMPPJoinMUC(jabber, *from, muc_join_as); + JoinMUC(data, event, *from, muc_id, matrix_name); *to = muc_id; Free(matrix_name); - Free(muc_join_as); } Free(chat_id); @@ -295,7 +324,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) } else { - char *name, *rev; + char *name; /* Try to find the chat ID */ muc_id = ParseeGetMUCID(data, chat_id); if (!chat_id) @@ -307,14 +336,12 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) * Is there a good way to check for that that isn't * just "await on join and try again?" */ name = ASGetName(data->config, id, m_sender); - rev = StrConcat(4, muc_id, "/", name, "[p]"); - XMPPJoinMUC(jabber, encoded_from, rev); + JoinMUC(data, event, encoded_from, muc_id, name); to = muc_id; Free(name); - Free(rev); } if (reply_id) { diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index 855f401..489b6f2 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -780,7 +780,7 @@ ParseeManageBan(ParseeData *data, char *user, char *room) return false; } - ref = DbLock(data->db, 1, "global_bans"); + ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "global_bans"); j = DbJson(ref); while (HashMapIterate(j, &key, (void **) &val)) { diff --git a/src/Parsee/User.c b/src/Parsee/User.c index e1e2710..096530d 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -540,7 +540,7 @@ ParseeSendPresence(ParseeData *data) char *rev = StrConcat(2, muc, "/parsee"); /* Make a fake user join the MUC */ Log(LOG_NOTICE, "Sending presence to %s", rev); - XMPPJoinMUC(data->jabber, "parsee", rev); + XMPPJoinMUC(data->jabber, "parsee", rev, false); Free(rev); } diff --git a/src/Routes/UserAck.c b/src/Routes/UserAck.c index 3db19b7..9bb17e2 100644 --- a/src/Routes/UserAck.c +++ b/src/Routes/UserAck.c @@ -130,7 +130,7 @@ RouteHead(RouteRoomAck, arr, argp) { char *rev = StrConcat(2, muc, "/parsee"); Log(LOG_NOTICE, "Sending presence to %s", rev); - XMPPJoinMUC(args->data->jabber, "parsee", rev); + XMPPJoinMUC(args->data->jabber, "parsee", rev, false); Free(rev); } diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index abf255c..aa518cd 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -199,8 +199,11 @@ XMPPEndCompStream(XMPPComponent *comp) { return; } - pthread_mutex_destroy(&comp->write_lock); + pthread_mutex_lock(&comp->write_lock); StreamClose(comp->stream); + comp->stream = NULL; + pthread_mutex_unlock(&comp->write_lock); + pthread_mutex_destroy(&comp->write_lock); Free(comp->host); Free(comp); } diff --git a/src/XMPP/MUC.c b/src/XMPP/MUC.c index 2f29a38..832fdfe 100644 --- a/src/XMPP/MUC.c +++ b/src/XMPP/MUC.c @@ -167,3 +167,87 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc) XMLFreeElement(stanza); Free(identifier); } +bool +XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, bool care) +{ + XMLElement *presence, *x, *reply; + char *from, *id; + if (!comp || !fr || !muc) + { + return false; + } + + pthread_mutex_lock(&comp->write_lock); + + presence = XMLCreateTag("presence"); + x = XMLCreateTag("x"); + XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host))); + XMLAddAttr(presence, "to", muc); + XMLAddAttr(presence, "id", (id = StrRandom(8))); + XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc"); + + XMLAddChild(presence, x); + XMPPAnnotatePresence(presence); + + XMLEncode(comp->stream, presence); + StreamFlush(comp->stream); + + XMLFreeElement(presence); + Free(from); + + pthread_mutex_unlock(&comp->write_lock); + + if (care && (reply = ParseeAwaitStanza(id, 500))) + { + bool exit_code = true; + + if (XMPPHasError(reply, "conflict")) + { + exit_code = false; + } + + XMLFreeElement(reply); + Free(id); + return exit_code; + } + Free(id); + return true; +} +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); +} diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index 5340633..0a42174 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -76,93 +76,7 @@ XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact) Free(from); Free(ident); } -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); -} - -bool -XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc) -{ - XMLElement *presence, *x, *reply; - char *from, *id; - if (!comp || !fr || !muc) - { - return false; - } - - pthread_mutex_lock(&comp->write_lock); - - presence = XMLCreateTag("presence"); - x = XMLCreateTag("x"); - XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host))); - XMLAddAttr(presence, "to", muc); - XMLAddAttr(presence, "id", (id = StrRandom(8))); - XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc"); - - XMLAddChild(presence, x); - XMPPAnnotatePresence(presence); - - XMLEncode(comp->stream, presence); - StreamFlush(comp->stream); - - XMLFreeElement(presence); - Free(from); - - pthread_mutex_unlock(&comp->write_lock); - - /*if ((reply = ParseeAwaitStanza(id, 500))) - { - bool exit_code = true; - - if (XMPPHasError(reply, "conflict")) - { - Log(LOG_WARNING, "UNIMPLEMENTED NAMING CONFLICT."); - exit_code = false; - } - - XMLFreeElement(reply); - Free(id); - return exit_code; - }*/ - (void) reply; - Free(id); - return true; -} bool XMPPIsParseeStanza(XMLElement *stanza) { diff --git a/src/XMPPThread/Stanzas/IQ.c b/src/XMPPThread/Stanzas/IQ.c index 5e0f151..069fba5 100644 --- a/src/XMPPThread/Stanzas/IQ.c +++ b/src/XMPPThread/Stanzas/IQ.c @@ -393,7 +393,7 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr) version = XMLCreateTag("version"); XMLAddChild(name, XMLCreateText(NAME)); - XMLAddChild(version, XMLCreateText(VERSION)); + XMLAddChild(version, XMLCreateText(VERSION "[" CODE "]")); } XMLAddChild(query, name); XMLAddChild(query, version); diff --git a/src/include/XMPP.h b/src/include/XMPP.h index 07fea6a..4015847 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -30,7 +30,7 @@ extern XMPPComponent * XMPPInitialiseCompStream(char *host, int port); extern bool XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared); /* Makes a user join/leave a MUC */ -extern bool XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc); +extern bool XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, bool care); extern void XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *r); /* TODO: XMPP stuff, I don't fucking know, I'm not a Jabbernerd. */ From 198bdb98e9d30856398775bca904337c7d640846 Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 18 Aug 2024 16:41:52 +0200 Subject: [PATCH 004/220] [MOD] Conceal Parsee IDs Those are meant to be secrets, unknowns. No one, except Parsee itself should be aware about it. I may add a `parsee-regen` tool, but it shall not show it. --- src/Parsee/Data.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index 489b6f2..d13499d 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -42,14 +42,13 @@ ParseeInitData(XMPPComponent *comp) if (!(ref = DbLock(data->db, 1, "info"))) { - char *id = StrRandom(8); + char *id = StrRandom(64); ref = DbCreate(data->db, 1, "info"); HashMapSet(DbJson(ref), "identifier", JsonValueString(id)); Free(id); } data->id = StrDuplicate(GrabString(DbJson(ref), 1, "identifier")); - Log(LOG_DEBUG, "- Parsee ID: %s", data->id); DbUnlock(data->db, ref); #define X_ROUTE(path, func) do {\ From c975dba852240abeea54090c4b12086dae8ce5e5 Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 18 Aug 2024 23:52:47 +0200 Subject: [PATCH 005/220] [MOD] Remove the last [p]based nicks, table moving --- README.MD | 7 +- src/Main.c | 7 ++ src/MatrixEventHandler.c | 27 +++-- src/Parsee/JIDTable.c | 194 ---------------------------------- src/Parsee/Tables/HeadTable.c | 67 ++++++++++++ src/Parsee/Tables/JIDTable.c | 72 +++++++++++++ src/Parsee/Tables/NickTable.c | 105 ++++++++++++++++++ src/Parsee/Tables/OIDTable.c | 68 ++++++++++++ src/include/Parsee.h | 5 + 9 files changed, 344 insertions(+), 208 deletions(-) delete mode 100644 src/Parsee/JIDTable.c create mode 100644 src/Parsee/Tables/HeadTable.c create mode 100644 src/Parsee/Tables/JIDTable.c create mode 100644 src/Parsee/Tables/NickTable.c create mode 100644 src/Parsee/Tables/OIDTable.c diff --git a/README.MD b/README.MD index 672d11b..fa305db 100644 --- a/README.MD +++ b/README.MD @@ -11,9 +11,10 @@ I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Al this means that I can integrate Parsee with KappaChat however I wish it to be, which allows me to mess around with a codebase I'm already familiar with. A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*. -Well, I'm *trying* to do that, at least. Please scream at me if that fails(or just doesn't run -on a overclocked Raspberry Pi 4B, which, by the way, was literally where Parsee+XMPP ran for -a good chunk of Parsee's start.) + +Well, I'm *trying* to do that, at least. +Please scream at me if that fails(or just doesn't run on a overclocked Raspberry +Pi 4B, which, by the way, is literally where Parsee+XMPP is running for now.) ### "Why not just use Matrix lol" ### "Why not just use XMPP lol" diff --git a/src/Main.c b/src/Main.c index 017a115..1b78a24 100644 --- a/src/Main.c +++ b/src/Main.c @@ -56,6 +56,8 @@ Main(Array *args, HashMap *env) int http = 8; ArgParseStateInit(&state); + /* TODO: Have a smarter way of generating the arg table + * (with a list of structs, with a description and everything) */ while ((flag = ArgParse(&state, args, "vgH:J:")) != -1) { switch (flag) @@ -76,6 +78,9 @@ Main(Array *args, HashMap *env) case 'v': LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG); break; + case '?': + Log(LOG_ERR, "INVALID ARGUMENT GIVEN"); + goto end; } } ParseeSetThreads(xmpp, http); @@ -100,6 +105,7 @@ Main(Array *args, HashMap *env) ParseeInitialiseJIDTable(); ParseeInitialiseOIDTable(); ParseeInitialiseHeadTable(); + ParseeInitialiseNickTable(); conf.port = parsee_conf->port; conf.threads = parsee_conf->http_threads; @@ -159,6 +165,7 @@ end: CronStop(cron); CronFree(cron); ParseeFreeData(conf.handlerArgs); + ParseeDestroyNickTable(); ParseeDestroyOIDTable(); ParseeDestroyHeadTable(); ParseeDestroyJIDTable(); diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 8827245..d688c3c 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -17,7 +17,8 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) { char *sender = GrabString(event, 1, "sender"); - char *rev = StrConcat(3, muc, "/", name); + char *nick = StrDuplicate(name); + char *rev = StrConcat(3, muc, "/", nick); int nonce = 0; while (!XMPPJoinMUC(data->jabber, jid, rev, true) && nonce < 20) @@ -32,8 +33,11 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) hex[8] = '\0'; } + Free(nick); Free(rev); - rev = StrConcat(6, muc, "/", name, "[", hex, "]"); + + nick = StrConcat(4, name, "[", hex, "]"); + rev = StrConcat(3, muc, "/", nick); nonce++; Free(nonce_str); @@ -41,6 +45,9 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) Free(input); Free(hex); } + + ParseePushNickTable(muc, sender, nick); + Free(nick); Free(rev); } @@ -118,11 +125,15 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) goto end; } - /* TODO: Check the name's validity */ - name = ASGetName(data->config, room_id, state_key); - rev = StrConcat(4, muc_id, "/", name, "[p]"); + /* TODO: We need to deal with the nick properly, as XMPP + * requires us to provide it whenever we want to even think + * about leaving... + * I love how this is the last place victim of the dreaded [p]... */ + name = StrDuplicate(ParseeLookupNick(muc_id, sender)); + rev = StrConcat(3, muc_id, "/", name); XMPPLeaveMUC(jabber, jid, rev, reason); + ParseePushNickTable(muc_id, sender, NULL); end: Free(chat_id); Free(muc_id); @@ -239,13 +250,7 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) } matrix_name = ASGetName(data->config, room_id, matrix_sender); - - /* TODO: Manage name conflicts. That would have been an easy - * task(try the original one, and use a counter if it fails), - * but that'd involve modifying the rest of the code, which - * I'm not doing at 01:39 ... */ JoinMUC(data, event, *from, muc_id, matrix_name); - *to = muc_id; Free(matrix_name); diff --git a/src/Parsee/JIDTable.c b/src/Parsee/JIDTable.c deleted file mode 100644 index 772b975..0000000 --- a/src/Parsee/JIDTable.c +++ /dev/null @@ -1,194 +0,0 @@ -#include - -#include -#include -#include -#include - -static pthread_mutex_t lock; -static HashMap *jid_table = NULL; - -void -ParseeInitialiseJIDTable(void) -{ - if (jid_table) - { - return; - } - pthread_mutex_init(&lock, NULL); - pthread_mutex_lock(&lock); - jid_table = HashMapCreate(); - pthread_mutex_unlock(&lock); -} -void -ParseePushJIDTable(char *muc, char *bare) -{ - if (!muc || !bare || !jid_table) - { - return; - } - pthread_mutex_lock(&lock); - bare = ParseeTrimJID(bare); - Free(HashMapSet(jid_table, muc, bare)); - pthread_mutex_unlock(&lock); -} -char * -ParseeLookupJID(char *muc) -{ - char *bare; - if (!muc || !jid_table) - { - return NULL; - } - pthread_mutex_lock(&lock); - bare = StrDuplicate(HashMapGet(jid_table, muc)); - pthread_mutex_unlock(&lock); - - if (!bare) - { - bare = StrDuplicate(muc); - } - return bare; -} -void -ParseeDestroyJIDTable(void) -{ - char *key; - void *val; - if (!jid_table) - { - return; - } - pthread_mutex_lock(&lock); - while (HashMapIterate(jid_table, &key, &val)) - { - Free(val); - } - HashMapFree(jid_table); - jid_table = NULL; - 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); -} - - -static pthread_mutex_t oid_lock; -static HashMap *oid_table = NULL; - -void -ParseeInitialiseOIDTable(void) -{ - if (oid_table) - { - return; - } - pthread_mutex_init(&oid_lock, NULL); - pthread_mutex_lock(&oid_lock); - oid_table = HashMapCreate(); - pthread_mutex_unlock(&oid_lock); -} -void -ParseePushOIDTable(char *muc, char *bare) -{ - if (!muc || !bare || !oid_table) - { - return; - } - pthread_mutex_lock(&oid_lock); - bare = StrDuplicate(bare); - Free(HashMapSet(oid_table, muc, bare)); - pthread_mutex_unlock(&oid_lock); -} -char * -ParseeLookupOID(char *muc) -{ - char *bare; - if (!muc || !oid_table) - { - return NULL; - } - pthread_mutex_lock(&oid_lock); - bare = StrDuplicate(HashMapGet(oid_table, muc)); - pthread_mutex_unlock(&oid_lock); - - return bare; -} -void -ParseeDestroyOIDTable(void) -{ - char *key; - void *val; - if (!oid_table) - { - return; - } - pthread_mutex_lock(&oid_lock); - while (HashMapIterate(oid_table, &key, &val)) - { - Free(val); - } - HashMapFree(oid_table); - oid_table = NULL; - pthread_mutex_unlock(&oid_lock); - pthread_mutex_destroy(&oid_lock); -} - diff --git a/src/Parsee/Tables/HeadTable.c b/src/Parsee/Tables/HeadTable.c new file mode 100644 index 0000000..f910af5 --- /dev/null +++ b/src/Parsee/Tables/HeadTable.c @@ -0,0 +1,67 @@ +#include + +#include +#include +#include +#include + +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/Parsee/Tables/JIDTable.c b/src/Parsee/Tables/JIDTable.c new file mode 100644 index 0000000..e5663c9 --- /dev/null +++ b/src/Parsee/Tables/JIDTable.c @@ -0,0 +1,72 @@ +#include + +#include +#include +#include +#include + +static pthread_mutex_t lock; +static HashMap *jid_table = NULL; + +void +ParseeInitialiseJIDTable(void) +{ + if (jid_table) + { + return; + } + pthread_mutex_init(&lock, NULL); + pthread_mutex_lock(&lock); + jid_table = HashMapCreate(); + pthread_mutex_unlock(&lock); +} +void +ParseePushJIDTable(char *muc, char *bare) +{ + if (!muc || !bare || !jid_table) + { + return; + } + pthread_mutex_lock(&lock); + bare = ParseeTrimJID(bare); + Free(HashMapSet(jid_table, muc, bare)); + pthread_mutex_unlock(&lock); +} +char * +ParseeLookupJID(char *muc) +{ + char *bare; + if (!muc || !jid_table) + { + return NULL; + } + pthread_mutex_lock(&lock); + bare = StrDuplicate(HashMapGet(jid_table, muc)); + pthread_mutex_unlock(&lock); + + if (!bare) + { + bare = StrDuplicate(muc); + } + return bare; +} +void +ParseeDestroyJIDTable(void) +{ + char *key; + void *val; + if (!jid_table) + { + return; + } + pthread_mutex_lock(&lock); + while (HashMapIterate(jid_table, &key, &val)) + { + Free(val); + } + HashMapFree(jid_table); + jid_table = NULL; + pthread_mutex_unlock(&lock); + pthread_mutex_destroy(&lock); +} + diff --git a/src/Parsee/Tables/NickTable.c b/src/Parsee/Tables/NickTable.c new file mode 100644 index 0000000..a9bb601 --- /dev/null +++ b/src/Parsee/Tables/NickTable.c @@ -0,0 +1,105 @@ +#include + +#include +#include +#include +#include +#include + +static pthread_mutex_t nick_lock; +static HashMap *nick_table = NULL; + +static char * +GenerateKey(char *muc, char *mxid) +{ + unsigned char *shaDigest; + + char *concatStr; + char *hexDigest; + if (!muc || !mxid) + { + return NULL; + } + + concatStr = StrConcat(3, muc, ":", mxid); + shaDigest = Sha256(concatStr); + hexDigest = ShaToHex(shaDigest); + + Free (shaDigest); + Free (concatStr); + return hexDigest; +} + +void +ParseeInitialiseNickTable(void) +{ + if (nick_table) + { + return; + } + pthread_mutex_init(&nick_lock, NULL); + pthread_mutex_lock(&nick_lock); + nick_table = HashMapCreate(); + pthread_mutex_unlock(&nick_lock); +} +void +ParseePushNickTable(char *muc, char *mxid, char *nick) +{ + char *key; + if (!muc || !mxid || !nick_table) + { + return; + } + pthread_mutex_lock(&nick_lock); + + key = GenerateKey(muc, mxid); + nick = StrDuplicate(nick); + if (nick) + { + Free(HashMapSet(nick_table, key, nick)); + } + else + { + Free(HashMapDelete(nick_table, key)); + } + Free(key); + + pthread_mutex_unlock(&nick_lock); +} +char * +ParseeLookupNick(char *muc, char *mxid) +{ + char *ret, *key; + if (!muc || !nick_table) + { + return NULL; + } + pthread_mutex_lock(&nick_lock); + + key = GenerateKey(muc, mxid); + ret = HashMapGet(nick_table, key); + Free(key); + + pthread_mutex_unlock(&nick_lock); + return ret; +} +void +ParseeDestroyNickTable(void) +{ + char *key; + void *val; + if (!nick_table) + { + return; + } + pthread_mutex_lock(&nick_lock); + while (HashMapIterate(nick_table, &key, &val)) + { + Free(val); + } + HashMapFree(nick_table); + nick_table = NULL; + pthread_mutex_unlock(&nick_lock); + pthread_mutex_destroy(&nick_lock); +} + diff --git a/src/Parsee/Tables/OIDTable.c b/src/Parsee/Tables/OIDTable.c new file mode 100644 index 0000000..29569c8 --- /dev/null +++ b/src/Parsee/Tables/OIDTable.c @@ -0,0 +1,68 @@ +#include + +#include +#include +#include +#include + +static pthread_mutex_t oid_lock; +static HashMap *oid_table = NULL; + +void +ParseeInitialiseOIDTable(void) +{ + if (oid_table) + { + return; + } + pthread_mutex_init(&oid_lock, NULL); + pthread_mutex_lock(&oid_lock); + oid_table = HashMapCreate(); + pthread_mutex_unlock(&oid_lock); +} +void +ParseePushOIDTable(char *muc, char *bare) +{ + if (!muc || !bare || !oid_table) + { + return; + } + pthread_mutex_lock(&oid_lock); + bare = StrDuplicate(bare); + Free(HashMapSet(oid_table, muc, bare)); + pthread_mutex_unlock(&oid_lock); +} +char * +ParseeLookupOID(char *muc) +{ + char *bare; + if (!muc || !oid_table) + { + return NULL; + } + pthread_mutex_lock(&oid_lock); + bare = StrDuplicate(HashMapGet(oid_table, muc)); + pthread_mutex_unlock(&oid_lock); + + return bare; +} +void +ParseeDestroyOIDTable(void) +{ + char *key; + void *val; + if (!oid_table) + { + return; + } + pthread_mutex_lock(&oid_lock); + while (HashMapIterate(oid_table, &key, &val)) + { + Free(val); + } + HashMapFree(oid_table); + oid_table = NULL; + pthread_mutex_unlock(&oid_lock); + pthread_mutex_destroy(&oid_lock); +} + diff --git a/src/include/Parsee.h b/src/include/Parsee.h index a7c6fe4..4a0903e 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -239,6 +239,11 @@ extern void ParseePushHeadTable(char *room, char *id); extern char *ParseeLookupHead(char *room); extern void ParseeDestroyHeadTable(void); +extern void ParseeInitialiseNickTable(void); +extern void ParseePushNickTable(char *muc, char *mxid, char *nick); +extern char *ParseeLookupNick(char *muc, char *mxid); +extern void ParseeDestroyNickTable(void); + /** Disables a user/room/MUC's ability to interact from Parsee, and attempts * to ban them from rooms where Parsee has the ability to do so ("noflying"). * --------------- From 1f747dd0160038eba8f729a3a2a80569eb759665 Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 19 Aug 2024 00:24:00 +0200 Subject: [PATCH 006/220] [FIX] Fix potential race condition --- src/XMPPThread/ReListener.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/XMPPThread/ReListener.c b/src/XMPPThread/ReListener.c index 62ff10b..04cae69 100644 --- a/src/XMPPThread/ReListener.c +++ b/src/XMPPThread/ReListener.c @@ -331,6 +331,7 @@ ParseeAwaitStanza(char *identifier, int64_t timeout) { /* Timeout detected, give up regardless of the status of our * search. */ + pthread_mutex_lock(&await_lock); HashMapDelete(await_table, identifier); pthread_mutex_unlock(&await_lock); break; @@ -344,7 +345,9 @@ ParseeAwaitStanza(char *identifier, int64_t timeout) stanza = awa.stanza; pthread_mutex_unlock(&awa.cond_lock); + pthread_mutex_lock(&await_lock); pthread_cond_destroy(&awa.condition); pthread_mutex_destroy(&awa.cond_lock); + pthread_mutex_unlock(&await_lock); return stanza; } From 4b2ede6a66a84b08701756f84c5864cba18dd864 Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 19 Aug 2024 11:40:29 +0200 Subject: [PATCH 007/220] [ADD] Help screen --- src/Main.c | 31 +++++++++++++++++++++++++++- src/Parsee/Data.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/include/Parsee.h | 23 +++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/Main.c b/src/Main.c index 1b78a24..770a2a6 100644 --- a/src/Main.c +++ b/src/Main.c @@ -27,6 +27,27 @@ ParseeUptime(void) return UtilTsMillis() - start; } +static const Argument arguments[] = +{ +#define Argument(c, req, vdesc, desc) \ + { \ + .end = false, \ + .argument = c, .value_req = req, \ + .value_descr = vdesc, \ + .description = desc \ + }, + + Argument('H', true, "number", "Sets the number of HTTP threads") + Argument('J', true, "number", "Sets the number of XMPP threads") + + Argument('g',false,NULL,"Generates a parsee.yaml AS file before exiting") + Argument('v',false,NULL,"Forces Parsee to print in a more verbose fashion") + Argument('h',false,NULL,"Generates an help screen(this one!)") + + { .end = true } +#undef Argument +}; + int Main(Array *args, HashMap *env) { @@ -51,6 +72,7 @@ Main(Array *args, HashMap *env) { ArgParseState state; + char *opts = ParseeGenerateGetopt(arguments); int flag; int xmpp = 8; int http = 8; @@ -58,10 +80,14 @@ Main(Array *args, HashMap *env) ArgParseStateInit(&state); /* TODO: Have a smarter way of generating the arg table * (with a list of structs, with a description and everything) */ - while ((flag = ArgParse(&state, args, "vgH:J:")) != -1) + while ((flag = ArgParse(&state, args, opts)) != -1) { switch (flag) { + case 'h': + ParseeGenerateHelp(arguments); + Free(opts); + goto end; case 'H': http = strtol(state.optArg, NULL, 10); break; @@ -74,15 +100,18 @@ Main(Array *args, HashMap *env) yaml = StreamOpen("parsee.yaml", "w"); ParseeExportConfigYAML(yaml); StreamClose(yaml); + Free(opts); goto end; case 'v': LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG); break; case '?': Log(LOG_ERR, "INVALID ARGUMENT GIVEN"); + Free(opts); goto end; } } + Free(opts); ParseeSetThreads(xmpp, http); } diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index d13499d..9b891ee 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -875,3 +875,52 @@ ParseeGenerateMTO(char *common_id) return matrix_to; } + + +void +ParseeGenerateHelp(const Argument *list) +{ + if (!list) + { + return; + } + + while (!list->end) + { + char *str = list->value_req ? + StrConcat(3, " [", list->value_descr, "]") : + StrDuplicate(""); + Log(LOG_INFO, "-%c%s", list->argument, str); + LogConfigIndent(LogConfigGlobal()); + Log(LOG_INFO, "%s", list->description); + LogConfigUnindent(LogConfigGlobal()); + list++; + + Free(str); + } + return; +} +char * +ParseeGenerateGetopt(const Argument *list) +{ + char *ret = NULL, *tmp = NULL; + if (!list) + { + return NULL; + } + + while (!list->end) + { + char buffer[3] = { + list->argument, list->value_req ? ':' : '\0', + '\0' + }; + + tmp = ret; + ret = StrConcat(2, ret, buffer); + Free(tmp); + + list++; + } + return ret; +} diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 4a0903e..030e748 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -58,6 +58,16 @@ typedef struct ParseeData { char *id; } ParseeData; +typedef struct Argument { + bool end; + + char argument; + bool value_req; + + const char *value_descr; + const char *description; +} Argument; + #define GrabString(obj, ...) JsonValueAsString(JsonGet(obj, __VA_ARGS__)) #define GrabInteger(obj, ...) JsonValueAsInteger(JsonGet(obj, __VA_ARGS__)) #define GrabBoolean(obj, ...) JsonValueAsBoolean(JsonGet(obj, __VA_ARGS__)) @@ -76,6 +86,19 @@ typedef struct ParseeData { #define MB * 1024 KB #define GB * 1024 MB +/** Generates a valid, getopt-style argument list from a end-terminated + * argument list. + * ------------ + * Returns: a valid string to be used in getopt and the likes[HEAP] + * Modifies: NOTHING */ +extern char * ParseeGenerateGetopt(const Argument *list); + +/** Generates a help screen to the logger. + * ------------ + * Returns: NOTHING + * Modifies: NOTHING */ +extern void ParseeGenerateHelp(const Argument *list); + /* Initialises a Parsee config from scratch, and writes to it * as JSON in the CWD. */ extern void ParseeConfigInit(void); From 6cd3df21db0c5167fe8bfe3168e511cc0e7a1ed8 Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 19 Aug 2024 14:51:34 +0200 Subject: [PATCH 008/220] [ADD] -O3, -C [config] flag --- Makefile | 4 ++-- src/Main.c | 42 +++++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 733bd71..bfff826 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,8 @@ AYAS=ayaya ETC=etc INCLUDES=src/include CC=cc -CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -g -ggdb -Wall -Werror -LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -g -ggdb +CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -O3 -s -Wall -Werror +LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -O3 -s AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)" BINARY=parsee # ============================ Compilation ================================= diff --git a/src/Main.c b/src/Main.c index 770a2a6..543d548 100644 --- a/src/Main.c +++ b/src/Main.c @@ -29,22 +29,26 @@ ParseeUptime(void) static const Argument arguments[] = { -#define Argument(c, req, vdesc, desc) \ +#define Arg(c, req, vdesc, desc) \ { \ .end = false, \ .argument = c, .value_req = req, \ .value_descr = vdesc, \ .description = desc \ }, +#define EndOfArgs { .end = true } - Argument('H', true, "number", "Sets the number of HTTP threads") - Argument('J', true, "number", "Sets the number of XMPP threads") + Arg('H', true, "number(=8)", "Sets the number of HTTP threads") + Arg('J', true, "number(=8)", "Sets the number of XMPP threads") + Arg('C', true, "file(='parsee.json')", "Sets the JSON config to use") - Argument('g',false,NULL,"Generates a parsee.yaml AS file before exiting") - Argument('v',false,NULL,"Forces Parsee to print in a more verbose fashion") - Argument('h',false,NULL,"Generates an help screen(this one!)") + Arg('g', false, NULL,"Generates a parsee.yaml AS file before exiting") + Arg('v', false, NULL,"Forces Parsee to print in a more verbose fashion") + Arg('h', false, NULL,"Generates an help screen(this one!)") - { .end = true } + EndOfArgs + +#undef EndOfArgs #undef Argument }; @@ -56,6 +60,10 @@ Main(Array *args, HashMap *env) Stream *yaml; Cron *cron = NULL; + char *configuration = "parsee.json"; + int xmpp = 8; + int http = 8; + start = UtilTsMillis(); memset(&conf, 0, sizeof(conf)); @@ -66,16 +74,10 @@ Main(Array *args, HashMap *env) Log(LOG_INFO, "======================="); LogConfigIndent(LogConfigGlobal()); - ParseeConfigLoad("parsee.json"); - ParseeConfigInit(); - parsee_conf = ParseeConfigGet(); - { ArgParseState state; char *opts = ParseeGenerateGetopt(arguments); int flag; - int xmpp = 8; - int http = 8; ArgParseStateInit(&state); /* TODO: Have a smarter way of generating the arg table @@ -105,6 +107,15 @@ Main(Array *args, HashMap *env) case 'v': LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG); break; + case 'C': + if (!UtilLastModified(state.optArg)) + { + Log(LOG_ERR, "Invalid config: %s", state.optArg); + Log(LOG_ERR, "Ignoring."); + break; + } + configuration = state.optArg; + break; case '?': Log(LOG_ERR, "INVALID ARGUMENT GIVEN"); Free(opts); @@ -115,6 +126,11 @@ Main(Array *args, HashMap *env) ParseeSetThreads(xmpp, http); } + ParseeConfigLoad(configuration); + ParseeConfigInit(); + parsee_conf = ParseeConfigGet(); + + Log(LOG_NOTICE, "Connecting to XMPP..."); jabber = XMPPInitialiseCompStream( parsee_conf->component_host, From 4c158ea18607efded85f036472905c7114600a8e Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 19 Aug 2024 16:40:30 +0200 Subject: [PATCH 009/220] [MOD] Use specific SHA1/256 abstraction functions --- src/MatrixEventHandler.c | 4 +-- src/Parsee/SHA.c | 57 ++++++++++++++++++++++++++++++++ src/Parsee/Tables/NickTable.c | 6 +--- src/Parsee/User.c | 6 ++-- src/XMPP/Component.c | 6 ++-- src/XMPPThread/Bridged.c | 5 +-- src/XMPPThread/Stanzas/Message.c | 2 +- src/include/Parsee.h | 14 ++++++++ src/include/XMPPCommands.x.h | 2 +- 9 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 src/Parsee/SHA.c diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index d688c3c..6ead22a 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -25,8 +25,7 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) { char *nonce_str = StrInt(nonce); char *input = StrConcat(4, sender, name, data->id, nonce_str); - unsigned char *digest = Sha256(input); - char *hex = ShaToHex(digest); + char *hex = ParseeSHA256(input); if (strlen(hex) >= 8) { @@ -41,7 +40,6 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) nonce++; Free(nonce_str); - Free(digest); Free(input); Free(hex); } diff --git a/src/Parsee/SHA.c b/src/Parsee/SHA.c new file mode 100644 index 0000000..9cd4484 --- /dev/null +++ b/src/Parsee/SHA.c @@ -0,0 +1,57 @@ +#include + +#include +#include + +static char * +ToHex(unsigned char *input, size_t length) +{ + char *hex = Malloc(length * 2 + 1); + size_t i; + + for (i = 0; i < length; i++) + { + const char *table = + "0123456789abcdef"; + unsigned char byte = input[i]; + hex[2 * i] = table[(byte >> 4) & 0xF]; + hex[2*i+1] = table[(byte >> 0) & 0xF]; + } + + hex[length * 2] = '\0'; + + return hex; +} + +char * +ParseeSHA256(char *string) +{ + unsigned char *sha; + char *returnString; + if (!string) + { + return NULL; + } + + sha = Sha256(string); + returnString = ToHex(sha, 32); + Free(sha); + + return returnString; +} +char * +ParseeSHA1(char *string) +{ + unsigned char *sha; + char *returnString; + if (!string) + { + return NULL; + } + + sha = Sha1(string); + returnString = ToHex(sha, 20); + Free(sha); + + return returnString; +} diff --git a/src/Parsee/Tables/NickTable.c b/src/Parsee/Tables/NickTable.c index a9bb601..d8e7141 100644 --- a/src/Parsee/Tables/NickTable.c +++ b/src/Parsee/Tables/NickTable.c @@ -12,8 +12,6 @@ static HashMap *nick_table = NULL; static char * GenerateKey(char *muc, char *mxid) { - unsigned char *shaDigest; - char *concatStr; char *hexDigest; if (!muc || !mxid) @@ -22,10 +20,8 @@ GenerateKey(char *muc, char *mxid) } concatStr = StrConcat(3, muc, ":", mxid); - shaDigest = Sha256(concatStr); - hexDigest = ShaToHex(shaDigest); + hexDigest = ParseeSHA256(concatStr); - Free (shaDigest); Free (concatStr); return hexDigest; } diff --git a/src/Parsee/User.c b/src/Parsee/User.c index 096530d..ad7ae03 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -295,19 +295,17 @@ char * ParseeGetDMID(char *mxid, char *jid) { char *concat, *sha; - unsigned char *raw; if (!mxid || !jid) { return NULL; } + /* We really don't care about safety here. */ jid = ParseeTrimJID(jid); concat = StrConcat(2, mxid, jid); - raw = Sha1(concat); - sha = ShaToHex(raw); + sha = ParseeSHA1(concat); Free(concat); - Free(raw); Free(jid); return sha; diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index aa518cd..0eee988 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -11,6 +11,7 @@ #include #include +#include #include /* The default component port Prosody uses. */ @@ -81,14 +82,11 @@ static char * ComputeHandshake(char *shared, char *stream) { char *source; - unsigned char *raw_sha; char *sha; source = StrConcat(2, stream, shared); - raw_sha = Sha1(source); - sha = ShaToHex(raw_sha); + sha = ParseeSHA1(source); - Free(raw_sha); Free(source); return sha; diff --git a/src/XMPPThread/Bridged.c b/src/XMPPThread/Bridged.c index 4afab8e..806c979 100644 --- a/src/XMPPThread/Bridged.c +++ b/src/XMPPThread/Bridged.c @@ -166,7 +166,6 @@ ServerHasXEP421(ParseeData *data, char *from) static char * ScrambleOID(ParseeData *data, char *opaque_oid) { - unsigned char *raw; char *sha, *mxid; const ParseeConfig *c = data->config; if (!opaque_oid) @@ -175,9 +174,7 @@ ScrambleOID(ParseeData *data, char *opaque_oid) } /* Turns this into a 128-byte long, Matrix-safe value. */ - raw = Sha256(opaque_oid); - sha = ShaToHex(raw); - Free(raw); + sha = ParseeSHA256(opaque_oid); /* TODO: Either mark this specially, or drop Parsee JID flags * altogether before any 1.0.0 */ diff --git a/src/XMPPThread/Stanzas/Message.c b/src/XMPPThread/Stanzas/Message.c index 5d33297..3c6949c 100644 --- a/src/XMPPThread/Stanzas/Message.c +++ b/src/XMPPThread/Stanzas/Message.c @@ -165,7 +165,7 @@ end_error: room = ParseeFindDMRoom(args, to, from); data = body ? ArrayGet(body->children, 0) : NULL; - /* TODO: CLEAN THAT UP */ + /* TODO: CLEAN THAT UP INTO A CREATEDM FUNCTION */ mroom_id = ParseeGetBridgedRoom(args, stanza); if (!mroom_id && !room && !XMPPIsParseeStanza(stanza) && to && *to == '@') diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 030e748..5af946a 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -99,6 +99,20 @@ extern char * ParseeGenerateGetopt(const Argument *list); * Modifies: NOTHING */ extern void ParseeGenerateHelp(const Argument *list); +/** Generates a hexadecimal version of the SHA-256 digest of the + * original string. + * ---------------- + * Returns: a hexadecimal string[HEAP] + * Modifies: NOTHING */ +extern char * ParseeSHA256(char *string); + +/** Generates a hexadecimal version of the SHA-1 digest of the + * original string. + * ---------------- + * Returns: a hexadecimal string[HEAP] + * Modifies: NOTHING */ +extern char * ParseeSHA1(char *string); + /* Initialises a Parsee config from scratch, and writes to it * as JSON in the CWD. */ extern void ParseeConfigInit(void); diff --git a/src/include/XMPPCommands.x.h b/src/include/XMPPCommands.x.h index 688b0ca..d305340 100644 --- a/src/include/XMPPCommands.x.h +++ b/src/include/XMPPCommands.x.h @@ -12,7 +12,7 @@ XMPPSetFormTitle(cmd, "Admin addition form"); \ XMPPSetFormInstruction(cmd, "Select a glob pattern to add as an admin"); \ }) \ - XMPP_COMMAND(DelAdminCallback, "del-admin", "Removes a glob from admin rights", { \ + XMPP_COMMAND(DelAdminCallback, "del-admin", "Removes glob from being admin", { \ XMPPCmdOptionsCreator(cmd, FormDelAdminCallback); \ XMPPSetFormTitle(cmd, "Admin removal form"); \ XMPPSetFormInstruction(cmd, "Select a glob pattern to remove as an admin"); \ From 1b62072a3a108aa9caddf663b90dd021ab7f4cb6 Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 19 Aug 2024 20:25:00 +0200 Subject: [PATCH 010/220] [MOD] Start separating Parsee functions more --- src/MatrixEventHandler.c | 1 - src/Parsee/Data.c | 425 --------------------------------- src/Parsee/Tables/NickTable.c | 1 - src/Parsee/User.c | 1 - src/Parsee/Utils/Achievement.c | 26 ++ src/Parsee/Utils/Arguments.c | 53 ++++ src/Parsee/Utils/Formatting.c | 237 ++++++++++++++++++ src/Parsee/Utils/Nofly.c | 75 ++++++ src/Parsee/Utils/String.c | 77 ++++++ src/XMPP/Component.c | 1 - src/XMPPThread/Bridged.c | 1 - src/XMPPThread/ReListener.c | 1 - 12 files changed, 468 insertions(+), 431 deletions(-) create mode 100644 src/Parsee/Utils/Achievement.c create mode 100644 src/Parsee/Utils/Arguments.c create mode 100644 src/Parsee/Utils/Formatting.c create mode 100644 src/Parsee/Utils/Nofly.c create mode 100644 src/Parsee/Utils/String.c diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 6ead22a..41c229e 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index 9b891ee..5a3257c 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -6,11 +6,9 @@ #include #include -#include #include #include -#include #include ParseeData * @@ -206,244 +204,7 @@ ParseeCleanup(void *datp) DbListFree(chats); Log(LOG_DEBUG, "Cleant up %d entries...", entries); } -int -ParseeFindDatastart(char *data) -{ - char *startline; - bool found = false; - if (!data) - { - return 0; - } - startline = data; - while (startline) - { - char *endline = strchr(startline, '\n'); - - if (*startline != '>') - { - found = true; - break; - } - - startline = endline ? endline + 1 : NULL; - } - - if (!found) - { - return 0; - } - - return (int) (startline - data); -} - -#include -typedef struct XMPPFlags { - bool quote; -} XMPPFlags; -static char * -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) do \ - { \ - size_t cidx; \ - size_t len = strp ? strlen(strp) : 0; \ - for (cidx = 0; cidx < len; 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: - Concat(elem->data); - break; - case XML_ELEMENT_TAG: - if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong")) - { - Concat("*"); - for (i = 0; i < ArraySize(elem->children); i++) - { - child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); - - Concat(subxep); - Free(subxep); - } - Concat("*"); - } - else if (StrEquals(elem->name, "em")) - { - Concat("_"); - for (i = 0; i < ArraySize(elem->children); i++) - { - child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); - - Concat(subxep); - Free(subxep); - } - Concat("_"); - } - else if (StrEquals(elem->name, "code")) - { - Concat("`"); - for (i = 0; i < ArraySize(elem->children); i++) - { - child = ArrayGet(elem->children, i); - 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(event, child, flags); - - Concat(subxep); - Free(subxep); - } - flags.quote = false; - Concat("\n"); - } - else if (StrEquals(elem->name, "br")) - { - Concat("\n"); - /* HTML fucking SUCKS */ - for (i = 0; i < ArraySize(elem->children); i++) - { - child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); - - Concat(subxep); - Free(subxep); - } - Concat("\n"); - } - else if (StrEquals(elem->name, "a")) - { - char *href = HashMapGet(elem->attrs, "href"); - Concat("("); - for (i = 0; i < ArraySize(elem->children); i++) - { - child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); - - Concat(subxep); - Free(subxep); - } - Concat(" points to "); - Concat(href); - Concat(" )"); - } - else - { - for (i = 0; i < ArraySize(elem->children); i++) - { - child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); - - Concat(subxep); - Free(subxep); - } - } - break; - default: - break; - } - return xepd; -} -char * -ParseeXMPPify(HashMap *event) -{ - char *type, *format, *html; - char *xepd = NULL; - XMLElement *elem; - - XMPPFlags flags; - if (!event) - { - return NULL; - } - - /* Check if it is a message event. */ - type = JsonValueAsString(HashMapGet(event, "type")); - if (!StrEquals(type, "m.room.message")) - { - return NULL; - } - - format = JsonValueAsString(JsonGet(event, 2, "content", "format")); - if (!StrEquals(format, "org.matrix.custom.html")) - { - /* Settle for the raw body instead. */ - char *body = JsonValueAsString(JsonGet(event, 2, "content", "body")); - return StrDuplicate(body); - } - - html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body")); - html = StrConcat(3, "", html, ""); - elem = XMLCDecode(StrStreamReader(html), true, true); - - flags.quote = false; - xepd = XMPPifyElement(event, elem, flags); - - XMLFreeElement(elem); - Free(html); - - return xepd; -} void ParseePushDMStanza(ParseeData *data, char *room_id, char *stanza_id, char *id, char *ev, char *sender) { @@ -738,189 +499,3 @@ end: return ret; } -void -ParseeGlobalBan(ParseeData *data, char *glob, char *reason) -{ - DbRef *ref; - HashMap *j, *obj; - if (!data || !glob) - { - return; - } - - ref = DbLock(data->db, 1, "global_bans"); - if (!ref) - { - ref = DbCreate(data->db, 1, "global_bans"); - } - - j = DbJson(ref); - - obj = HashMapCreate(); - if (reason) - { - HashMapSet(obj, "reason", JsonValueString(reason)); - } - HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis())); - JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj))); - - DbUnlock(data->db, ref); -} -bool -ParseeManageBan(ParseeData *data, char *user, char *room) -{ - DbRef *ref; - HashMap *j; - char *key; - JsonValue *val; - bool banned = false , matches = false; - if (!data || !user) - { - return false; - } - - ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "global_bans"); - j = DbJson(ref); - while (HashMapIterate(j, &key, (void **) &val)) - { - HashMap *obj = JsonValueAsObject(val); - if (matches) - { - continue; - } - if (GlobMatches(key, user)) - { - banned = true; - matches = true; - if (room) - { - /* TODO: Use the object to set the reason */ - ASBan(data->config, room, user); - (void) obj; - } - } - } - DbUnlock(data->db, ref); - - return banned; -} -char * -ParseeStringifyDate(uint64_t millis) -{ - uint64_t rest = millis; - uint64_t hours, minutes, seconds; - char *hs, *ms, *ss, *out; - - hours = rest / (1 HOURS); - rest = rest % (1 HOURS); - - minutes = rest / (1 MINUTES); - rest = rest % (1 MINUTES); - - seconds = rest / (1 SECONDS); - - hs = StrInt(hours); - ms = StrInt(minutes); - ss = StrInt(seconds); - - out = StrConcat(8, - hours ? hs : "", - hours ? " hours" : "", - (hours && minutes) ? ", " : "", - - minutes ? ms : "", - minutes ? " minutes" : "", - (minutes && seconds) ? ", " : "", - - seconds ? ss : "", - seconds ? " seconds" : "" - ); - Free(hs); - Free(ms); - Free(ss); - - return out; -} - -void -ParseeAchievement(const char *func, const char *msg, bool die) -{ - Log(LOG_ERR, "=========== Achievement GET! ==========="); - Log(LOG_ERR, "%s: %s.", func, msg); - Log(LOG_ERR, "THIS IS, LET'S SAY, NOT GOOD, AND A SIGN OF "); - Log(LOG_ERR, "A PROGRAMMER ERROR. PLEASE KILLALL -9 PARSEE."); - Log(LOG_ERR, ""); - Log(LOG_ERR, "YOU, HOWEVER, GET TO WIN AN AWARD FOR THIS."); - Log(LOG_ERR, "I AM SIMPLY JEALOUS OF YOU GETTING SUCH A "); - Log(LOG_ERR, "GOOD ERROR MESSAGE."); - Log(LOG_ERR, "=========== Achievement GET! ==========="); - - if (die) - { - abort(); - } -} -char * -ParseeGenerateMTO(char *common_id) -{ - char *matrix_to; - if (!common_id) - { - return NULL; - } - - common_id = HttpUrlEncode(common_id); - matrix_to = StrConcat(2, "https://matrix.to/#/", common_id); - Free(common_id); - - return matrix_to; -} - - -void -ParseeGenerateHelp(const Argument *list) -{ - if (!list) - { - return; - } - - while (!list->end) - { - char *str = list->value_req ? - StrConcat(3, " [", list->value_descr, "]") : - StrDuplicate(""); - Log(LOG_INFO, "-%c%s", list->argument, str); - LogConfigIndent(LogConfigGlobal()); - Log(LOG_INFO, "%s", list->description); - LogConfigUnindent(LogConfigGlobal()); - list++; - - Free(str); - } - return; -} -char * -ParseeGenerateGetopt(const Argument *list) -{ - char *ret = NULL, *tmp = NULL; - if (!list) - { - return NULL; - } - - while (!list->end) - { - char buffer[3] = { - list->argument, list->value_req ? ':' : '\0', - '\0' - }; - - tmp = ret; - ret = StrConcat(2, ret, buffer); - Free(tmp); - - list++; - } - return ret; -} diff --git a/src/Parsee/Tables/NickTable.c b/src/Parsee/Tables/NickTable.c index d8e7141..1778927 100644 --- a/src/Parsee/Tables/NickTable.c +++ b/src/Parsee/Tables/NickTable.c @@ -3,7 +3,6 @@ #include #include #include -#include #include static pthread_mutex_t nick_lock; diff --git a/src/Parsee/User.c b/src/Parsee/User.c index ad7ae03..9e2ee22 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/src/Parsee/Utils/Achievement.c b/src/Parsee/Utils/Achievement.c new file mode 100644 index 0000000..136f6a0 --- /dev/null +++ b/src/Parsee/Utils/Achievement.c @@ -0,0 +1,26 @@ +#include + +#include + +#include + +void +ParseeAchievement(const char *func, const char *msg, bool die) +{ + Log(LOG_ERR, "=========== Achievement GET! ==========="); + Log(LOG_ERR, "%s: %s.", func, msg); + Log(LOG_ERR, "THIS IS, LET'S SAY, NOT GOOD, AND A SIGN OF "); + Log(LOG_ERR, "A PROGRAMMER ERROR. PLEASE KILLALL -9 PARSEE."); + Log(LOG_ERR, ""); + Log(LOG_ERR, "YOU, HOWEVER, GET TO WIN AN AWARD FOR THIS."); + Log(LOG_ERR, "I AM SIMPLY JEALOUS OF YOU GETTING SUCH A "); + Log(LOG_ERR, "GOOD ERROR MESSAGE."); + Log(LOG_ERR, "=========== Achievement GET! ==========="); + + if (die) + { + abort(); + } +} + + diff --git a/src/Parsee/Utils/Arguments.c b/src/Parsee/Utils/Arguments.c new file mode 100644 index 0000000..9d4c897 --- /dev/null +++ b/src/Parsee/Utils/Arguments.c @@ -0,0 +1,53 @@ +#include + +#include +#include +#include + +void +ParseeGenerateHelp(const Argument *list) +{ + if (!list) + { + return; + } + + while (!list->end) + { + char *str = list->value_req ? + StrConcat(3, " [", list->value_descr, "]") : + StrDuplicate(""); + Log(LOG_INFO, "-%c%s", list->argument, str); + LogConfigIndent(LogConfigGlobal()); + Log(LOG_INFO, "%s", list->description); + LogConfigUnindent(LogConfigGlobal()); + list++; + + Free(str); + } + return; +} +char * +ParseeGenerateGetopt(const Argument *list) +{ + char *ret = NULL, *tmp = NULL; + if (!list) + { + return NULL; + } + + while (!list->end) + { + char buffer[3] = { + list->argument, list->value_req ? ':' : '\0', + '\0' + }; + + tmp = ret; + ret = StrConcat(2, ret, buffer); + Free(tmp); + + list++; + } + return ret; +} diff --git a/src/Parsee/Utils/Formatting.c b/src/Parsee/Utils/Formatting.c new file mode 100644 index 0000000..e538abc --- /dev/null +++ b/src/Parsee/Utils/Formatting.c @@ -0,0 +1,237 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +typedef struct XMPPFlags { + bool quote; +} XMPPFlags; +static char * +XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) +{ + char *xepd = NULL, *tmp = NULL; + + size_t i; + XMLElement *child; + char *reply_id = GrabString( + 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) do \ + { \ + size_t cidx; \ + size_t len = strp ? strlen(strp) : 0; \ + for (cidx = 0; cidx < len; 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: + Concat(elem->data); + break; + case XML_ELEMENT_TAG: + if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong")) + { + Concat("*"); + for (i = 0; i < ArraySize(elem->children); i++) + { + child = ArrayGet(elem->children, i); + subxep = XMPPifyElement(event, child, flags); + + Concat(subxep); + Free(subxep); + } + Concat("*"); + } + else if (StrEquals(elem->name, "em")) + { + Concat("_"); + for (i = 0; i < ArraySize(elem->children); i++) + { + child = ArrayGet(elem->children, i); + subxep = XMPPifyElement(event, child, flags); + + Concat(subxep); + Free(subxep); + } + Concat("_"); + } + else if (StrEquals(elem->name, "code")) + { + Concat("`"); + for (i = 0; i < ArraySize(elem->children); i++) + { + child = ArrayGet(elem->children, i); + 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(event, child, flags); + + Concat(subxep); + Free(subxep); + } + flags.quote = false; + Concat("\n"); + } + else if (StrEquals(elem->name, "br")) + { + Concat("\n"); + /* HTML fucking SUCKS */ + for (i = 0; i < ArraySize(elem->children); i++) + { + child = ArrayGet(elem->children, i); + subxep = XMPPifyElement(event, child, flags); + + Concat(subxep); + Free(subxep); + } + Concat("\n"); + } + else if (StrEquals(elem->name, "a")) + { + char *href = HashMapGet(elem->attrs, "href"); + Concat("("); + for (i = 0; i < ArraySize(elem->children); i++) + { + child = ArrayGet(elem->children, i); + subxep = XMPPifyElement(event, child, flags); + + Concat(subxep); + Free(subxep); + } + Concat(" points to "); + Concat(href); + Concat(" )"); + } + else + { + for (i = 0; i < ArraySize(elem->children); i++) + { + child = ArrayGet(elem->children, i); + subxep = XMPPifyElement(event, child, flags); + + Concat(subxep); + Free(subxep); + } + } + break; + default: + break; + } + return xepd; +} +char * +ParseeXMPPify(HashMap *event) +{ + char *type, *format, *html; + char *xepd = NULL; + XMLElement *elem; + + XMPPFlags flags; + if (!event) + { + return NULL; + } + + /* Check if it is a message event. */ + type = JsonValueAsString(HashMapGet(event, "type")); + if (!StrEquals(type, "m.room.message")) + { + return NULL; + } + + format = JsonValueAsString(JsonGet(event, 2, "content", "format")); + if (!StrEquals(format, "org.matrix.custom.html")) + { + /* Settle for the raw body instead. */ + char *body = JsonValueAsString(JsonGet(event, 2, "content", "body")); + return StrDuplicate(body); + } + + html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body")); + html = StrConcat(3, "", html, ""); + elem = XMLCDecode(StrStreamReader(html), true, true); + + flags.quote = false; + xepd = XMPPifyElement(event, elem, flags); + + XMLFreeElement(elem); + Free(html); + + return xepd; +} + +char * +ParseeGenerateMTO(char *common_id) +{ + char *matrix_to; + if (!common_id) + { + return NULL; + } + + common_id = HttpUrlEncode(common_id); + matrix_to = StrConcat(2, "https://matrix.to/#/", common_id); + Free(common_id); + + return matrix_to; +} diff --git a/src/Parsee/Utils/Nofly.c b/src/Parsee/Utils/Nofly.c new file mode 100644 index 0000000..8a82c0f --- /dev/null +++ b/src/Parsee/Utils/Nofly.c @@ -0,0 +1,75 @@ +#include + +#include +#include + +#include +#include + +void +ParseeGlobalBan(ParseeData *data, char *glob, char *reason) +{ + DbRef *ref; + HashMap *j, *obj; + if (!data || !glob) + { + return; + } + + ref = DbLock(data->db, 1, "global_bans"); + if (!ref) + { + ref = DbCreate(data->db, 1, "global_bans"); + } + + j = DbJson(ref); + + obj = HashMapCreate(); + if (reason) + { + HashMapSet(obj, "reason", JsonValueString(reason)); + } + HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis())); + JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj))); + + DbUnlock(data->db, ref); +} +bool +ParseeManageBan(ParseeData *data, char *user, char *room) +{ + DbRef *ref; + HashMap *j; + char *key; + JsonValue *val; + bool banned = false , matches = false; + if (!data || !user) + { + return false; + } + + ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "global_bans"); + j = DbJson(ref); + while (HashMapIterate(j, &key, (void **) &val)) + { + HashMap *obj = JsonValueAsObject(val); + if (matches) + { + continue; + } + if (GlobMatches(key, user)) + { + banned = true; + matches = true; + if (room) + { + /* TODO: Use the object to set the reason */ + ASBan(data->config, room, user); + (void) obj; + } + } + } + DbUnlock(data->db, ref); + + return banned; +} + diff --git a/src/Parsee/Utils/String.c b/src/Parsee/Utils/String.c new file mode 100644 index 0000000..89d6f4a --- /dev/null +++ b/src/Parsee/Utils/String.c @@ -0,0 +1,77 @@ +#include + +#include +#include + +#include +#include + +int +ParseeFindDatastart(char *data) +{ + char *startline; + bool found = false; + if (!data) + { + return 0; + } + + startline = data; + while (startline) + { + char *endline = strchr(startline, '\n'); + + if (*startline != '>') + { + found = true; + break; + } + + startline = endline ? endline + 1 : NULL; + } + + if (!found) + { + return 0; + } + + return (int) (startline - data); +} + +char * +ParseeStringifyDate(uint64_t millis) +{ + uint64_t rest = millis; + uint64_t hours, minutes, seconds; + char *hs, *ms, *ss, *out; + + hours = rest / (1 HOURS); + rest = rest % (1 HOURS); + + minutes = rest / (1 MINUTES); + rest = rest % (1 MINUTES); + + seconds = rest / (1 SECONDS); + + hs = StrInt(hours); + ms = StrInt(minutes); + ss = StrInt(seconds); + + out = StrConcat(8, + hours ? hs : "", + hours ? " hours" : "", + (hours && minutes) ? ", " : "", + + minutes ? ms : "", + minutes ? " minutes" : "", + (minutes && seconds) ? ", " : "", + + seconds ? ss : "", + seconds ? " seconds" : "" + ); + Free(hs); + Free(ms); + Free(ss); + + return out; +} diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index 0eee988..b43488b 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -77,7 +77,6 @@ XMPPInitialiseCompStream(char *host, int port) return comp; } -#include static char * ComputeHandshake(char *shared, char *stream) { diff --git a/src/XMPPThread/Bridged.c b/src/XMPPThread/Bridged.c index 806c979..69f9033 100644 --- a/src/XMPPThread/Bridged.c +++ b/src/XMPPThread/Bridged.c @@ -3,7 +3,6 @@ #include #include #include -#include #include diff --git a/src/XMPPThread/ReListener.c b/src/XMPPThread/ReListener.c index 04cae69..e66e1af 100644 --- a/src/XMPPThread/ReListener.c +++ b/src/XMPPThread/ReListener.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include From fb511b4df0ad1f2b6713228f6ef1490702b219be Mon Sep 17 00:00:00 2001 From: LDA Date: Wed, 21 Aug 2024 13:51:52 +0200 Subject: [PATCH 011/220] [MOD/ADD] Separate AS code, XMPP reactions removal --- Makefile | 4 +- src/AS.c | 971 ------------------------------- src/AS/Events.c | 93 +++ src/AS/Indicators.c | 119 ++++ src/AS/Media.c | 129 ++++ src/AS/Ping.c | 39 ++ src/AS/Profile.c | 138 +++++ src/AS/Register.c | 46 ++ src/AS/Relations.c | 81 +++ src/AS/Room.c | 352 +++++++++++ src/AS/Send.c | 48 ++ src/AS/State.c | 37 ++ src/XEP-0393.c | 7 + src/XMPPThread/ReListener.c | 2 - src/XMPPThread/Stanzas/Message.c | 128 ++-- src/include/AS.h | 18 +- 16 files changed, 1183 insertions(+), 1029 deletions(-) create mode 100644 src/AS/Events.c create mode 100644 src/AS/Indicators.c create mode 100644 src/AS/Media.c create mode 100644 src/AS/Ping.c create mode 100644 src/AS/Profile.c create mode 100644 src/AS/Register.c create mode 100644 src/AS/Relations.c create mode 100644 src/AS/Room.c create mode 100644 src/AS/Send.c create mode 100644 src/AS/State.c diff --git a/Makefile b/Makefile index bfff826..88cfb8d 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,8 @@ AYAS=ayaya ETC=etc INCLUDES=src/include CC=cc -CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -O3 -s -Wall -Werror -LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -O3 -s +CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -O3 -g -Wall -Werror +LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -O3 -g AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)" BINARY=parsee # ============================ Compilation ================================= diff --git a/src/AS.c b/src/AS.c index 81ce4a9..f9d1700 100644 --- a/src/AS.c +++ b/src/AS.c @@ -55,976 +55,5 @@ ASAuthenticateRequest(const ParseeConfig *data, HttpClientContext *ctx) HttpRequestHeader(ctx, "Authorization", bearer); Free(bearer); } -bool -ASRegisterUser(const ParseeConfig *conf, char *user) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - HttpStatus status; - if (!conf || !user) - { - return false; - } - /* Create user. We don't actually care about the value as we can - * masquerade, as long as it exists. */ - ctx = ParseeCreateRequest( - conf, - HTTP_POST, "/_matrix/client/v3/register" - ); - json = HashMapCreate(); - HashMapSet(json,"type",JsonValueString("m.login.application_service")); - - user = ParseeGetLocal(user); - HashMapSet(json,"username",JsonValueString(user)); - - ASAuthenticateRequest(conf, ctx); - status = ParseeSetRequestJSON(ctx, json); - - HttpClientContextFree(ctx); - JsonFree(json); - - Free(user); - - return status == HTTP_OK; -} - -void -ASPing(const ParseeConfig *conf) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path; - if (!conf) - { - return; - } - - path = StrConcat(3, - "/_matrix/client/v1/appservice/", - "Parsee%20XMPP", - "/ping" - ); - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - HttpClientContextFree(ctx); - JsonFree(json); -} -void -ASInvite(const ParseeConfig *conf, char *id, char *invited) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path, *bridge; - if (!conf || !id || !invited) - { - return; - } - - bridge = StrConcat(4, - "@", conf->sender_localpart, - ":", conf->server_base - ); - path = StrConcat(5, - "/_matrix/client/v3/rooms/", id, "/invite", - "?user_id=", bridge - ); - Free(bridge); - - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - HashMapSet(json, "user_id", JsonValueString(invited)); - HashMapSet(json, "reason", JsonValueString("Pass over.")); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - - HttpClientContextFree(ctx); - JsonFree(json); -} -void -ASBan(const ParseeConfig *conf, char *id, char *banned) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path, *bridge; - if (!conf || !id || !banned) - { - return; - } - - bridge = StrConcat(4, - "@", conf->sender_localpart, - ":", conf->server_base - ); - path = StrConcat(5, - "/_matrix/client/v3/rooms/", id, "/ban", - "?user_id=", bridge - ); - Free(bridge); - - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - HashMapSet(json, "user_id", JsonValueString(banned)); - HashMapSet(json, "reason", JsonValueString("Parsee felt jealous.")); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - - HttpClientContextFree(ctx); - JsonFree(json); -} -void -ASKick(const ParseeConfig *conf, char *id, char *banned) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path, *bridge; - if (!conf || !id || !banned) - { - return; - } - - bridge = StrConcat(4, - "@", conf->sender_localpart, - ":", conf->server_base - ); - path = StrConcat(5, - "/_matrix/client/v3/rooms/", id, "/kick", - "?user_id=", bridge - ); - Free(bridge); - - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - HashMapSet(json, "user_id", JsonValueString(banned)); - HashMapSet(json, "reason", JsonValueString("Parsee felt jealous.")); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - - HttpClientContextFree(ctx); - JsonFree(json); -} -char * -ASJoin(const ParseeConfig *conf, char *id, char *masquerade) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path, *ret; - if (!conf || !id) - { - return NULL; - } - - if (!masquerade) - { - char *raw = StrConcat(4, - "@", conf->sender_localpart, - ":", conf->server_base - ); - masquerade = HttpUrlEncode(raw); - Free(raw); - } - else - { - masquerade = HttpUrlEncode(masquerade); - } - id = HttpUrlEncode(id); - path = StrConcat(5, - "/_matrix/client/v3/join/", id, "?", - "user_id=", masquerade - ); - - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - JsonFree(json); - - json = JsonDecode(HttpClientStream(ctx)); - ret = StrDuplicate(GrabString(json, 1, "room_id")); - JsonFree(json); - - HttpClientContextFree(ctx); - Free(masquerade); - Free(id); - - return ret; -} -void -ASLeave(const ParseeConfig *conf, char *id, char *masquerade) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path; - if (!conf || !id) - { - return; - } - - if (!masquerade) - { - char *raw = StrConcat(4, - "@", conf->sender_localpart, - ":", conf->server_base - ); - masquerade = HttpUrlEncode(raw); - Free(raw); - } - else - { - masquerade = HttpUrlEncode(masquerade); - } - id = HttpUrlEncode(id); - path = StrConcat(5, - "/_matrix/client/v3/rooms/", id, "/leave?", - "user_id=", masquerade - ); - - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - JsonFree(json); - - HttpClientContextFree(ctx); - Free(masquerade); - Free(id); -} -void -ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state) -{ - HttpClientContext *ctx = NULL; - char *path; - if (!conf || !id || !type || !mask || !state) - { - JsonFree(state); - return; - } - - path = StrConcat(9, - "/_matrix/client/v3/rooms/", id, "/state/", - type, "/", key, "?", "user_id=", mask - ); - - ctx = ParseeCreateRequest(conf, HTTP_PUT, path); - Free(path); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, state); - - HttpClientContextFree(ctx); - JsonFree(state); -} -char * -ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c) -{ - HttpClientContext *ctx = NULL; - char *path; - char *txn, *ret; - HashMap *reply; - if (!conf || !id || !type || !user || !c) - { - JsonFree(c); - return NULL; - } - - txn = StrRandom(16); - path = StrConcat(9, - "/_matrix/client/v3/rooms/", - id, "/send/", type, "/", txn, "?", - "user_id=", user - ); - Free(txn); - - ctx = ParseeCreateRequest(conf, HTTP_PUT, path); - Free(path); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, c); - - reply = JsonDecode(HttpClientStream(ctx)); - ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id"))); - JsonFree(reply); - - HttpClientContextFree(ctx); - JsonFree(c); - - return ret; -} -char * -ASCreateRoom(const ParseeConfig *conf, char *by, char *alias) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path, *id; - if (!conf || !by) - { - return NULL; - } - - path = StrConcat(3, - "/_matrix/client/v3/createRoom", - "?user_id=", by - ); - - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - if (alias) - { - char *trimmed = StrDuplicate(alias); - if (*alias == '#') - { - char *tmp, cb[2] = { 0 }; - alias++; - Free(trimmed); - trimmed = NULL; - - while (*alias && *alias != ':') - { - cb[0] = *alias; - tmp = trimmed; - trimmed = StrConcat(2, trimmed, cb); - Free(tmp); - alias ++; - } - } - HashMapSet(json, "room_alias_name", JsonValueString(trimmed)); - Free(trimmed); - } - HashMapSet(json, "visibility", JsonValueString("public")); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - - JsonFree(json); - json = JsonDecode(HttpClientStream(ctx)); - id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id"))); - HttpClientContextFree(ctx); - JsonFree(json); - - return id; -} -char * -ASCreateDM(const ParseeConfig *conf, char *by, char *with) -{ - HttpClientContext *ctx = NULL; - HashMap *json = NULL; - char *path, *id; - if (!conf || !by || !with) - { - return NULL; - } - - path = StrConcat(3, - "/_matrix/client/v3/createRoom", - "?user_id=", by - ); - - ctx = ParseeCreateRequest( - conf, - HTTP_POST, path - ); - Free(path); - json = HashMapCreate(); - { - Array *invitees = ArrayCreate(); - - ArrayAdd(invitees, JsonValueString(with)); - HashMapSet(json, "invite", JsonValueArray(invitees)); - HashMapSet(json, "is_direct", JsonValueBoolean(true)); - } - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - - JsonFree(json); - json = JsonDecode(HttpClientStream(ctx)); - id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id"))); - HttpClientContextFree(ctx); - JsonFree(json); - - return id; -} -void -ASSetAvatar(const ParseeConfig *conf, char *user, char *mxc) -{ - HttpClientContext *ctx = NULL; - HashMap *json; - char *path; - if (!conf || !user || !mxc) - { - return; - } - - user = HttpUrlEncode(user); - path = StrConcat(6, - "/_matrix/client/v3/profile/", - user, "/avatar_url", "?", - "user_id=", user - ); - - json = HashMapCreate(); - HashMapSet(json, "avatar_url", JsonValueString(mxc)); - ctx = ParseeCreateRequest(conf, HTTP_PUT, path); - Free(path); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - - HttpClientContextFree(ctx); - JsonFree(json); - Free(user); -} -void -ASSetName(const ParseeConfig *conf, char *user, char *name) -{ - HttpClientContext *ctx = NULL; - HashMap *json; - char *path; - if (!conf || !user || !name) - { - return; - } - - user = HttpUrlEncode(user); - path = StrConcat(6, - "/_matrix/client/v3/profile/", - user, "/displayname", "?", - "user_id=", user - ); - - json = HashMapCreate(); - HashMapSet(json, "displayname", JsonValueString(name)); - ctx = ParseeCreateRequest(conf, HTTP_PUT, path); - Free(path); - ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); - - HttpClientContextFree(ctx); - 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->server_base); - 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) -{ - HttpClientContext *ctx; - HashMap *reply; - char *path, *ret; - char *u2 = user; - if (!c || !user) - { - return NULL; - } - - if (!room) - { - user = HttpUrlEncode(user); - path = StrConcat(3, - "/_matrix/client/v3/profile/", user, "/displayname" - ); - ctx = ParseeCreateRequest(c, HTTP_GET, path); - Free(user); - ASAuthenticateRequest(c, ctx); - HttpRequestSendHeaders(ctx); - HttpRequestSend(ctx); - - reply = JsonDecode(HttpClientStream(ctx)); - - ret = StrDuplicate( - JsonValueAsString(HashMapGet(reply, "displayname")) - ); - HttpClientContextFree(ctx); - JsonFree(reply); - Free(path); - - if (!ret) - { - ret = StrDuplicate(u2); - } - return ret; - } - user = HttpUrlEncode(user); - room = HttpUrlEncode(room); - path = StrConcat(4, - "/_matrix/client/v3/rooms/", room, - "/state/m.room.member/", user - ); - ctx = ParseeCreateRequest(c, HTTP_GET, path); - Free(user); - Free(room); - ASAuthenticateRequest(c, ctx); - HttpRequestSendHeaders(ctx); - HttpRequestSend(ctx); - - reply = JsonDecode(HttpClientStream(ctx)); - - ret = StrDuplicate( - JsonValueAsString(HashMapGet(reply, "displayname")) - ); - HttpClientContextFree(ctx); - JsonFree(reply); - Free(path); - - if (!ret) - { - ret = StrDuplicate(u2); - } - return ret; -} -HashMap * -ASGetPL(const ParseeConfig *c, char *room) -{ - char *path; - HttpClientContext *ctx; - HashMap *reply; - if (!c || !room) - { - return NULL; - } - room = HttpUrlEncode(room); - path = StrConcat(4, - "/_matrix/client/v3/rooms/", room, - "/state/m.room.power_levels/", "" - ); - ctx = ParseeCreateRequest(c, HTTP_GET, path); - Free(room); - ASAuthenticateRequest(c, ctx); - HttpRequestSendHeaders(ctx); - HttpRequestSend(ctx); - - reply = JsonDecode(HttpClientStream(ctx)); - - HttpClientContextFree(ctx); - Free(path); - - return reply; -} -void -ASSetPL(const ParseeConfig *conf, char *id, HashMap *m) -{ - char *user; - if (!conf || !id || !m) - { - return; - } - user = StrConcat(4, - "@",conf->sender_localpart, - ":",conf->server_base - ); - ASSetState(conf, id, "m.room.power_levels", "", user, m); - Free(user); -} - -char * -ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime) -{ - char *size_str, *path, *ret, *user; - int i; - HttpClientContext *ctx; - HashMap *reply; - if (!c || !from) - { - return NULL; - } - - size_str = StrInt(size); - user = StrConcat(4, "@",c->sender_localpart,":",c->server_base); - path = StrConcat(2, - "/_matrix/media/v3/upload?user_id=", user - ); - ctx = ParseeCreateRequest(c, HTTP_POST, path); - ASAuthenticateRequest(c, ctx); - if (size) - { - HttpRequestHeader(ctx, "Content-Length", size_str); - } - if (mime) - { - HttpRequestHeader(ctx, "Content-Type", mime); - } - HttpRequestSendHeaders(ctx); - - for (i = 0; i < size; i++) - { - int ch = StreamGetc(from); - if (ch == EOF) - { - break; - } - StreamPutc(HttpClientStream(ctx), ch); - } - HttpRequestSend(ctx); - reply = JsonDecode(HttpClientStream(ctx)); - ret = StrDuplicate( - JsonValueAsString(HashMapGet(reply, "content_uri")) - ); - if (!ret) - { - JsonEncode(reply, StreamStdout(), JSON_PRETTY); - StreamFlush(StreamStdout()); - } - HttpClientContextFree(ctx); - JsonFree(reply); - Free(size_str); - Free(path); - Free(user); - - return ret; -} -char * -ASReupload(const ParseeConfig *c, char *from, char **mime) -{ - Uri *uri; - HttpClientContext *ctx; - unsigned short port; - int size = 0, flags = HTTP_FLAG_NONE; - char *ret, *content_len; - - if (!c || !from) - { - return NULL; - } - - uri = UriParse(from); - if (!uri) - { - return NULL; - } - if (uri->port) - { - port = uri->port; - } - else if (StrEquals(uri->proto, "https")) - { - port = 443; - } - else - { - port = 80; - } - - if (StrEquals(uri->proto, "https")) - { - flags |= HTTP_FLAG_TLS; - } - - ctx = HttpRequest( - HTTP_GET, flags, port, uri->host, uri->path - ); - HttpRequestSendHeaders(ctx); - HttpRequestSend(ctx); - - if (mime) - { - *mime = HashMapGet(HttpResponseHeaders(ctx), "content-type"); - *mime = StrDuplicate(*mime); - } - - content_len = HashMapGet(HttpResponseHeaders(ctx), "content-length"); - if (content_len) - { - size = strtol(content_len, NULL, 10); - } - ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL); - - HttpClientContextFree(ctx); - UriFree(uri); - return ret; -} -void -ASType(const ParseeConfig *c, char *user, char *room, bool status) -{ - HttpClientContext *ctx = NULL; - HashMap *json; - char *path; - if (!c || !user || !room) - { - return; - } - - user = HttpUrlEncode(user); - path = StrConcat(6, - "/_matrix/client/v3/rooms/", - room, "/typing/", user, - "?user_id=", user - ); - - json = HashMapCreate(); - HashMapSet(json, "typing", JsonValueBoolean(status)); - /* If someone types for 10 minutes straight, they got something weird man. */ - HashMapSet(json, "timeout", JsonValueBoolean(10 MINUTES)); - ctx = ParseeCreateRequest(c, HTTP_PUT, path); - Free(path); - ASAuthenticateRequest(c, ctx); - ParseeSetRequestJSON(ctx, json); - JsonFree(json); - - HttpClientContextFree(ctx); - 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) -{ - HttpClientContext *ctx = NULL; - HashMap *json; - char *path; - if (!c || !key) - { - return NULL; - } - - if (!user) - { - char *raw = StrConcat(4, - "@", c->sender_localpart, - ":", c->server_base - ); - user = HttpUrlEncode(raw); - Free(raw); - } - else - { - user = HttpUrlEncode(user); - } - path = StrConcat(7, - "/_matrix/client/v3/user/", - user, "/account_data/", key, "?", - "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; -} -void -ASSetUserConfig(const ParseeConfig *c, char *user, char *key, HashMap *map) -{ - HttpClientContext *ctx = NULL; - char *path; - if (!c || !key || !map) - { - JsonFree(map); - return; - } - - if (!user) - { - char *raw = StrConcat(4, - "@", c->sender_localpart, - ":", c->server_base - ); - user = HttpUrlEncode(raw); - Free(raw); - } - else - { - user = HttpUrlEncode(user); - } - - path = StrConcat(7, - "/_matrix/client/v3/user/", - user, "/account_data/", key, "?", - "user_id=", user - ); - - ctx = ParseeCreateRequest(c, HTTP_PUT, path); - Free(path); - ASAuthenticateRequest(c, ctx); - ParseeSetRequestJSON(ctx, map); - - HttpClientContextFree(ctx); - Free(user); - JsonFree(map); - - return; -} -void -ASRedact(const ParseeConfig *c, char *room, char *user, char *e_id) -{ - HttpClientContext *ctx = NULL; - HashMap *request; - char *path, *txn; - if (!c || !room || !e_id) - { - return; - } - - if (!user) - { - char *raw = StrConcat(4, - "@", c->sender_localpart, - ":", c->server_base - ); - user = HttpUrlEncode(raw); - Free(raw); - } - else - { - user = HttpUrlEncode(user); - } - room = HttpUrlEncode(room); - e_id = HttpUrlEncode(e_id); - txn = StrRandom(16); - - path = StrConcat(9, - "/_matrix/client/v3/rooms/", - room, "/redact/", e_id, "/", txn, - "?", "user_id=", user - ); - - request = HashMapCreate(); - ctx = ParseeCreateRequest(c, HTTP_PUT, path); - Free(path); - ASAuthenticateRequest(c, ctx); - ParseeSetRequestJSON(ctx, request); - JsonFree(request); - - HttpClientContextFree(ctx); - Free(user); - Free(room); - Free(e_id); - Free(txn); - - return; -} -void -ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg) -{ - HttpClientContext *ctx = NULL; - HashMap *request; - char *path; - char *status_str = NULL; - if (!c || !user) - { - return; - } - - switch (status) - { - case USER_STATUS_ONLINE: status_str = "online"; break; - case USER_STATUS_OFFLINE: status_str = "offline"; break; - case USER_STATUS_UNAVAILABLE: status_str = "unavailable"; break; - default: return; - } - - user = HttpUrlEncode(user); - path = StrConcat(5, - "/_matrix/client/v3/presence/",user,"/status", - "?user_id=", user - ); - Free(user); - - request = HashMapCreate(); - HashMapSet(request, "presence", JsonValueString(status_str)); - if (msg) - { - HashMapSet(request, "status_msg", JsonValueString(msg)); - } - - ctx = ParseeCreateRequest(c, HTTP_PUT, path); - ASAuthenticateRequest(c, ctx); - ParseeSetRequestJSON(ctx, request); - JsonFree(request); - - HttpClientContextFree(ctx); - Free(path); -} diff --git a/src/AS/Events.c b/src/AS/Events.c new file mode 100644 index 0000000..83ce147 --- /dev/null +++ b/src/AS/Events.c @@ -0,0 +1,93 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +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->server_base); + 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; +} + +void +ASRedact(const ParseeConfig *c, char *room, char *user, char *e_id) +{ + HttpClientContext *ctx = NULL; + HashMap *request; + char *path, *txn; + if (!c || !room || !e_id) + { + return; + } + + if (!user) + { + char *raw = StrConcat(4, + "@", c->sender_localpart, + ":", c->server_base + ); + user = HttpUrlEncode(raw); + Free(raw); + } + else + { + user = HttpUrlEncode(user); + } + room = HttpUrlEncode(room); + e_id = HttpUrlEncode(e_id); + txn = StrRandom(16); + + path = StrConcat(9, + "/_matrix/client/v3/rooms/", + room, "/redact/", e_id, "/", txn, + "?", "user_id=", user + ); + + request = HashMapCreate(); + ctx = ParseeCreateRequest(c, HTTP_PUT, path); + Free(path); + ASAuthenticateRequest(c, ctx); + ParseeSetRequestJSON(ctx, request); + JsonFree(request); + + HttpClientContextFree(ctx); + Free(user); + Free(room); + Free(e_id); + Free(txn); + + return; +} diff --git a/src/AS/Indicators.c b/src/AS/Indicators.c new file mode 100644 index 0000000..afe860a --- /dev/null +++ b/src/AS/Indicators.c @@ -0,0 +1,119 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +void +ASType(const ParseeConfig *c, char *user, char *room, bool status) +{ + HttpClientContext *ctx = NULL; + HashMap *json; + char *path; + if (!c || !user || !room) + { + return; + } + + user = HttpUrlEncode(user); + path = StrConcat(6, + "/_matrix/client/v3/rooms/", + room, "/typing/", user, + "?user_id=", user + ); + + json = HashMapCreate(); + HashMapSet(json, "typing", JsonValueBoolean(status)); + /* If someone types for 10 minutes straight, they got something weird man. */ + HashMapSet(json, "timeout", JsonValueBoolean(10 MINUTES)); + ctx = ParseeCreateRequest(c, HTTP_PUT, path); + Free(path); + ASAuthenticateRequest(c, ctx); + ParseeSetRequestJSON(ctx, json); + JsonFree(json); + + HttpClientContextFree(ctx); + 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); +} + +void +ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg) +{ + HttpClientContext *ctx = NULL; + HashMap *request; + char *path; + char *status_str = NULL; + if (!c || !user) + { + return; + } + + switch (status) + { + case USER_STATUS_ONLINE: status_str = "online"; break; + case USER_STATUS_OFFLINE: status_str = "offline"; break; + case USER_STATUS_UNAVAILABLE: status_str = "unavailable"; break; + default: return; + } + + user = HttpUrlEncode(user); + path = StrConcat(5, + "/_matrix/client/v3/presence/",user,"/status", + "?user_id=", user + ); + Free(user); + + request = HashMapCreate(); + HashMapSet(request, "presence", JsonValueString(status_str)); + if (msg) + { + HashMapSet(request, "status_msg", JsonValueString(msg)); + } + + ctx = ParseeCreateRequest(c, HTTP_PUT, path); + ASAuthenticateRequest(c, ctx); + ParseeSetRequestJSON(ctx, request); + JsonFree(request); + + HttpClientContextFree(ctx); + Free(path); +} diff --git a/src/AS/Media.c b/src/AS/Media.c new file mode 100644 index 0000000..4e642e9 --- /dev/null +++ b/src/AS/Media.c @@ -0,0 +1,129 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +char * +ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime) +{ + char *size_str, *path, *ret, *user; + int i; + HttpClientContext *ctx; + HashMap *reply; + if (!c || !from) + { + return NULL; + } + + size_str = StrInt(size); + user = StrConcat(4, "@",c->sender_localpart,":",c->server_base); + path = StrConcat(2, + "/_matrix/media/v3/upload?user_id=", user + ); + ctx = ParseeCreateRequest(c, HTTP_POST, path); + ASAuthenticateRequest(c, ctx); + if (size) + { + HttpRequestHeader(ctx, "Content-Length", size_str); + } + if (mime) + { + HttpRequestHeader(ctx, "Content-Type", mime); + } + HttpRequestSendHeaders(ctx); + + for (i = 0; i < size; i++) + { + int ch = StreamGetc(from); + if (ch == EOF) + { + break; + } + StreamPutc(HttpClientStream(ctx), ch); + } + HttpRequestSend(ctx); + reply = JsonDecode(HttpClientStream(ctx)); + ret = StrDuplicate( + JsonValueAsString(HashMapGet(reply, "content_uri")) + ); + if (!ret) + { + JsonEncode(reply, StreamStdout(), JSON_PRETTY); + StreamFlush(StreamStdout()); + } + HttpClientContextFree(ctx); + JsonFree(reply); + Free(size_str); + Free(path); + Free(user); + + return ret; +} +char * +ASReupload(const ParseeConfig *c, char *from, char **mime) +{ + Uri *uri; + HttpClientContext *ctx; + unsigned short port; + int size = 0, flags = HTTP_FLAG_NONE; + char *ret, *content_len; + + if (!c || !from) + { + return NULL; + } + + uri = UriParse(from); + if (!uri) + { + return NULL; + } + if (uri->port) + { + port = uri->port; + } + else if (StrEquals(uri->proto, "https")) + { + port = 443; + } + else + { + port = 80; + } + + if (StrEquals(uri->proto, "https")) + { + flags |= HTTP_FLAG_TLS; + } + + ctx = HttpRequest( + HTTP_GET, flags, port, uri->host, uri->path + ); + HttpRequestSendHeaders(ctx); + HttpRequestSend(ctx); + + if (mime) + { + *mime = HashMapGet(HttpResponseHeaders(ctx), "content-type"); + *mime = StrDuplicate(*mime); + } + + content_len = HashMapGet(HttpResponseHeaders(ctx), "content-length"); + if (content_len) + { + size = strtol(content_len, NULL, 10); + } + ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL); + + HttpClientContextFree(ctx); + UriFree(uri); + return ret; +} + diff --git a/src/AS/Ping.c b/src/AS/Ping.c new file mode 100644 index 0000000..88ac7cb --- /dev/null +++ b/src/AS/Ping.c @@ -0,0 +1,39 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +void +ASPing(const ParseeConfig *conf) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path; + if (!conf) + { + return; + } + + path = StrConcat(3, + "/_matrix/client/v1/appservice/", + "Parsee%20XMPP", + "/ping" + ); + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + HttpClientContextFree(ctx); + JsonFree(json); +} diff --git a/src/AS/Profile.c b/src/AS/Profile.c new file mode 100644 index 0000000..73e9b4b --- /dev/null +++ b/src/AS/Profile.c @@ -0,0 +1,138 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +void +ASSetAvatar(const ParseeConfig *conf, char *user, char *mxc) +{ + HttpClientContext *ctx = NULL; + HashMap *json; + char *path; + if (!conf || !user || !mxc) + { + return; + } + + user = HttpUrlEncode(user); + path = StrConcat(6, + "/_matrix/client/v3/profile/", + user, "/avatar_url", "?", + "user_id=", user + ); + + json = HashMapCreate(); + HashMapSet(json, "avatar_url", JsonValueString(mxc)); + ctx = ParseeCreateRequest(conf, HTTP_PUT, path); + Free(path); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + + HttpClientContextFree(ctx); + JsonFree(json); + Free(user); +} +void +ASSetName(const ParseeConfig *conf, char *user, char *name) +{ + HttpClientContext *ctx = NULL; + HashMap *json; + char *path; + if (!conf || !user || !name) + { + return; + } + + user = HttpUrlEncode(user); + path = StrConcat(6, + "/_matrix/client/v3/profile/", + user, "/displayname", "?", + "user_id=", user + ); + + json = HashMapCreate(); + HashMapSet(json, "displayname", JsonValueString(name)); + ctx = ParseeCreateRequest(conf, HTTP_PUT, path); + Free(path); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + + HttpClientContextFree(ctx); + JsonFree(json); + Free(user); +} + +char * +ASGetName(const ParseeConfig *c, char *room, char *user) +{ + HttpClientContext *ctx; + HashMap *reply; + char *path, *ret; + char *u2 = user; + if (!c || !user) + { + return NULL; + } + + if (!room) + { + user = HttpUrlEncode(user); + path = StrConcat(3, + "/_matrix/client/v3/profile/", user, "/displayname" + ); + ctx = ParseeCreateRequest(c, HTTP_GET, path); + Free(user); + ASAuthenticateRequest(c, ctx); + HttpRequestSendHeaders(ctx); + HttpRequestSend(ctx); + + reply = JsonDecode(HttpClientStream(ctx)); + + ret = StrDuplicate( + JsonValueAsString(HashMapGet(reply, "displayname")) + ); + HttpClientContextFree(ctx); + JsonFree(reply); + Free(path); + + if (!ret) + { + ret = StrDuplicate(u2); + } + return ret; + } + user = HttpUrlEncode(user); + room = HttpUrlEncode(room); + path = StrConcat(4, + "/_matrix/client/v3/rooms/", room, + "/state/m.room.member/", user + ); + ctx = ParseeCreateRequest(c, HTTP_GET, path); + Free(user); + Free(room); + ASAuthenticateRequest(c, ctx); + HttpRequestSendHeaders(ctx); + HttpRequestSend(ctx); + + reply = JsonDecode(HttpClientStream(ctx)); + + ret = StrDuplicate( + JsonValueAsString(HashMapGet(reply, "displayname")) + ); + HttpClientContextFree(ctx); + JsonFree(reply); + Free(path); + + if (!ret) + { + ret = StrDuplicate(u2); + } + return ret; +} diff --git a/src/AS/Register.c b/src/AS/Register.c new file mode 100644 index 0000000..cbb8881 --- /dev/null +++ b/src/AS/Register.c @@ -0,0 +1,46 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +bool +ASRegisterUser(const ParseeConfig *conf, char *user) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + HttpStatus status; + if (!conf || !user) + { + return false; + } + + /* Create user. We don't actually care about the value as we can + * masquerade, as long as it exists. */ + ctx = ParseeCreateRequest( + conf, + HTTP_POST, "/_matrix/client/v3/register" + ); + json = HashMapCreate(); + + HashMapSet(json,"type",JsonValueString("m.login.application_service")); + + user = ParseeGetLocal(user); + HashMapSet(json,"username",JsonValueString(user)); + + ASAuthenticateRequest(conf, ctx); + status = ParseeSetRequestJSON(ctx, json); + + HttpClientContextFree(ctx); + JsonFree(json); + + Free(user); + + return status == HTTP_OK; +} diff --git a/src/AS/Relations.c b/src/AS/Relations.c new file mode 100644 index 0000000..4809413 --- /dev/null +++ b/src/AS/Relations.c @@ -0,0 +1,81 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +Array * +ASGetRelations(const ParseeConfig *c, size_t n, char *room, char *event, char *type) +{ + HttpClientContext *ctx = NULL; + Array *ret, *chunk; + HashMap *json = NULL; + char *path; + char *user; + size_t i; + if (!c || !n || !room || !event) + { + return NULL; + } + + user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base); + if (event) + { + path = StrConcat(6, + "/_matrix/client/v1/rooms/", room, + "/relations/", event, + "?user_id=", user + ); + } + else + { + path = StrConcat(4, + "/_matrix/client/v1/rooms/", room, + "/relations?user_id=", user + ); + } + Free(user); + + ctx = ParseeCreateRequest(c, HTTP_GET, path); + Free(path); + ASAuthenticateRequest(c, ctx); + HttpRequestSendHeaders(ctx); + HttpRequestSend(ctx); + + + json = JsonDecode(HttpClientStream(ctx)); + ret = ArrayCreate(); + chunk = GrabArray(json, 1, "chunk"); + for (i = 0; i < ArraySize(chunk); i++) + { + HashMap *obj = JsonValueAsObject(ArrayGet(chunk, i)); + ArrayAdd(ret, JsonDuplicate(obj)); + } + + HttpClientContextFree(ctx); + JsonFree(json); + return ret; +} + +void +ASFreeRelations(Array *relations) +{ + size_t i; + if (!relations) + { + return; + } + + for (i = 0; i < ArraySize(relations); i++) + { + JsonFree(ArrayGet(relations, i)); + } + + ArrayFree(relations); +} diff --git a/src/AS/Room.c b/src/AS/Room.c new file mode 100644 index 0000000..3bc7cbf --- /dev/null +++ b/src/AS/Room.c @@ -0,0 +1,352 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +void +ASInvite(const ParseeConfig *conf, char *id, char *invited) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path, *bridge; + if (!conf || !id || !invited) + { + return; + } + + bridge = StrConcat(4, + "@", conf->sender_localpart, + ":", conf->server_base + ); + path = StrConcat(5, + "/_matrix/client/v3/rooms/", id, "/invite", + "?user_id=", bridge + ); + Free(bridge); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + HashMapSet(json, "user_id", JsonValueString(invited)); + HashMapSet(json, "reason", JsonValueString("Pass over.")); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + + HttpClientContextFree(ctx); + JsonFree(json); +} +void +ASBan(const ParseeConfig *conf, char *id, char *banned) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path, *bridge; + if (!conf || !id || !banned) + { + return; + } + + bridge = StrConcat(4, + "@", conf->sender_localpart, + ":", conf->server_base + ); + path = StrConcat(5, + "/_matrix/client/v3/rooms/", id, "/ban", + "?user_id=", bridge + ); + Free(bridge); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + HashMapSet(json, "user_id", JsonValueString(banned)); + HashMapSet(json, "reason", JsonValueString("Parsee felt jealous.")); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + + HttpClientContextFree(ctx); + JsonFree(json); +} +void +ASKick(const ParseeConfig *conf, char *id, char *banned) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path, *bridge; + if (!conf || !id || !banned) + { + return; + } + + bridge = StrConcat(4, + "@", conf->sender_localpart, + ":", conf->server_base + ); + path = StrConcat(5, + "/_matrix/client/v3/rooms/", id, "/kick", + "?user_id=", bridge + ); + Free(bridge); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + HashMapSet(json, "user_id", JsonValueString(banned)); + HashMapSet(json, "reason", JsonValueString("Parsee felt jealous.")); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + + HttpClientContextFree(ctx); + JsonFree(json); +} +char * +ASJoin(const ParseeConfig *conf, char *id, char *masquerade) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path, *ret; + if (!conf || !id) + { + return NULL; + } + + if (!masquerade) + { + char *raw = StrConcat(4, + "@", conf->sender_localpart, + ":", conf->server_base + ); + masquerade = HttpUrlEncode(raw); + Free(raw); + } + else + { + masquerade = HttpUrlEncode(masquerade); + } + id = HttpUrlEncode(id); + path = StrConcat(5, + "/_matrix/client/v3/join/", id, "?", + "user_id=", masquerade + ); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + JsonFree(json); + + json = JsonDecode(HttpClientStream(ctx)); + ret = StrDuplicate(GrabString(json, 1, "room_id")); + JsonFree(json); + + HttpClientContextFree(ctx); + Free(masquerade); + Free(id); + + return ret; +} +void +ASLeave(const ParseeConfig *conf, char *id, char *masquerade) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path; + if (!conf || !id) + { + return; + } + + if (!masquerade) + { + char *raw = StrConcat(4, + "@", conf->sender_localpart, + ":", conf->server_base + ); + masquerade = HttpUrlEncode(raw); + Free(raw); + } + else + { + masquerade = HttpUrlEncode(masquerade); + } + id = HttpUrlEncode(id); + path = StrConcat(5, + "/_matrix/client/v3/rooms/", id, "/leave?", + "user_id=", masquerade + ); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + JsonFree(json); + + HttpClientContextFree(ctx); + Free(masquerade); + Free(id); +} + +char * +ASCreateRoom(const ParseeConfig *conf, char *by, char *alias) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path, *id; + if (!conf || !by) + { + return NULL; + } + + path = StrConcat(3, + "/_matrix/client/v3/createRoom", + "?user_id=", by + ); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + if (alias) + { + char *trimmed = StrDuplicate(alias); + if (*alias == '#') + { + char *tmp, cb[2] = { 0 }; + alias++; + Free(trimmed); + trimmed = NULL; + + while (*alias && *alias != ':') + { + cb[0] = *alias; + tmp = trimmed; + trimmed = StrConcat(2, trimmed, cb); + Free(tmp); + alias ++; + } + } + HashMapSet(json, "room_alias_name", JsonValueString(trimmed)); + Free(trimmed); + } + HashMapSet(json, "visibility", JsonValueString("public")); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + + JsonFree(json); + json = JsonDecode(HttpClientStream(ctx)); + id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id"))); + HttpClientContextFree(ctx); + JsonFree(json); + + return id; +} +char * +ASCreateDM(const ParseeConfig *conf, char *by, char *with) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path, *id; + if (!conf || !by || !with) + { + return NULL; + } + + path = StrConcat(3, + "/_matrix/client/v3/createRoom", + "?user_id=", by + ); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + { + Array *invitees = ArrayCreate(); + + ArrayAdd(invitees, JsonValueString(with)); + HashMapSet(json, "invite", JsonValueArray(invitees)); + HashMapSet(json, "is_direct", JsonValueBoolean(true)); + } + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + + JsonFree(json); + json = JsonDecode(HttpClientStream(ctx)); + id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id"))); + HttpClientContextFree(ctx); + JsonFree(json); + + return id; +} + +HashMap * +ASGetPL(const ParseeConfig *c, char *room) +{ + char *path; + HttpClientContext *ctx; + HashMap *reply; + if (!c || !room) + { + return NULL; + } + room = HttpUrlEncode(room); + path = StrConcat(4, + "/_matrix/client/v3/rooms/", room, + "/state/m.room.power_levels/", "" + ); + ctx = ParseeCreateRequest(c, HTTP_GET, path); + Free(room); + ASAuthenticateRequest(c, ctx); + HttpRequestSendHeaders(ctx); + HttpRequestSend(ctx); + + reply = JsonDecode(HttpClientStream(ctx)); + + HttpClientContextFree(ctx); + Free(path); + + return reply; +} +void +ASSetPL(const ParseeConfig *conf, char *id, HashMap *m) +{ + char *user; + if (!conf || !id || !m) + { + return; + } + user = StrConcat(4, + "@",conf->sender_localpart, + ":",conf->server_base + ); + ASSetState(conf, id, "m.room.power_levels", "", user, m); + Free(user); +} diff --git a/src/AS/Send.c b/src/AS/Send.c new file mode 100644 index 0000000..4032423 --- /dev/null +++ b/src/AS/Send.c @@ -0,0 +1,48 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +char * +ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c) +{ + HttpClientContext *ctx = NULL; + char *path; + char *txn, *ret; + HashMap *reply; + if (!conf || !id || !type || !user || !c) + { + JsonFree(c); + return NULL; + } + + txn = StrRandom(16); + path = StrConcat(9, + "/_matrix/client/v3/rooms/", + id, "/send/", type, "/", txn, "?", + "user_id=", user + ); + Free(txn); + + ctx = ParseeCreateRequest(conf, HTTP_PUT, path); + Free(path); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, c); + + reply = JsonDecode(HttpClientStream(ctx)); + ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id"))); + JsonFree(reply); + + HttpClientContextFree(ctx); + JsonFree(c); + + return ret; +} + diff --git a/src/AS/State.c b/src/AS/State.c new file mode 100644 index 0000000..ef2ff14 --- /dev/null +++ b/src/AS/State.c @@ -0,0 +1,37 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include + +void +ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state) +{ + HttpClientContext *ctx = NULL; + char *path; + if (!conf || !id || !type || !mask || !state) + { + JsonFree(state); + return; + } + + path = StrConcat(9, + "/_matrix/client/v3/rooms/", id, "/state/", + type, "/", key, "?", "user_id=", mask + ); + + ctx = ParseeCreateRequest(conf, HTTP_PUT, path); + Free(path); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, state); + + HttpClientContextFree(ctx); + JsonFree(state); +} + diff --git a/src/XEP-0393.c b/src/XEP-0393.c index 8f3cef2..a90b4a1 100644 --- a/src/XEP-0393.c +++ b/src/XEP-0393.c @@ -80,6 +80,13 @@ DecodeQuote(StringRect rect, size_t *skip) ret = rect; ret.end_line--; + /* TODO: You can easily craft strings that conceal data( + * > Please mind the whitespace + * > hidden we're concealing data to Matrix + * > star in Well, you can still stare at the body and XML + * > four but that's for Nerds + * > seasons See, Touhou reference! + * concealing!) */ while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace(ch)) { shift_by++; diff --git a/src/XMPPThread/ReListener.c b/src/XMPPThread/ReListener.c index e66e1af..0641f42 100644 --- a/src/XMPPThread/ReListener.c +++ b/src/XMPPThread/ReListener.c @@ -52,7 +52,6 @@ XMPPDispatcher(void *argp) { XMLElement *stanza = RetrieveStanza(thread); - /* TODO: I've seen some spikes in some threads. */ if (!stanza) { UtilSleepMillis(5); @@ -153,7 +152,6 @@ ParseeXMPPThread(void *argp) pthread_mutex_init(&info.lock, NULL); /* ... and its readers. */ - /* TODO: Make that configurable. */ info.available_dispatchers = args->config->xmpp_threads; info.dispatchers = Malloc( sizeof(*info.dispatchers) * info.available_dispatchers diff --git a/src/XMPPThread/Stanzas/Message.c b/src/XMPPThread/Stanzas/Message.c index 3c6949c..1b207b1 100644 --- a/src/XMPPThread/Stanzas/Message.c +++ b/src/XMPPThread/Stanzas/Message.c @@ -9,6 +9,62 @@ #include +static void +ProcessChatstates(ParseeData *args, XMLElement *stanza) +{ + char *from_matrix, *mroom_id; +#define CHAT_STATES "http://jabber.org/protocol/chatstates" + if (XMLookForTKV(stanza, "composing", "xmlns", CHAT_STATES)) + { + from_matrix = ParseeGetBridgedUser(args, stanza); + mroom_id = ParseeGetBridgedRoom(args, stanza); + + ASType(args->config, from_matrix, mroom_id, true); + + Free(from_matrix); + Free(mroom_id); + mroom_id = NULL; + from_matrix = NULL; + } + if (XMLookForTKV(stanza, "active", "xmlns", CHAT_STATES)) + { + char *latest = NULL; + from_matrix = ParseeGetBridgedUser(args, stanza); + 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(from_matrix); + Free(latest); + Free(mroom_id); + mroom_id = NULL; + from_matrix = NULL; + } + if (XMLookForTKV(stanza, "paused", "xmlns", CHAT_STATES) || + XMLookForTKV(stanza, "received", "xmlns", "urn:xmpp:receipts") || + XMLookForTKV(stanza, "displayed", "xmlns", "urn:xmpp:chat-markers:0")) + { + /* TODO: Use stanza ID if possible */ + char *latest = NULL; + from_matrix = ParseeGetBridgedUser(args, stanza); + mroom_id = ParseeGetBridgedRoom(args, stanza); + + latest = ParseeLookupHead(mroom_id); + + ASPresence(args->config, from_matrix, mroom_id, latest); + + Free(from_matrix); + Free(latest); + Free(mroom_id); + mroom_id = NULL; + from_matrix = NULL; + + } +#undef CHAT_STATES +} bool MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) { @@ -107,57 +163,7 @@ end_error: PEPManagerHandle(thr->info->pep_manager, stanza); /* TODO: Separate the chatstate processing code. */ -#define CHAT_STATES "http://jabber.org/protocol/chatstates" - if (XMLookForTKV(stanza, "composing", "xmlns", CHAT_STATES)) - { - from_matrix = ParseeGetBridgedUser(args, stanza); - mroom_id = ParseeGetBridgedRoom(args, stanza); - - ASType(args->config, from_matrix, mroom_id, true); - - Free(from_matrix); - Free(mroom_id); - mroom_id = NULL; - from_matrix = NULL; - } - if (XMLookForTKV(stanza, "active", "xmlns", CHAT_STATES)) - { - char *latest = NULL; - from_matrix = ParseeGetBridgedUser(args, stanza); - 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(from_matrix); - Free(latest); - Free(mroom_id); - mroom_id = NULL; - from_matrix = NULL; - } - if (XMLookForTKV(stanza, "paused", "xmlns", CHAT_STATES) || - XMLookForTKV(stanza, "received", "xmlns", "urn:xmpp:receipts") || - XMLookForTKV(stanza, "displayed", "xmlns", "urn:xmpp:chat-markers:0")) - { - /* TODO: Use stanza ID if possible */ - char *latest = NULL; - from_matrix = ParseeGetBridgedUser(args, stanza); - mroom_id = ParseeGetBridgedRoom(args, stanza); - - latest = ParseeLookupHead(mroom_id); - - ASPresence(args->config, from_matrix, mroom_id, latest); - - Free(from_matrix); - Free(latest); - Free(mroom_id); - mroom_id = NULL; - from_matrix = NULL; - - } -#undef CHAT_STATES + ProcessChatstates(args, stanza); to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to")); decode_from = ParseeLookupJID(from); @@ -283,8 +289,28 @@ end_error: else if (reactions) { Array *react_child = reactions->children; + Array *to_redact; size_t reacts = ArraySize(react_child); event_id = ParseeGetReactedEvent(args, stanza); + + to_redact = ASGetRelations( + args->config, 30, mroom_id, event_id, + "m.reaction" + ); + for (i = 0; i < ArraySize(to_redact); i++) + { + HashMap *e = ArrayGet(to_redact, i); + char *e_sender = GrabString(e, 1, "sender"); + char *e_id = GrabString(e, 1, "event_id"); + if (!StrEquals(e_sender, encoded)) + { + continue; + } + + ASRedact(args->config, mroom_id, encoded, e_id); + } + ASFreeRelations(to_redact); + for (i = 0; i < reacts; i++) { XMLElement *reaction, *react_data; diff --git a/src/include/AS.h b/src/include/AS.h index cdb67c1..1d993e1 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -3,7 +3,8 @@ /*-* * Functions used to communicate with a Matrix server and execute actions * over HTTP(S)+JSON. This effectively serves as Parsee's Matrix SDK. - * TODO: Write my own RanSDK for KappaChat. + * TODO: Write my own RumiaSDK for the KappaChat project(not the client + * itself). * -------- * Writren-By: LDA */ @@ -156,6 +157,17 @@ extern char * ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, c * See-Also: ASUpload */ extern char * ASReupload(const ParseeConfig *c, char *from, char **mime); -extern HashMap * ASGetUserConfig(const ParseeConfig *c, char *user, char *key); -extern void ASSetUserConfig(const ParseeConfig *c, char *u, char *key, HashMap *map); +/** Gets up to {n} relations to an event(with a specific type, optionally). + * ---------- + * Returns: A list of JSON objects[HEAP] + * Thrasher: ASFreeRelations + * See-Also: https://spec.matrix.org/v1.7/client-server-api/#get_matrixclientv1roomsroomidrelationseventid */ +extern Array * ASGetRelations(const ParseeConfig *c, size_t n, char *room, char *event, char *type); + +/** Destroys a relation list created by {ASGetRelations} + * ------------ + * Returns: NOTHING + * Thrashes: {relations} + * See-Also: ASGetRelations */ +extern void ASFreeRelations(Array *relations); #endif From 8042ccc0ccb1ec37e87356664e319ba8bd95222b Mon Sep 17 00:00:00 2001 From: LDA Date: Thu, 22 Aug 2024 00:04:50 +0200 Subject: [PATCH 012/220] [ADD] Add basic Matrix ID parser --- .gitignore | 3 +++ Makefile | 2 ++ src/MatrixEventHandler.c | 8 +++----- src/MatrixID.c | 34 ++++++++++++++++++++++++++++++++ src/Parsee/User.c | 20 +++++++++++-------- src/XMPP/Component.c | 1 + src/XMPPThread/Stanzas/Message.c | 2 -- src/include/Matrix.h | 15 ++++++++++++++ 8 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 src/MatrixID.c diff --git a/.gitignore b/.gitignore index ff7606f..422a0f1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ tools/out/* ayaya/* ayaya + +#ctags +tags diff --git a/Makefile b/Makefile index 88cfb8d..7236d15 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,8 @@ all: binary utils binary: $(OBJ_FILES) $(CC) $(LDFLAGS) $(OBJ_FILES) -o $(BINARY) +tags: + @ctags --recurse $(SOURCE)/ clean: rm -rf $(OBJECT) $(BINARY) $(AYAS) diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 41c229e..effcc45 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -11,6 +11,8 @@ #include #include +#include + static void JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) { @@ -20,7 +22,7 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) char *rev = StrConcat(3, muc, "/", nick); int nonce = 0; - while (!XMPPJoinMUC(data->jabber, jid, rev, true) && nonce < 20) + while (!XMPPJoinMUC(data->jabber, jid, rev, true) && nonce < 32) { char *nonce_str = StrInt(nonce); char *input = StrConcat(4, sender, name, data->id, nonce_str); @@ -122,10 +124,6 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) goto end; } - /* TODO: We need to deal with the nick properly, as XMPP - * requires us to provide it whenever we want to even think - * about leaving... - * I love how this is the last place victim of the dreaded [p]... */ name = StrDuplicate(ParseeLookupNick(muc_id, sender)); rev = StrConcat(3, muc_id, "/", name); diff --git a/src/MatrixID.c b/src/MatrixID.c new file mode 100644 index 0000000..4488fd4 --- /dev/null +++ b/src/MatrixID.c @@ -0,0 +1,34 @@ +#include + +#include + +#include + +UserID * +MatrixParseID(char *user) +{ + UserID *ret = NULL; + char *localstart, *serverstart; + if (!user || *user != '@') + { + return NULL; + } + + localstart = user + 1; + serverstart = strchr(user, ':'); + if (!*localstart || !serverstart || localstart == serverstart) + { + return NULL; + } + if (!*++serverstart) + { + return NULL; + } + + ret = Malloc(sizeof(*ret)); + memset(ret, '\0', sizeof(*ret)); + memcpy(ret->localpart, localstart, serverstart - localstart - 1); + memcpy(ret->server, serverstart, strlen(serverstart)); + + return ret; +} diff --git a/src/Parsee/User.c b/src/Parsee/User.c index 9e2ee22..42cfb1b 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -10,32 +10,36 @@ #include #include +#include + bool ParseeIsPuppet(const ParseeConfig *conf, char *user) { - char *localpart; + UserID *id; bool flag = true; size_t len; - if (!user || !conf || *user != '@') + if (!user || + !conf || + *user != '@' || + !(id = MatrixParseID(user))) { return false; } - localpart = user + 1; len = strlen(conf->namespace_base); - if (strncmp(localpart, conf->namespace_base, len)) + if (strncmp(id->localpart, conf->namespace_base, len)) { flag = false; } - len = strlen(conf->sender_localpart); - if (!strncmp(localpart, conf->sender_localpart, len)) + if (StrEquals(id->localpart, conf->sender_localpart)) { flag = true; } - /* TODO: Check serverpart. 2 Parsee instances may be running in the same - * room. */ + flag = flag && StrEquals(id->server, conf->server_base); + + Free(id); return flag; } char * diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index b43488b..c44d14b 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -154,6 +154,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared) if (ev->type != XML_LEXER_ELEM || !StrEquals(ev->element, "handshake")) { + Log(LOG_DEBUG, "type=%d elem='%s'", ev->type, ev->element); Log(LOG_ERR, "Excepted empty handshake reply, got nonsense."); Log(LOG_ERR, "Another service (possibly Parsee) may have taken over."); Log(LOG_ERR, ""); diff --git a/src/XMPPThread/Stanzas/Message.c b/src/XMPPThread/Stanzas/Message.c index 1b207b1..230131d 100644 --- a/src/XMPPThread/Stanzas/Message.c +++ b/src/XMPPThread/Stanzas/Message.c @@ -162,7 +162,6 @@ end_error: PEPManagerHandle(thr->info->pep_manager, stanza); - /* TODO: Separate the chatstate processing code. */ ProcessChatstates(args, stanza); to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to")); @@ -317,7 +316,6 @@ end_error: reaction = ArrayGet(react_child, i); react_data = ArrayGet(reaction->children, 0); - /* TODO: We should manage removed reactions. */ Free(ASSend( args->config, mroom_id, encoded, "m.reaction", diff --git a/src/include/Matrix.h b/src/include/Matrix.h index 3d86abd..0ffb7ec 100644 --- a/src/include/Matrix.h +++ b/src/include/Matrix.h @@ -3,6 +3,21 @@ #include +/* A simple representation of everything. It is not as complex as + * Telodendria's CommonID parser, simply because Parsee does not + * need such complexity. */ +typedef struct UserID { + /* We're being extra leinent. */ + char localpart[256]; + char server[256]; +} UserID; + +/** Parses a Matrix user ID, with no attention to detail. + * ----------- + * Returns: a valid user ID[HEAP] | NULL + * Thrasher: Free */ +extern UserID * MatrixParseID(char *user); + /* Creates an error message JSON, with the specified code and message. */ extern HashMap * MatrixCreateError(char *err, char *msg); From 54c5331835787d16addb3c0bae7fea96eed0acb9 Mon Sep 17 00:00:00 2001 From: lda Date: Thu, 22 Aug 2024 14:38:02 +0200 Subject: [PATCH 013/220] [MOD] Try to make Parsee work with TCC --- src/Command/Parser.c | 2 +- src/Parsee/Utils/Formatting.c | 4 +++- src/XMPP/Component.c | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Command/Parser.c b/src/Command/Parser.c index 6ff55d3..2101254 100644 --- a/src/Command/Parser.c +++ b/src/Command/Parser.c @@ -49,7 +49,7 @@ CommandParse(char *cmd) char c = *cur; char *tmp; bool type; - char char_type; + char char_type = '\0'; switch (state) { case STATE_WHITE: diff --git a/src/Parsee/Utils/Formatting.c b/src/Parsee/Utils/Formatting.c index e538abc..4e89aec 100644 --- a/src/Parsee/Utils/Formatting.c +++ b/src/Parsee/Utils/Formatting.c @@ -37,7 +37,9 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) size_t len = strp ? strlen(strp) : 0; \ for (cidx = 0; cidx < len; cidx++) \ { \ - char cch[2] = { strp[cidx], 0 }; \ + char cch[2]; \ + cch[0] = strp[cidx]; \ + cch[1] = '\0'; \ char nch = *cch ? strp[cidx+1] : '\0'; \ bool c = *cch == '\n' && nch != '>'; \ if (c && flags.quote) \ diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index b43488b..a581845 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -33,6 +33,15 @@ XMPPInitialiseCompStream(char *host, int port) hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(host, serv, &hints, &res0); + if (error) + { + const char *error_str = gai_strerror(error); + Log(LOG_ERR, + "%s: cannot connect to '%s': %s", __func__, + host, error_str + ); + return NULL; + } for (res = res0; res; res = res->ai_next) { From 2cc8e16ba276d08016c8b1a3f2cb536d5eae5c15 Mon Sep 17 00:00:00 2001 From: lda Date: Thu, 22 Aug 2024 14:40:34 +0200 Subject: [PATCH 014/220] [MOD] Allow other compilers --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7236d15..82d651a 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ OBJECT=build AYAS=ayaya ETC=etc INCLUDES=src/include -CC=cc +CC ?=cc CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -O3 -g -Wall -Werror LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -O3 -g AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)" From 27e0b96ecd6c3f973617804ea2777937fbbc536c Mon Sep 17 00:00:00 2001 From: LDA Date: Thu, 22 Aug 2024 14:49:38 +0200 Subject: [PATCH 015/220] [MOD] Add manpages Oops, gitignore didn't track these --- .gitignore | 3 + etc/man/man1/parsee-adminify.1 | 39 +++++++++++ etc/man/man1/parsee-aya.1 | 40 +++++++++++ etc/man/man1/parsee-config.1 | 90 +++++++++++++++++++++++++ etc/man/man1/parsee.1 | 118 +++++++++++++++++++++++++++++++++ 5 files changed, 290 insertions(+) create mode 100644 etc/man/man1/parsee-adminify.1 create mode 100644 etc/man/man1/parsee-aya.1 create mode 100644 etc/man/man1/parsee-config.1 create mode 100644 etc/man/man1/parsee.1 diff --git a/.gitignore b/.gitignore index 422a0f1..7f1e762 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ ayaya #ctags tags + +# Whitelists +!etc/* diff --git a/etc/man/man1/parsee-adminify.1 b/etc/man/man1/parsee-adminify.1 new file mode 100644 index 0000000..cc29132 --- /dev/null +++ b/etc/man/man1/parsee-adminify.1 @@ -0,0 +1,39 @@ +." The last field is the codename, by the way. +." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN +.TH parsee-adminify 1 "Parsee Utility" "phantasmagoria" + +.SH NAME +parsee-adminify - bootstrap an admin to a new Parsee server + +.SH SYNOPSIS +parsee-adminify +.B [DB path] +.B [user] + +.SH DESCRIPTION +.I parsee-adminify +is a tool to add/list the Parsee administrator list. It currently only +allows you to see/add admins to it, using simplified globrules. + +.SH OPTIONS +.TP +.B [DB path] +The DB directory this tool will use to manage administrators. +.TP +.B [user] +If set, adds the glob +.I [user] +as a Parsee administrator. Otherwise, lists all administrators in +the Parsee instance. + +.SH BUGS +.PP +Currently not available with flat-based backends. + +.SH LICENSE +Unlike Parsee, +.B parsee-adminify +is under the CC0/PD. + +.SH SEE ALSO +.B parsee-config(1), parsee-aya(1), parsee(1) diff --git a/etc/man/man1/parsee-aya.1 b/etc/man/man1/parsee-aya.1 new file mode 100644 index 0000000..c688102 --- /dev/null +++ b/etc/man/man1/parsee-aya.1 @@ -0,0 +1,40 @@ +." The last field is the codename, by the way. +." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN +.TH parsee-aya 1 "Parsee Utility" "phantasmagoria" + +.SH NAME +parsee-aya - generate some nice Ayaya! documentation + +.SH SYNOPSIS +parsee-aya +.B [-i HEADER] +.B [-o HTML] +.B [-C STYLESHEET] +.B [-p NAME] + +.SH DESCRIPTION +.I parsee-aya +is a tool to generate Ayadocs(HTML documentation) from a formatted +header file. See a Parsee header file for an example of the inner usage. + +.SH OPTIONS +.TP +.BR -i HEADER +The input header file to process out. +.TP +.BR -o HTML +The HTML file to write out the Ayadoc. +.TP +.BR -C STYLESHEET +A stylesheet file to use for Ayadocs. +.TP +.BR -p NAME +The project's name. If unavailable, defaults to Ayaya! + +.SH LICENSE +Unlike Parsee, +.B parsee-aya +is under the CC0/PD. + +.SH SEE ALSO +.B parsee-config(1), parsee-adminify(1), parsee(1) diff --git a/etc/man/man1/parsee-config.1 b/etc/man/man1/parsee-config.1 new file mode 100644 index 0000000..d21fca6 --- /dev/null +++ b/etc/man/man1/parsee-config.1 @@ -0,0 +1,90 @@ +." The last field is the codename, by the way. +." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN +.TH parsee-config 1 "Parsee Utility" "phantasmagoria" + +.SH NAME +parsee-config - generate a basic configuration file + +.SH SYNOPSIS +parsee-config +.B [-H HOMESERVER_NAME] +.B [-s SHARED_SECRET] +.B [-m MEDIA_URL] +.B [-J JABBER_HOST] +.B [-p JABBER_PORT] +.B [-d DATABASE] +.B [-S DATABASE size] + +.SH DESCRIPTION +.I parsee-config +creates a basic configuration file to initialise a Parsee instance with the +given input flags, and makes a basic database. A basic example of +.I parsee-config +would be this(for a blow.hole server, with a xmpp.blow.hole JCP on Prosody, +and a 128MB LMDB backend) +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ parsee-config \\ + -d '/var/lib/parsee' \\ + -m 'https://pmedia.blow.hole' \\ + -H 'blow.hole' \\ + -s 'The Dark Shared Secret' \\ + -J 'xmpp.blow.hole' \\ + -S 128 +.fi +.if n \{\ +.RE +.\} +.sp + +.SH OPTIONS +.TP +.BR -H HOMESERVER_NAME +.I HOMESERVER_NAME +points to the homeserver name, without delegation (which is autosensed). +For example, if you except Parsee users to be on +.IR @something:blow.hole +, then it should be set to +.IR blow.hole . +.TP +.BR -s SHARED_SECRET +.I SHARED_SECRET +is a shared secret known by Parsee and the XMPP component to authenticate. +.TP +.BR -m MEDIA_URL +.I MEDIA_URL +is an optional field used by Parsee as an address that points to Matrix +media. It must be publicly accessible (behind a reverse proxy to HTTP:7642) +.TP +.BR -J JABBER_HOST +.I JABBER_HOST +is used as the component host for Parsee. +.TP +.BR -p JABBER_PORT +.I JABBER_PORT +is used as the component post for Parsee. Parsee uses it alongside +.I JABBER_HOST +to connect to +.I JABBER_HOST:JABBER_PORT +over TCP. +.TP +.BR -d DATABASE +The directory inwhich Parsee stores its database(LMDB/flat-file). Whenever the +database is flat or LMDB is dictated by the presence of the -S flag, and +whenever Cytoplasm has LMDB enabled. +.TP +.BR -S SIZE +If set, enables LMDB in Parsee, and sets its max DB size to +.BR SIZE MiB . + + +.SH LICENSE +Unlike Parsee, +.B parsee-config +is under the CC0/PD. + +.SH SEE ALSO +.B parsee-aya(1), parsee-adminify(1), parsee(1) diff --git a/etc/man/man1/parsee.1 b/etc/man/man1/parsee.1 new file mode 100644 index 0000000..bad9ff6 --- /dev/null +++ b/etc/man/man1/parsee.1 @@ -0,0 +1,118 @@ +." The last field is the codename, by the way. +." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN +.TH parsee 1 "Parsee Utility" "phantasmagoria" + +.SH NAME +parsee - the jealous XMPP-Matrix bridge + +.SH SYNOPSIS +parsee +.B [-J num] +.B [-H num] +.B [-C file] +.B [-g] +.B [-v] +.B [-h] + +.SH DESCRIPTION +Parsee is a +.B bridging program +, that is, it connects two chat protocols together (here, XMPP/Jabber, +and Matrix). +.PP +As such, a user on XMPP can communicate with Matrix users, and +vice-versa with it. +.PP +Currently, Parsee is under +.BI beta . +This means that it is still subject to bugs, flaws, and may change in a +backwards-incompatible manner at any time. + +.SH FIRST RUN +To start with a new install of Parsee(assuming you have a homeserver +with AS support and a XMPP server with JCP support, and that you know +the shared secret/domain for that component. Guides for that are +outside the scope of this manpage.), start by running +.B parsee-config(1) +with the flags provided in it, according to your configuration. +.PP +This should generate a +.I parsee.json +file, which is the configuration files for Parsee, and a directory where +the Parsee database may reside. You can then run +.I parsee -g +in that directory, which should generate a +.I parsee.yaml +file, this time with the generated AS configuration file, which you can +apply to your homeserver(how is implementation-dependent, but it is +generally a matter of modifying a configuration file to add the path +to such file). + +.SH OPTIONS +.TP +.BR -J num +Set the number of threads used for XMPP stanza processing to +.I num\[char46] +.TP +.BR -H num +Set the number of threads used for HTTP request processing to +.I num\[char46] +.TP +.BR -C file +Sets the configuration JSON path to +.I file\[char46] +.PP +Defaults to +.I parsee.json +if none can be found. +.TP +.BR -g +Generates a +.I parsee.yaml +file to be used as an Application-Service entry within Matrix, and +exits. +.TP +.BR -v +Verbose logging of Parsee's behaviour. Recommended when testing Parsee +for bugs/hints. +.TP +.BR -h +Prints the command list with descriptions. + +.SH BUGS +.PP +Sometimes Parsee will not respond to ^C requests properly, which +causes a system administrator to have to invoke SIGKILL. +.PP +Parsee seems to grow slowly in memory usage when messages are bridged +which, in the long run, could cause a memory error. All of the memory is +tracked, however, which means that this isn't exactly a leak. +.PP +Some important features still aren't implemented yet(e.g being able to join +a MUC from XMPP) + +.SH CHATROOMS +You may talk about Parsee on these rooms(on Matrix and XMPP): +.RE +.IP xmpp:parsee@conference.monocles.eu?join +for XMPP, which is bridged along Matrix +.IP #parsee:tedomum.net +for Matrix, which is bridged along Parsee +.RS + +.SH AUTHORS +." Contributors, feel free to put your names here in a PR, as long as +." it is acceptable +. PP +.BR LDA: +main maintainer of Parsee, accessible over XMPP at lda@freetards.xyz +and over Matrix as @fourier:ari.lt. + +.SH LICENSE +Parsee is available under the AGPL3, but has some code under CC0/PD, and +some from Cytoplasm itself. Please see +.I https://git.kappach.at/lda/Parsee +for more information. + +.SH SEE ALSO +.B parsee-config(1), parsee-adminify(1), parsee-aya(1) From 692cb8aa6f63492d6b8e633857f9a3c31950e4ab Mon Sep 17 00:00:00 2001 From: LDA Date: Sat, 24 Aug 2024 19:36:37 +0200 Subject: [PATCH 016/220] [MOD] Verbosity levels, avatar cleanser I have a weird bug where Parsee seems to hang on a Db request, been tryign to figure that otu since a while but can't quite put my finger on where. You can have this commit, as a treat. --- src/Commands/Stats.c | 1 - src/Events.c | 2 - src/Glob.c | 1 - src/HttParsee.c | 1 - src/Main.c | 61 ++++++++++++++++++++++++++++--- src/Parsee/Config.c | 8 +--- src/Parsee/User.c | 6 +-- src/Routes/Ping.c | 37 ------------------- src/Routes/Transactions.c | 1 - src/XMPP/Component.c | 3 +- src/XMPPThread/Bridged.c | 1 - src/XMPPThread/PEP.c | 27 ++++++++++++++ src/XMPPThread/Pubsub.c | 32 ---------------- src/XMPPThread/ReListener.c | 29 ++++++++++++++- src/XMPPThread/Stanzas/IQ.c | 13 +++---- src/XMPPThread/Stanzas/Presence.c | 31 ++++++++++++---- src/include/Parsee.h | 7 ++++ src/include/Routes.h | 1 - tools/noavatars.c | 39 ++++++++++++++++++++ 19 files changed, 190 insertions(+), 111 deletions(-) delete mode 100644 src/Routes/Ping.c delete mode 100644 src/XMPPThread/Pubsub.c create mode 100644 tools/noavatars.c diff --git a/src/Commands/Stats.c b/src/Commands/Stats.c index 1102f89..219204a 100644 --- a/src/Commands/Stats.c +++ b/src/Commands/Stats.c @@ -24,7 +24,6 @@ CommandHead(CmdStats, cmd, argp) BotInitialise(); - /* TODO: Separate these into different "categories" */ ReplySprintf("Information for %s v%s (Cytoplasm %s)", NAME, VERSION, CytoplasmGetVersionStr() ); diff --git a/src/Events.c b/src/Events.c index 2a3f5b3..ded7b89 100644 --- a/src/Events.c +++ b/src/Events.c @@ -37,12 +37,10 @@ MatrixCreateMessage(char *body) HashMapSet(map, "msgtype", JsonValueString("m.text")); HashMapSet(map, "body", JsonValueString(body)); { - /* TODO */ XEP393Element *e = XEP393(body); text = XEP393ToXMLString(e); XEP393FreeElement(e); - HashMapSet(map, "formatted_body", JsonValueString(text)); HashMapSet(map, "format", JsonValueString("org.matrix.custom.html")); Free(text); diff --git a/src/Glob.c b/src/Glob.c index 805f5a6..02765dd 100644 --- a/src/Glob.c +++ b/src/Glob.c @@ -19,7 +19,6 @@ GlobMatches(char *rule, char *string) switch (c1) { case '*': - /* TODO */ while ((c2 = *string) && (c2 != next)) { string++; diff --git a/src/HttParsee.c b/src/HttParsee.c index 1f9ade9..724f9ff 100644 --- a/src/HttParsee.c +++ b/src/HttParsee.c @@ -41,7 +41,6 @@ ParseeRequest(HttpServerContext *ctx, void *argp) JsonFree(response); response = MatrixCreateError("M_NOT_FOUND", "Route not found."); - /* TODO: Set a thing */ } /* Whatever, we routed a thing. */ diff --git a/src/Main.c b/src/Main.c index 543d548..3303db1 100644 --- a/src/Main.c +++ b/src/Main.c @@ -43,7 +43,10 @@ static const Argument arguments[] = Arg('C', true, "file(='parsee.json')", "Sets the JSON config to use") Arg('g', false, NULL,"Generates a parsee.yaml AS file before exiting") - Arg('v', false, NULL,"Forces Parsee to print in a more verbose fashion") + Arg('v', false, NULL, + "Forces Parsee to print in a more verbose fashion " + "(-vv prints stanzas to stderr)" + ) Arg('h', false, NULL,"Generates an help screen(this one!)") EndOfArgs @@ -63,6 +66,7 @@ Main(Array *args, HashMap *env) char *configuration = "parsee.json"; int xmpp = 8; int http = 8; + int verbose = 0; start = UtilTsMillis(); @@ -78,10 +82,7 @@ Main(Array *args, HashMap *env) ArgParseState state; char *opts = ParseeGenerateGetopt(arguments); int flag; - ArgParseStateInit(&state); - /* TODO: Have a smarter way of generating the arg table - * (with a list of structs, with a description and everything) */ while ((flag = ArgParse(&state, args, opts)) != -1) { switch (flag) @@ -105,7 +106,26 @@ Main(Array *args, HashMap *env) Free(opts); goto end; case 'v': - LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG); + switch (++verbose) + { + case PARSEE_VERBOSE_LOG: + LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG); + break; + case PARSEE_VERBOSE_STANZA: + Log(LOG_DEBUG, "Enabling stanza printing."); + break; + case PARSEE_VERBOSE_COMICAL: + Log(LOG_DEBUG, "What?"); + Log(LOG_DEBUG, "No, but like, what do you except?"); + Log(LOG_DEBUG, "Like do you want to log _every_ instruction?"); + Log(LOG_DEBUG, "Like just every single thing %s does?", NAME); + Log(LOG_DEBUG, " ( why??? )"); + Log(LOG_DEBUG, "....................................."); + Log(LOG_DEBUG, "Argh."); + Log(LOG_DEBUG, "Alright. I'll do my best."); + Log(LOG_DEBUG, "Get what you paid for."); + break; + } break; case 'C': if (!UtilLastModified(state.optArg)) @@ -126,6 +146,10 @@ Main(Array *args, HashMap *env) ParseeSetThreads(xmpp, http); } + if (verbose >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Loading configuration..."); + } ParseeConfigLoad(configuration); ParseeConfigInit(); parsee_conf = ParseeConfigGet(); @@ -142,14 +166,38 @@ Main(Array *args, HashMap *env) )) { Log(LOG_ERR, "Could not connect to XMPP..."); + + if (verbose >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Destroying component..."); + } XMPPEndCompStream(jabber); goto end; } Log(LOG_NOTICE, "Creating volatile tables..."); + if (verbose >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Initialising JID table"); + } ParseeInitialiseJIDTable(); + + if (verbose >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Initialising OID table"); + } ParseeInitialiseOIDTable(); + + if (verbose >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Initialising head table"); + } ParseeInitialiseHeadTable(); + + if (verbose >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Initialising nick table"); + } ParseeInitialiseNickTable(); conf.port = parsee_conf->port; @@ -158,6 +206,9 @@ Main(Array *args, HashMap *env) conf.handlerArgs = ParseeInitData(jabber); conf.handler = ParseeRequest; + Log(LOG_DEBUG, "Verbosity level: %d", verbose); + ((ParseeData *) conf.handlerArgs)->verbosity = verbose; + Log(LOG_NOTICE, "Setting up local Matrix user..."); if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart)) { diff --git a/src/Parsee/Config.c b/src/Parsee/Config.c index 4bad59f..f1a14b6 100644 --- a/src/Parsee/Config.c +++ b/src/Parsee/Config.c @@ -109,7 +109,6 @@ PromptInteger(const char *expression, int def, ...) return l; } -/* TODO: Memleaks, galore! */ void ParseeConfigInit(void) { @@ -120,8 +119,7 @@ ParseeConfigInit(void) return; } - /* TODO: Give the user an achievement at the end, just because they're - * cool. */ + /* TODO: Get rid of this, as parsee-config is the main way of doing it */ Log(LOG_NOTICE, "It seems like it is the first time you have configured "); Log(LOG_NOTICE, "Parsee."); Log(LOG_NOTICE, "As such, I need to ask you a couple of questions before "); @@ -137,7 +135,6 @@ ParseeConfigInit(void) config->xmpp_threads = 8; config->db_size = 64 MB; - /* TODO: This is NOT user friendly, and I know it! */ config->sender_localpart = PromptString( "Name of the bridge bot, used for commands and bridged rooms", "_parsee_bridge" @@ -150,7 +147,7 @@ ParseeConfigInit(void) config->listen_as = StrDuplicate("localhost"); config->port = PromptInteger( "Matrix port for the AS service to use", - 7642 /* proposed by Saint */ + 7642 ); @@ -185,7 +182,6 @@ ParseeConfigInit(void) NULL ); - /* TODO: Make that configurable. */ config->server_base = StrDuplicate(config->homeserver_host); Log(LOG_NOTICE, "Done! Please look over to the parsee.yaml file, "); diff --git a/src/Parsee/User.c b/src/Parsee/User.c index 42cfb1b..f1ac101 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -96,7 +96,7 @@ ParseeDecodeLocalJID(const ParseeConfig *c, char *mxid) data_start = jid_flags; while (*data_start && *data_start != '_') { - /* TODO: Make this a macro */ + /* TODO: Get rid of this */ if (*data_start == 'l') { plain_jid = true; @@ -126,7 +126,7 @@ ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias) data_start = jid_flags; while (*data_start && *data_start != '_') { - /* TODO: Make this a macro */ + /* TODO: Get rid of this */ if (*data_start == 'm') { plain_jid = true; @@ -236,7 +236,6 @@ ParseeEncodeMXID(char *mxid) { char src = mxid[i]; - /* TODO: More robust system */ if (src <= 0x20 || (src == '"' || src == '&' || src == '\'' || src == '/' || @@ -685,7 +684,6 @@ ParseeToUnauth(ParseeData *data, char *mxc) return NULL; } - /* TODO: HTTPS */ l = snprintf(NULL, 0, PAT, data->config->media_base, diff --git a/src/Routes/Ping.c b/src/Routes/Ping.c deleted file mode 100644 index 88dc533..0000000 --- a/src/Routes/Ping.c +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#include - -#include -#include - -RouteHead(RoutePing, arr, argp) -{ - ParseeHttpArg *args = argp; - HashMap *request = NULL; - HashMap *response = NULL; - - response = ASVerifyRequest(args); - if (response) - { - goto end; - } - if (HttpRequestMethodGet(args->ctx) != HTTP_POST) - { - HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED); - response = MatrixCreateError( - "M_UNRECOGNIZED", - "Path /ping only accepts POST as a valid method." - ); - goto end; - } - Log(LOG_INFO, "Pong!"); - - RequestJSON(); - - /* TODO: Load ping info */ - response = HashMapCreate(); -end: - JsonFree(request); - return response; -} diff --git a/src/Routes/Transactions.c b/src/Routes/Transactions.c index 7987f50..27e7393 100644 --- a/src/Routes/Transactions.c +++ b/src/Routes/Transactions.c @@ -30,7 +30,6 @@ RouteHead(RouteTxns, arr, argp) RequestJSON(); - /* TODO: Do a thing with these. */ events = JsonValueAsArray(HashMapGet(request, "events")); for (i = 0; i < ArraySize(events); i++) { diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index d646436..71f617a 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -102,7 +102,6 @@ ComputeHandshake(char *shared, char *stream) bool XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared) { - /* TODO */ XMLexer *sax; XMLEvent *ev; char *stream_id, *handshake; @@ -176,7 +175,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared) ret = true; /* We can uhh, send stanzas, and receive them! */ - Log(LOG_INFO, "Communications to '%s' established.", as); + Log(LOG_DEBUG, "Communications to '%s' established.", as); XMLFreeEvent(ev); Free(stream_id); diff --git a/src/XMPPThread/Bridged.c b/src/XMPPThread/Bridged.c index 69f9033..7ca364c 100644 --- a/src/XMPPThread/Bridged.c +++ b/src/XMPPThread/Bridged.c @@ -118,7 +118,6 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza) return ret; } -/* TODO: Cache this information. */ bool ServerHasXEP421(ParseeData *data, char *from) { diff --git a/src/XMPPThread/PEP.c b/src/XMPPThread/PEP.c index 1dc36ea..be60096 100644 --- a/src/XMPPThread/PEP.c +++ b/src/XMPPThread/PEP.c @@ -7,6 +7,33 @@ #include +#include +#include + +XMLElement * +CreatePubsubRequest(char *from, char *to, char *node) +{ + XMLElement *iq_req, *pubsub, *sub; + char *id; + iq_req = XMLCreateTag("iq"); + XMLAddAttr(iq_req, "from", from); + XMLAddAttr(iq_req, "to", to); + XMLAddAttr(iq_req, "id", (id = StrRandom(16))); + XMLAddAttr(iq_req, "type", "set"); + + pubsub = XMLCreateTag("pubsub"); + XMLAddAttr(pubsub, "xmlns", "http://jabber.org/protocol/pubsub"); + XMLAddChild(iq_req, pubsub); + + sub = XMLCreateTag("subscribe"); + XMLAddAttr(sub, "node", node); + XMLAddAttr(sub, "jid", from); + XMLAddChild(pubsub, sub); + + Free(id); + return iq_req; +} + struct PEPManager { pthread_mutex_t lock; diff --git a/src/XMPPThread/Pubsub.c b/src/XMPPThread/Pubsub.c deleted file mode 100644 index c2ec8af..0000000 --- a/src/XMPPThread/Pubsub.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "XMPPThread/internal.h" - -#include -#include - -#include -#include - -/* TODO: This should be in PEP.c */ -XMLElement * -CreatePubsubRequest(char *from, char *to, char *node) -{ - XMLElement *iq_req, *pubsub, *sub; - char *id; - iq_req = XMLCreateTag("iq"); - XMLAddAttr(iq_req, "from", from); - XMLAddAttr(iq_req, "to", to); - XMLAddAttr(iq_req, "id", (id = StrRandom(16))); - XMLAddAttr(iq_req, "type", "set"); - - pubsub = XMLCreateTag("pubsub"); - XMLAddAttr(pubsub, "xmlns", "http://jabber.org/protocol/pubsub"); - XMLAddChild(iq_req, pubsub); - - sub = XMLCreateTag("subscribe"); - XMLAddAttr(sub, "node", node); - XMLAddAttr(sub, "jid", from); - XMLAddChild(pubsub, sub); - - Free(id); - return iq_req; -} diff --git a/src/XMPPThread/ReListener.c b/src/XMPPThread/ReListener.c index 0641f42..914b774 100644 --- a/src/XMPPThread/ReListener.c +++ b/src/XMPPThread/ReListener.c @@ -58,6 +58,11 @@ XMPPDispatcher(void *argp) continue; } + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name); + } + if (StrEquals(stanza->name, "presence")) { PresenceStanza(args, stanza); @@ -68,6 +73,10 @@ XMPPDispatcher(void *argp) { if (!MessageStanza(args, stanza, thread)) { + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Done receiving message with an error"); + } continue; } } @@ -82,6 +91,11 @@ XMPPDispatcher(void *argp) StreamPrintf(StreamStdout(), "\n"); StreamFlush(StreamStdout()); } + + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "Done receiving stanza='%s'.", stanza->name); + } XMLFreeElement(stanza); } @@ -180,9 +194,22 @@ ParseeXMPPThread(void *argp) stanza = XMLDecode(jabber->stream, false); if (!stanza) { + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "RECEIVED EOF."); + } break; } + if (args->verbosity >= PARSEE_VERBOSE_STANZA) + { + Stream *output = StreamStderr(); + StreamPrintf(output, "-------STANZA BEGIN-------" "\n"); + XMLEncode(output, stanza); + StreamPrintf(output, "\n--------STANZA END--------" "\n"); + StreamFlush(output); + } + id = HashMapGet(stanza->attrs, "id"); if (id) { @@ -271,7 +298,7 @@ ParseeAwaitStanza(char *identifier, int64_t timeout) { return NULL; } - + pthread_mutex_lock(&await_lock); if (HashMapGet(await_table, identifier)) { diff --git a/src/XMPPThread/Stanzas/IQ.c b/src/XMPPThread/Stanzas/IQ.c index 069fba5..ac855a4 100644 --- a/src/XMPPThread/Stanzas/IQ.c +++ b/src/XMPPThread/Stanzas/IQ.c @@ -120,7 +120,6 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) { XMLElement *vcard = XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"); - /* TODO: Move this to PEP */ XMLElement *event = XMLookForTKV(stanza, "pubsub", "xmlns", "http://jabber.org/protocol/pubsub" @@ -169,12 +168,16 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) DbUnlock(args->db, avatars); return; } + JsonValueFree(JsonSet( + json, JsonValueString(id), + 1, from + )); + DbUnlock(args->db, avatars); base64 = TrimBase64(data->data); b64len = base64 ? strlen(base64) : 0; length = Base64DecodedSize(base64, b64len); - /* TODO: Bound checks for a size limit. */ bdata = (char *) Base64Decode(base64, b64len); datastream = StrStreamReaderN(bdata, length); @@ -185,12 +188,6 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) from_matrix = ParseeGetBridgedUser(args, stanza); ASSetAvatar(args->config, from_matrix, mxc); - JsonValueFree(JsonSet( - json, JsonValueString(id), - 1, from - )); - DbUnlock(args->db, avatars); - Free(mxc); Free(jid); Free(bdata); diff --git a/src/XMPPThread/Stanzas/Presence.c b/src/XMPPThread/Stanzas/Presence.c index 1a8e298..dd592f0 100644 --- a/src/XMPPThread/Stanzas/Presence.c +++ b/src/XMPPThread/Stanzas/Presence.c @@ -95,7 +95,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) if (!real_matrix || *real_matrix != '@') { Free(real_matrix); - /*real_matrix = ParseeEncodeJID(args->config, decode_from, false);*/ real_matrix = ParseeGetBridgedUser(args, stanza); } @@ -109,8 +108,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) } else if (StrEquals(affiliation, "member")) { - /* TODO: Reconsider setting the PL if a member, as there's no - * clear reason to do this in some cases. */ power_level = 0; } else if (StrEquals(affiliation, "outcast")) @@ -139,7 +136,7 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) } /* Set the user's PL - * TODO: Do NOT change the PL of *real* people nilly-willy. + * NOTE: Do NOT change the PL of *real* people nilly-willy. * In some scenarios, this is really bad behaviour. */ if (room) { @@ -151,7 +148,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) * lacking any checking. (--gen) */ if (StrEquals(parsee, matrix_user_pl)) { - /* TODO: Give the user an achievement, this is goofy. */ Log(LOG_ERR, "BAD PL DOWNGRADE (%d->%d)", level, power_level); } if (users && level != power_level && @@ -239,6 +235,13 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) { return; } + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, + "Looking at VCard in presence from %s", + oid + ); + } avatars = DbLock(args->db, 1, "avatars"); if (!avatars) @@ -246,18 +249,32 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) avatars = DbCreate(args->db, 1, "avatars"); } json = DbJson(avatars); + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "VCard: Locked the database!"); + } avatar_id = GrabString(json, 1, oid); if (avatar_id && StrEquals(avatar_id, p_dat->data)) { + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, + "VCard: %s is already cached for %s", avatar_id, oid + ); + } DbUnlock(args->db, avatars); return; } + + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "VCard: %s for %s", p_dat->data, oid); + } JsonValueFree(JsonSet( json, JsonValueString(p_dat->data), 1, oid) ); - DbUnlock(args->db, avatars); from = ParseeJID(args); @@ -273,8 +290,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) XMLFreeElement(vcard_request); Free(from); } - - /* TODO: Sending VCard on presence is slow and stalls the thread */ #undef MUC_USER_NS } diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 5af946a..e27fc76 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -17,6 +17,11 @@ #include #include +#define PARSEE_VERBOSE_NONE 0 +#define PARSEE_VERBOSE_LOG 1 +#define PARSEE_VERBOSE_STANZA 2 +#define PARSEE_VERBOSE_COMICAL 53 /* MINUTES */ + typedef struct ParseeConfig { /* -------- MATRIX -------- */ char *as_token, *hs_token; @@ -56,6 +61,8 @@ typedef struct ParseeData { Db *db; char *id; + + int verbosity; } ParseeData; typedef struct Argument { diff --git a/src/include/Routes.h b/src/include/Routes.h index 0baf81b..369a0d7 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -72,7 +72,6 @@ typedef struct ParseeCmdArg { /* A list of all routes. */ #define ROUTES X_ROUTE("/", RouteRoot) \ 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/client/v1/media/download/(.*)/(.*)", RouteMedia) diff --git a/tools/noavatars.c b/tools/noavatars.c new file mode 100644 index 0000000..e8ef0f4 --- /dev/null +++ b/tools/noavatars.c @@ -0,0 +1,39 @@ +/* noavatars.c - Clean up the avatar cache + * ============================================================ + * Under CC0, as its a rather useful example of a Parsee tool. + * See LICENSE for more information about Parsee's licensing. */ + +#include +#include +#include +#include + +#include + +int +Main(Array *args, HashMap *env) +{ + char *db_path, *exec; + Db *parsee; + + exec = ArrayGet(args, 0); + + if (ArraySize(args) < 2) + { + Log(LOG_ERR, "Usage: %s [DB path]", exec); + return EXIT_FAILURE; + } + + db_path = ArrayGet(args, 1); + + parsee = DbOpenLMDB(db_path, 8 * 1024 * 1024); + if (parsee) + { + DbDelete(parsee, 1, "avatars"); + DbClose(parsee); + return EXIT_SUCCESS; + } + + Log(LOG_ERR, "%s: couldn't open DB '%s'", exec, db_path); + return EXIT_FAILURE; +} From 39cc04fc2eee8ee1f961169f47af81991d954ecd Mon Sep 17 00:00:00 2001 From: LDA Date: Sun, 25 Aug 2024 23:00:31 +0200 Subject: [PATCH 017/220] [MOD] HMAC-based mediachecking --- LICENSE | 4 +-- src/MatrixEventHandler.c | 4 +-- src/Parsee/HMAC.c | 76 ++++++++++++++++++++++++++++++++++++++++ src/Parsee/User.c | 14 ++++++-- src/Routes/Media.c | 18 ++++++++-- src/include/Parsee.h | 8 +++++ 6 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 src/Parsee/HMAC.c diff --git a/LICENSE b/LICENSE index 5d9765c..1e1282b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -For the files src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile, see COPYING.CC0 -to be present. +For the files src/Parsee/HMAC.c, src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile, +see COPYING.CC0. For any other file in src/, see COPYING.AGPL as the primary license. As Parsee depends on Cytoplasm, its license is left here in COPYING.CYTO diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index effcc45..247f907 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -25,8 +25,8 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name) while (!XMPPJoinMUC(data->jabber, jid, rev, true) && nonce < 32) { char *nonce_str = StrInt(nonce); - char *input = StrConcat(4, sender, name, data->id, nonce_str); - char *hex = ParseeSHA256(input); + char *input = StrConcat(3, sender, name, nonce_str); + char *hex = ParseeHMACS(data->id, input); if (strlen(hex) >= 8) { diff --git a/src/Parsee/HMAC.c b/src/Parsee/HMAC.c new file mode 100644 index 0000000..3904ebc --- /dev/null +++ b/src/Parsee/HMAC.c @@ -0,0 +1,76 @@ +/* CC0 implementation of a HMAC system based on Cytoplasm. + * Ignore the scary "Parsee.h", its practically unused. */ +#include + +#include +#include +#include + +#include + +/* A 64-byte key! */ +static uint8_t * +ComputeKPad(char *key, uint8_t pad) +{ + size_t klen; + uint8_t *kp; + uint8_t *kopad; + size_t i; + if ((klen = strlen(key)) <= 64) + { + kp = Malloc(klen * sizeof(uint8_t)); + memcpy(kp, key, klen); + } + else + { + kp = (uint8_t *) Sha256(key); + klen = 32; + } + + /* Now that we have K', lets compute it XORd with opad */ + kopad = Malloc(64 * sizeof(uint8_t)); + for (i = 0; i < 64; i++) + { + uint8_t byte = i < klen ? kp[i] : 0x00; + kopad[i] = byte ^ pad; + } + + Free(kp); + return kopad; +} + +char * +ParseeHMAC(char *key, uint8_t *msg, size_t msglen) +{ + uint8_t *opad, *ipad; + uint8_t *innersha; + uint8_t *outer; + unsigned char *sha; + char *str; + if (!key || !msg || !msglen) + { + return NULL; + } + + opad = ComputeKPad(key, 0x5C); + + ipad = ComputeKPad(key, 0x36); + ipad = Realloc(ipad, 64 + msglen); + memcpy(ipad + 64, msg, msglen); + innersha = Sha256Raw(ipad, 64 + msglen); + + outer = Malloc(64 + 32); + memcpy(outer, opad, 64); + memcpy(outer + 64, innersha, 32); + + sha = Sha256Raw(outer, 64 + 32); + str = ShaToHex(sha, HASH_SHA256); + + Free(innersha); + Free(outer); + Free(ipad); + Free(opad); + Free(sha); + + return str; +} diff --git a/src/Parsee/User.c b/src/Parsee/User.c index f1ac101..79c8a55 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -667,7 +667,8 @@ ParseeToUnauth(ParseeData *data, char *mxc) { Uri *url = NULL; char *ret; -#define PAT "%s/_matrix/client/v1/media/download/%s%s" + char *key, *hmac; +#define PAT "%s/_matrix/client/v1/media/download/%s%s?hmac=%s" size_t l; if (!data || !mxc) { @@ -684,18 +685,25 @@ ParseeToUnauth(ParseeData *data, char *mxc) return NULL; } + key = StrConcat(2, url->host, url->path); + hmac = ParseeHMACS(data->id, key); + Free(key); + l = snprintf(NULL, 0, PAT, data->config->media_base, - url->host, url->path + url->host, url->path, + hmac ); ret = Malloc(l + 3); snprintf(ret, l + 1, PAT, data->config->media_base, - url->host, url->path + url->host, url->path, + hmac ); UriFree(url); + Free(hmac); return ret; } diff --git a/src/Routes/Media.c b/src/Routes/Media.c index 45d79d0..137ef0f 100644 --- a/src/Routes/Media.c +++ b/src/Routes/Media.c @@ -2,27 +2,41 @@ #include #include +#include #include #include #include +#include + RouteHead(RouteMedia, arr, argp) { ParseeHttpArg *args = argp; HttpClientContext *cctx; - HashMap *reqh; + HashMap *reqh, *params; char *server = ArrayGet(arr, 0); char *identi = ArrayGet(arr, 1); char *path, *key, *val; + char *hmac, *chkmak = NULL; + + params = HttpRequestParams(args->ctx); + hmac = HashMapGet(params, "hmac"); /* TODO: Make it check the DB for its validicity. "Purging" would be useful. */ - if (!server || !identi) { + char *concat = StrConcat(3, server, "/", identi); + chkmak = ParseeHMACS(args->data->id, concat); + Free(concat); + } + if (!server || !identi || !hmac || !StrEquals(hmac, chkmak)) + { + Free(chkmak); HttpResponseStatus(args->ctx, HTTP_BAD_REQUEST); return MatrixCreateError("M_NOT_YET_UPLOADED", "No server/identifier"); } + Free(chkmak); server = HttpUrlEncode(server); identi = HttpUrlEncode(identi); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index e27fc76..8182675 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -361,4 +361,12 @@ extern char * ParseeGenerateMTO(char *common_id); * Modifies: the Parsee config */ extern void ParseeSetThreads(int xmpp, int http); +/** Computes an HMAC (with SHA-256) with a known key, and a generic message. + * -------------- + * Returns: a hex representation of the HMAC[HEAP]. + * Thrasher: Free + * Modifies: NOTHING */ +extern char * ParseeHMAC(char *key, uint8_t *msg, size_t msglen); +#define ParseeHMACS(key, msg) ParseeHMAC(key, (uint8_t *) msg, strlen(msg)) + #endif From 55674c369be3dadf1bd2a64cfeb209705ee52d46 Mon Sep 17 00:00:00 2001 From: LDA Date: Mon, 26 Aug 2024 21:07:55 +0200 Subject: [PATCH 018/220] [MOD] Avoid allocating memory in HMAC --- Makefile | 2 +- src/Main.c | 9 +++++---- src/Parsee/HMAC.c | 29 ++++++++++++----------------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 82d651a..c76e361 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ all: binary utils binary: $(OBJ_FILES) $(CC) $(LDFLAGS) $(OBJ_FILES) -o $(BINARY) -tags: +tags: $(SRC_FILES) @ctags --recurse $(SOURCE)/ clean: diff --git a/src/Main.c b/src/Main.c index 3303db1..fd599ba 100644 --- a/src/Main.c +++ b/src/Main.c @@ -42,12 +42,13 @@ static const Argument arguments[] = Arg('J', true, "number(=8)", "Sets the number of XMPP threads") Arg('C', true, "file(='parsee.json')", "Sets the JSON config to use") - Arg('g', false, NULL,"Generates a parsee.yaml AS file before exiting") + Arg('g', false, NULL, + "Generates a parsee.yaml AS file before exiting") Arg('v', false, NULL, "Forces Parsee to print in a more verbose fashion " - "(-vv prints stanzas to stderr)" - ) - Arg('h', false, NULL,"Generates an help screen(this one!)") + "(-vv prints stanzas to stderr)") + Arg('h', false, NULL, + "Generates an help screen(this one!)") EndOfArgs diff --git a/src/Parsee/HMAC.c b/src/Parsee/HMAC.c index 3904ebc..efdd09a 100644 --- a/src/Parsee/HMAC.c +++ b/src/Parsee/HMAC.c @@ -8,13 +8,11 @@ #include -/* A 64-byte key! */ -static uint8_t * -ComputeKPad(char *key, uint8_t pad) +static void +ComputeKPad(char *key, uint8_t pad, uint8_t *kopad) { size_t klen; uint8_t *kp; - uint8_t *kopad; size_t i; if ((klen = strlen(key)) <= 64) { @@ -28,7 +26,6 @@ ComputeKPad(char *key, uint8_t pad) } /* Now that we have K', lets compute it XORd with opad */ - kopad = Malloc(64 * sizeof(uint8_t)); for (i = 0; i < 64; i++) { uint8_t byte = i < klen ? kp[i] : 0x00; @@ -36,41 +33,39 @@ ComputeKPad(char *key, uint8_t pad) } Free(kp); - return kopad; } char * ParseeHMAC(char *key, uint8_t *msg, size_t msglen) { - uint8_t *opad, *ipad; + uint8_t opad[64], ipad[64 + msglen]; + uint8_t outer[64 + 32]; + uint8_t *innersha; - uint8_t *outer; - unsigned char *sha; + uint8_t *sha; char *str; if (!key || !msg || !msglen) { return NULL; } - opad = ComputeKPad(key, 0x5C); + /* Initialise K' XOR opad and K' XOR ipad */ + ComputeKPad(key, 0x5C, opad); + ComputeKPad(key, 0x36, ipad); - ipad = ComputeKPad(key, 0x36); - ipad = Realloc(ipad, 64 + msglen); + /* Compute H((K' XOR ipad) || msg) */ memcpy(ipad + 64, msg, msglen); innersha = Sha256Raw(ipad, 64 + msglen); - outer = Malloc(64 + 32); + /* Compute (K' XOR opad) || H((K' XOR ipad) || msg) */ memcpy(outer, opad, 64); memcpy(outer + 64, innersha, 32); + /* Compute H((K' XOR opad) || H((K' XOR ipad) || msg)) */ sha = Sha256Raw(outer, 64 + 32); str = ShaToHex(sha, HASH_SHA256); Free(innersha); - Free(outer); - Free(ipad); - Free(opad); Free(sha); - return str; } From 3366fcb7598f944c868628a24e67840e284c7060 Mon Sep 17 00:00:00 2001 From: LDA Date: Thu, 29 Aug 2024 15:31:11 +0200 Subject: [PATCH 019/220] [MOD] Be stringent about OIDs, use fmemopen --- src/AS/Media.c | 1 + src/Streams/Reader.c | 114 +----------------------------- src/XMPPCommands/AddAdmin.c | 1 - src/XMPPThread/Bridged.c | 3 +- src/XMPPThread/Stanzas/IQ.c | 29 +++++++- src/XMPPThread/Stanzas/Message.c | 21 ++++++ src/XMPPThread/Stanzas/Presence.c | 27 +++++-- src/XMPPThread/internal.h | 2 + 8 files changed, 76 insertions(+), 122 deletions(-) diff --git a/src/AS/Media.c b/src/AS/Media.c index 4e642e9..31c7231 100644 --- a/src/AS/Media.c +++ b/src/AS/Media.c @@ -19,6 +19,7 @@ ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime) HashMap *reply; if (!c || !from) { + Log(LOG_ERR, "No ASUpload input (c=%p from=%p)", c, from); return NULL; } diff --git a/src/Streams/Reader.c b/src/Streams/Reader.c index 19fcefe..2c0613a 100644 --- a/src/Streams/Reader.c +++ b/src/Streams/Reader.c @@ -7,125 +7,13 @@ #include -typedef struct ReaderCookie { - size_t length; - size_t offset; - char *buffer; -} ReaderCookie; - -#define Remaining() (cook->length - cook->offset) - -static ssize_t -ReadStreamReader(void *coop, void *to, size_t n) -{ - ReaderCookie *cook = coop; - size_t remaining; - if (!cook) - { - return 0; - } - - remaining = Remaining(); - if (n > remaining) - { - memcpy(to, cook->buffer + cook->offset, remaining); - cook->offset = cook->length; - return remaining; - } - - memcpy(to, cook->buffer + cook->offset, n); - cook->offset += n; - return n; -} -static ssize_t -WriteStreamReader(void *coop, void *from, size_t n) -{ - /* Writing to a stream reader is silly. */ - return 0; -} -static off_t -SeekStreamReader(void *coop, off_t mag, int sgn) -{ - ReaderCookie *cook = coop; - if (!cook) - { - return 0; - } - - switch (sgn) - { - case SEEK_SET: - if (mag > cook->length) - { - cook->offset = cook->length; - return 0; - } - else if (mag < 0) - { - cook->offset = 0; - return 0; - } - cook->offset = mag; - return 0; - case SEEK_CUR: - cook->offset += mag; - if (cook->offset > cook->length) - { - cook->offset = cook->length; - } - else if (cook->offset < 0) - { - cook->offset = 0; - } - return 0; - case SEEK_END: - cook->offset += cook->length + mag; - if (cook->offset > cook->length) - { - cook->offset = cook->length; - } - else if (cook->offset < 0) - { - cook->offset = 0; - } - return 0; - - } - return 0; -} - -static int -CloseStreamReader(void *coop) -{ - /* Nothing to free as of now. */ - if (coop) - { - Free(coop); - } - return 0; -} - -const static IoFunctions Functions = { - .read = ReadStreamReader, - .seek = SeekStreamReader, - .write = WriteStreamReader, - .close = CloseStreamReader, -}; - Stream * StrStreamReaderN(char *buffer, int n) { - Io *raw_io; - ReaderCookie *cookie; if (!buffer) { return NULL; } - cookie = Malloc(sizeof(*cookie)); - cookie->buffer = buffer; - cookie->length = n ? n : strlen(buffer); - cookie->offset = 0; - raw_io = IoCreate(cookie, Functions); - return StreamIo(raw_io); + return StreamFile(fmemopen(buffer, n ? n : strlen(buffer), "rb")); } diff --git a/src/XMPPCommands/AddAdmin.c b/src/XMPPCommands/AddAdmin.c index a9bd02b..321d18d 100644 --- a/src/XMPPCommands/AddAdmin.c +++ b/src/XMPPCommands/AddAdmin.c @@ -43,7 +43,6 @@ AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement ref = DbLock(data->db, 1, "admins"); admins = GrabArray(DbJson(ref), 1, "admins"); - ArrayAdd(admins, JsonValueString(glob)); DbUnlock(data->db, ref); } diff --git a/src/XMPPThread/Bridged.c b/src/XMPPThread/Bridged.c index 7ca364c..5ec2b27 100644 --- a/src/XMPPThread/Bridged.c +++ b/src/XMPPThread/Bridged.c @@ -161,7 +161,7 @@ ServerHasXEP421(ParseeData *data, char *from) * into a SHA-256 value * > "The recipient MUST consider the occupant identifier to be an opaque * > string.". */ -static char * +char * ScrambleOID(ParseeData *data, char *opaque_oid) { char *sha, *mxid; @@ -226,6 +226,7 @@ ParseeGetBridgedUserI(ParseeData *data, XMLElement *stanza, char *force) { ParseePushOIDTable(xmpp_from, occ_id); } + Log(LOG_DEBUG, "Trying Occ ID for %s{%s}", xmpp_from, occ_id); } if (!occ_id) diff --git a/src/XMPPThread/Stanzas/IQ.c b/src/XMPPThread/Stanzas/IQ.c index ac855a4..ebfd84e 100644 --- a/src/XMPPThread/Stanzas/IQ.c +++ b/src/XMPPThread/Stanzas/IQ.c @@ -186,6 +186,10 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) jid = ParseeLookupJID(from); from_matrix = ParseeGetBridgedUser(args, stanza); + Log(LOG_DEBUG, + "Setting the avatar of '%s' to %s", + from_matrix, mxc + ); ASSetAvatar(args->config, from_matrix, mxc); Free(mxc); @@ -224,12 +228,13 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) /* Get the base64, decode it, and shove it in a nicely put * MXC address */ - base64 = data->data; + base64 = TrimBase64(data->data); b64len = base64 ? strlen(base64) : 0; length = Base64DecodedSize(base64, b64len); bdata = Base64Decode(base64, b64len); datastream = StrStreamReaderN(bdata, length); + Log(LOG_DEBUG, "(%dB @ %p) = %p (%d)", b64len, base64, bdata, length); mxc = ASUpload( args->config, datastream, @@ -237,6 +242,7 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) tdata ? tdata->data : NULL ); Free(bdata); + Free(base64); StreamClose(datastream); room = ParseeGetBridgedRoom(args, stanza); @@ -247,8 +253,27 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) /* TODO: More reliable system for telling the difference appart */ if (jid && !StrEquals(from, resource)) { - from_matrix = ParseeGetBridgedUserI(args, stanza, jid); + char *oid = ParseeLookupOID(jid); + char *hsh = ParseeSHA256(oid); + bool scrambled = + oid && StrEquals(jid, HashMapGet(stanza->attrs, "from")); + from_matrix = scrambled + ? ScrambleOID(args, oid) + : ParseeGetBridgedUser(args, stanza); + + Log(LOG_DEBUG, + "Setting the vCard of '%s'(%s) to %s (%d B)", + from_matrix, jid, mxc, + length + ); + Log(LOG_DEBUG, + "OID=%s[%s]", + oid, hsh + ); + ASSetAvatar(args->config, from_matrix, mxc); + Free(oid); + Free(hsh); } else if (room) { diff --git a/src/XMPPThread/Stanzas/Message.c b/src/XMPPThread/Stanzas/Message.c index 230131d..f7230fb 100644 --- a/src/XMPPThread/Stanzas/Message.c +++ b/src/XMPPThread/Stanzas/Message.c @@ -88,14 +88,33 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) from = NULL; decode_from = NULL; from_matrix = NULL; + Log(LOG_DEBUG, " usage=%d", MemoryAllocated()); from = HashMapGet(stanza->attrs, "from"); if (ParseeManageBan(args, from, NULL)) { XMLFreeElement(stanza); + Log(LOG_DEBUG, " usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__); return false; } + if (ServerHasXEP421(args, from)) + { + XMLElement *occupant = XMLookForTKV( + stanza, "occupant-id", + "xmlns", "urn:xmpp:occupant-id:0" + ); + char *occ_id = occupant ? HashMapGet(occupant->attrs, "id") : NULL; + if (occ_id) + { + Log(LOG_DEBUG, + "'%s' has support for XEP-421, fetching OID=%s", + from, occ_id + ); + ParseePushOIDTable(from, occ_id); + } + } + if (StrEquals(type, "error")) { char *type, *text, *user, *parsee; @@ -132,6 +151,7 @@ end_error: Free(parsee); Free(room); Free(user); + Log(LOG_DEBUG, " usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__); return false; } @@ -402,6 +422,7 @@ end: Free(decode_from); Free(room); Free(to); + Log(LOG_DEBUG, " usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__); return true; } diff --git a/src/XMPPThread/Stanzas/Presence.c b/src/XMPPThread/Stanzas/Presence.c index dd592f0..225a361 100644 --- a/src/XMPPThread/Stanzas/Presence.c +++ b/src/XMPPThread/Stanzas/Presence.c @@ -63,8 +63,26 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) XMLElement *user_info; XMLElement *vc = XMLookForTKV(stanza, "x", "xmlns", "vcard-temp:x:update"); XMLElement *status = XMLookForUnique(stanza, "status"); + char *oid = HashMapGet(stanza->attrs, "from"); + if (ServerHasXEP421(args, oid)) + { + XMLElement *occupant = XMLookForTKV( + stanza, "occupant-id", + "xmlns", "urn:xmpp:occupant-id:0" + ); + char *occ_id = occupant ? HashMapGet(occupant->attrs, "id") : NULL; + if (occ_id) + { + Log(LOG_DEBUG, + "'%s' has support for XEP-421, fetching OID=%s", + oid, occ_id + ); + ParseePushOIDTable(oid, occ_id); + } + } + if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS))) { XMLElement *item = XMLookForUnique(user_info, "item"); @@ -259,7 +277,7 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) { if (args->verbosity >= PARSEE_VERBOSE_COMICAL) { - Log(LOG_DEBUG, + Log(LOG_WARNING, "VCard: %s is already cached for %s", avatar_id, oid ); } @@ -267,10 +285,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) return; } - if (args->verbosity >= PARSEE_VERBOSE_COMICAL) - { - Log(LOG_DEBUG, "VCard: %s for %s", p_dat->data, oid); - } JsonValueFree(JsonSet( json, JsonValueString(p_dat->data), 1, oid) @@ -279,6 +293,9 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) from = ParseeJID(args); + Log(LOG_DEBUG, + "Sending a vCard avatar request for %s(=%s)", oid, p_dat->data + ); vcard_request = CreateVCardRequest( from, HashMapGet(stanza->attrs, "from") ); diff --git a/src/XMPPThread/internal.h b/src/XMPPThread/internal.h index c24b528..d937b8a 100644 --- a/src/XMPPThread/internal.h +++ b/src/XMPPThread/internal.h @@ -94,3 +94,5 @@ void DestroyPEPManager(PEPManager *manager); /* PEP callbacks for the handler */ void PEPAvatarEvent(PEPManager *m, XMLElement *stanza, XMLElement *item); void PEPVCardEvent(PEPManager *m, XMLElement *stanza, XMLElement *item); + +char * ScrambleOID(ParseeData *data, char *opaque_oid); From 063314b0816acb8443b5db42130ff5806b7ff673 Mon Sep 17 00:00:00 2001 From: LDA Date: Thu, 29 Aug 2024 23:47:31 +0200 Subject: [PATCH 020/220] [MOD] Cache XEP-0421 support --- src/Parsee/Data.c | 5 +++++ src/Routes/Root.c | 8 +++++++- src/XMPPThread/Bridged.c | 20 ++++++++++++++++++-- src/include/Parsee.h | 3 +++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index 5a3257c..bcfd7e3 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -27,6 +27,9 @@ ParseeInitData(XMPPComponent *comp) data->jabber = comp; data->handler = CommandCreateRouter(); + data->oid_servers = HashMapCreate(); + pthread_mutex_init(&data->oidl, NULL); + if (data->config->db_size) { data->db = DbOpenLMDB(data->config->db_path, data->config->db_size); @@ -71,6 +74,8 @@ ParseeFreeData(ParseeData *data) return; } + HashMapFree(data->oid_servers); + pthread_mutex_destroy(&data->oidl); Free(data->id); XMPPEndCompStream(data->jabber); DbClose(data->db); diff --git a/src/Routes/Root.c b/src/Routes/Root.c index 56e462e..8a0f319 100644 --- a/src/Routes/Root.c +++ b/src/Routes/Root.c @@ -48,7 +48,13 @@ GetRandomQuote(void) "XMPP kinda sucks.", "Matrix kinda sucks.", - "Throw jabs!" + "Throw jabs!", + "'Won't you hold my <presence/> safe?' - Kanako", + + NAME ": the federated world's little little kobashi", + + "Go take a look at your stanzas!", + "Go take a look at your objects!" }; const size_t count = sizeof(quotes)/sizeof(*quotes); diff --git a/src/XMPPThread/Bridged.c b/src/XMPPThread/Bridged.c index 5ec2b27..b8c9a3a 100644 --- a/src/XMPPThread/Bridged.c +++ b/src/XMPPThread/Bridged.c @@ -123,7 +123,7 @@ ServerHasXEP421(ParseeData *data, char *from) { char *server = NULL, *parsee; XMLElement *disco; - bool ret; + bool ret = false; if (!data || !from) { return false; @@ -138,17 +138,33 @@ ServerHasXEP421(ParseeData *data, char *from) { server++; } - server = StrDuplicate(server); + server = StrDuplicate(server); if (strchr(server, '/')) { *(strchr(server, '/')) = '\0'; } + pthread_mutex_lock(&data->oidl); + ret = HashMapGet(data->oid_servers, server); + pthread_mutex_unlock(&data->oidl); + if (ret) + { + Log(LOG_DEBUG, "XEP-0421 is cached for '%s'", server); + Free(server); + return ret; + } + parsee = ParseeJID(data); disco = XMPPSendDisco(data->jabber, parsee, server); ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0"); + if (ret) + { + pthread_mutex_lock(&data->oidl); + HashMapSet(data->oid_servers, server, server); + pthread_mutex_unlock(&data->oidl); + } XMLFreeElement(disco); Free(parsee); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 8182675..0c8eb31 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -63,6 +63,9 @@ typedef struct ParseeData { char *id; int verbosity; + + HashMap *oid_servers; + pthread_mutex_t oidl; } ParseeData; typedef struct Argument { From 9d9453f96a85eafffeb9a6f6e77e4ca5f5dee274 Mon Sep 17 00:00:00 2001 From: LDA Date: Fri, 30 Aug 2024 18:13:24 +0200 Subject: [PATCH 021/220] [MOD] Cache disco --- src/Parsee/Data.c | 6 ++++++ src/Routes/Root.c | 2 +- src/XMPP/Stanza.c | 25 ++++++++++++++++++++++--- src/XMPPCommands/DelAdmin.c | 9 ++++++--- src/XMPPThread/Bridged.c | 18 +----------------- src/include/Parsee.h | 1 + src/include/XMPP.h | 8 +++++++- 7 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index bcfd7e3..ce2ddc7 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -69,11 +69,17 @@ ParseeInitData(XMPPComponent *comp) void ParseeFreeData(ParseeData *data) { + char *entity; + XMLElement *disco; if (!data) { return; } + while (HashMapIterate(data->oid_servers, &entity, (void **) &disco)) + { + XMLFreeElement(disco); + } HashMapFree(data->oid_servers); pthread_mutex_destroy(&data->oidl); Free(data->id); diff --git a/src/Routes/Root.c b/src/Routes/Root.c index 8a0f319..831d4b0 100644 --- a/src/Routes/Root.c +++ b/src/Routes/Root.c @@ -206,7 +206,7 @@ RouteHead(RouteRoot, arr, argp) P("Some clicky links relating to %s:", NAME); P("
    "); { - const char *fedi = "https://ak.ari.lt/parsee"; + const char *fedi = "https://tengu.kappach.at/parsee"; const char *icon = "https://kappach.at/parsee.gif"; P("
  • Repository
  • ", REPOSITORY); P("
  • Fediverse
  • ", fedi); diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index 0a42174..e731792 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -235,21 +235,31 @@ XMPPHasError(XMLElement *stanza, char *type) } XMLElement * -XMPPSendDisco(XMPPComponent *jabber, char *from, char *to) +XMPPSendDisco(ParseeData *data, char *from, char *to) { + XMPPComponent *jabber = data ? data->jabber : NULL; XMLElement *ret, *iq; char *identifier; - if (!jabber || !from || !to) + if (!data || !jabber || !from || !to) { return NULL; } + pthread_mutex_lock(&data->oidl); + if ((ret = HashMapGet(data->oid_servers, to))) + { + ret = XMLCopy(ret); + pthread_mutex_unlock(&data->oidl); + return ret; + } + pthread_mutex_unlock(&data->oidl); + iq = XMLCreateTag("iq"); XMLAddAttr(iq, "type", "get"); XMLAddAttr(iq, "from", from); XMLAddAttr(iq, "to", to); - XMLAddAttr(iq, "id", (identifier = StrRandom(69))); + XMLAddAttr(iq, "id", (identifier = StrRandom(64))); { XMLElement *query = XMLCreateTag("query"); XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info"); @@ -264,6 +274,15 @@ XMPPSendDisco(XMPPComponent *jabber, char *from, char *to) ret = ParseeAwaitStanza(identifier, 1.25 SECONDS); Free(identifier); + if (ret) + { + /* TODO: On my own instance, disco replies are _really_ expensive. + * About 120KB. This is sad. Really sad. */ + pthread_mutex_lock(&data->oidl); + XMLFreeElement(HashMapSet(data->oid_servers, to, ret)); + ret = XMLCopy(ret); + pthread_mutex_unlock(&data->oidl); + } return ret; } bool diff --git a/src/XMPPCommands/DelAdmin.c b/src/XMPPCommands/DelAdmin.c index 038f9a2..603b09f 100644 --- a/src/XMPPCommands/DelAdmin.c +++ b/src/XMPPCommands/DelAdmin.c @@ -92,7 +92,7 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from) DbRef *admins; Array *admin_list; size_t i; - XMPPOption *admin_opt; + XMPPOption *admin_opt = NULL; char *trimmed = ParseeTrimJID(from); if (!ParseeIsAdmin(data, trimmed)) @@ -104,14 +104,17 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from) } Free(trimmed); - admin_opt = XMPPCreateList(true, false, "glob", "[NVM!]"); - admins = DbLock(data->db, 1, "admins"); admin_list = GrabArray(DbJson(admins), 1, "admins"); for (i = 0; i < ArraySize(admin_list); i++) { char *glob = JsonValueAsString(ArrayGet(admin_list, i)); + if (!admin_opt) + { + admin_opt = XMPPCreateList(true, false, "glob", glob); + continue; + } XMPPAddListOption(admin_opt, glob); } diff --git a/src/XMPPThread/Bridged.c b/src/XMPPThread/Bridged.c index b8c9a3a..58fb571 100644 --- a/src/XMPPThread/Bridged.c +++ b/src/XMPPThread/Bridged.c @@ -145,26 +145,10 @@ ServerHasXEP421(ParseeData *data, char *from) *(strchr(server, '/')) = '\0'; } - pthread_mutex_lock(&data->oidl); - ret = HashMapGet(data->oid_servers, server); - pthread_mutex_unlock(&data->oidl); - if (ret) - { - Log(LOG_DEBUG, "XEP-0421 is cached for '%s'", server); - Free(server); - return ret; - } - parsee = ParseeJID(data); - disco = XMPPSendDisco(data->jabber, parsee, server); + disco = XMPPSendDisco(data, parsee, server); ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0"); - if (ret) - { - pthread_mutex_lock(&data->oidl); - HashMapSet(data->oid_servers, server, server); - pthread_mutex_unlock(&data->oidl); - } XMLFreeElement(disco); Free(parsee); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 0c8eb31..705ef10 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -5,6 +5,7 @@ *

    TODO: Consider separating some declarations here...

    * -------- * Writren-By: LDA */ +typedef struct ParseeData ParseeData; #include #include diff --git a/src/include/XMPP.h b/src/include/XMPP.h index 4015847..24a23d9 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -102,7 +102,13 @@ extern void XMPPAnnotatePresence(XMLElement *presence); extern bool XMPPHasError(XMLElement *stanza, char *type); -extern XMLElement * XMPPSendDisco(XMPPComponent *jabber, char *from, char *to); +#include +/** Sends a XMPP discovery request to an entity, and caches it if possible. + * ------------------------------------------------------------------------ + * Returns: a valid XML element[HEAP] + * Modifies: the {data}'s cache + * Thrasher: XMLFreeElement */ +extern XMLElement * XMPPSendDisco(ParseeData *data, char *from, char *to); extern bool XMPPDiscoAdvertises(XMLElement *disco, char *var); From 1f3d738bb461f4e6265ca0dcbe5a7b058c6aaf5b Mon Sep 17 00:00:00 2001 From: LDA Date: Sat, 31 Aug 2024 13:16:58 +0200 Subject: [PATCH 022/220] [MOD] Import PNG media. --- .gitignore | 1 + Makefile | 18 ++++++++++++++---- README.MD | 32 +++++++++++++++++++------------- XEPS-TBD.TXT | 27 ++++++++++++++------------- etc/media/README | 5 +++++ etc/media/parsee_logo.png | Bin 0 -> 871 bytes src/AS/Media.c | 1 - src/Main.c | 5 +++-- src/Parsee/HMAC.c | 9 ++++++--- src/Routes/Media.c | 12 +++++++++--- src/Routes/Root.c | 16 +++++++++++++--- src/include/Parsee.h | 3 +++ 12 files changed, 87 insertions(+), 42 deletions(-) create mode 100644 etc/media/README create mode 100644 etc/media/parsee_logo.png diff --git a/.gitignore b/.gitignore index 7f1e762..f2c57e6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ tags # Whitelists !etc/* +!etc/** diff --git a/Makefile b/Makefile index c76e361..8d71552 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,9 @@ # ================================ # TODO: Consider making something akin to a configure script that checks # for dependencies, or maybe even use *autoconf* (the devil!) +# +# TODO: Either get rid of the GNU extensions, or use a script/program +# to build Parsee on a POSIXly environment. I'd dig parsee-buildout, tbf. # =========================== Parsee Flags ============================= @@ -31,13 +34,13 @@ LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -O3 -g AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)" BINARY=parsee # ============================ Compilation ================================= -SRC_FILES:=$(shell find $(SOURCE) -name '*.c') -OBJ_FILES:=${subst $(SOURCE)/,$(OBJECT)/,$(patsubst %.c, %.o, $(SRC_FILES))} +SRC_FILES:=$(shell find $(SOURCE) -name '*.c') $(shell find $(ETC)/media -name '*.png') +OBJ_FILES:=${subst $(ETC)/media/,$(OBJECT)/,${subst $(SOURCE)/,$(OBJECT)/,$(patsubst %.png, %.o, $(patsubst %.c, %.o, $(SRC_FILES)))}} CPP_FILES:=$(shell find $(INCLUDES) -name '*.h') AYA_FILES:=${subst $(INCLUDES)/,$(AYAS)/,$(patsubst %.h, %.html, $(CPP_FILES))} -all: binary utils +all: utils binary binary: $(OBJ_FILES) $(CC) $(LDFLAGS) $(OBJ_FILES) -o $(BINARY) @@ -47,9 +50,16 @@ tags: $(SRC_FILES) clean: rm -rf $(OBJECT) $(BINARY) $(AYAS) +$(OBJECT)/%.o: $(ETC)/media/%.png + @mkdir -p $(shell dirname "$@") + @echo "const char media_$(shell basename $< .png)[] =" > $@.c + @base64 $< | \ + sed -e 's/^\(.*\)$$/ "\1"/' | \ + sed -e '$$ s/^\(.*\)$$/\1;/' >> $@.c + $(CC) -c $(CFLAGS) $@.c -o $@ $(OBJECT)/%.o: $(SOURCE)/%.c @mkdir -p $(shell dirname "$@") - $(CC) -c $(CFLAGS) $< -o $@ + $(CC) -c $(CFLAGS) $< -o $@ utils: (cd tools && make) diff --git a/README.MD b/README.MD index fa305db..51fcf7f 100644 --- a/README.MD +++ b/README.MD @@ -1,15 +1,16 @@ # Parsee - the jealous XMPP<=>Matrix bridge -Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is NOT a drop-in replacment. +Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is +NOT a drop-in replacment. ## Why? ### Naming -The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi), a -"*bridge* princess". +The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi), +a "*bridge* princess". ### Reasoning (personal to LDA) -I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister project to KappaChat, -this means that I can integrate Parsee with KappaChat however I wish it to be, which allows me to mess around with a -codebase I'm already familiar with. +I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister +project to KappaChat, this means that I can integrate Parsee with KappaChat however I wish it +to be, which allows me to mess around with a codebase I'm already familiar with. A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*. Well, I'm *trying* to do that, at least. @@ -31,10 +32,13 @@ $ make ayadoc # If you want to build HTML documentation $ make [PREFIX=(install path)] install # To install Parsee. ``` If there are any Cytoplasm-related build failures, you may want to check the Makefile to -change a few variables (you can set `CYTO_INC` and `CYTO_LIB`) +change a few variables (you can set `CYTO_INC` and `CYTO_LIB` for Cytoplasm's include and +library paths specifically.) ### DEPENDENCIES -Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendria.io/Telodendria/Cytoplasm). Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but you can get away without those). +Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendria.io/Telodendria/Cytoplasm). +Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but +you can get away without those if you're adventurous). ## RUNNING First off, you may want to configure Parsee by running the `config` tool(generally named @@ -62,19 +66,20 @@ returns with a landing page, then this side works. You can read it for some more Currently, the main sources of documentation are the Ayadocs(for headers) and the manpages (see `etc/man`) -## TODOS -- Add [libomemo](https://github.com/gkdr/libomemo) as an optional dependency. +## TODOS before 1.0 rolls around +- Add [libomemo](https://github.com/gkdr/libomemo) or something as an optional dependency. - It depends on more stuff anyways, and I don't want to weigh down the dependency list of Parsee for that. - Matrix's libolm is deprecated. They replaced it with a Rust version that pulls in *way too many* dependencies, and that lacks a C binding. We may put in the work of either forking off libolm or making a binding to KappaChat. + - Josh did infact tell me that maybe C bindings may happen. I'd be + willing to help out, but IDK. In any case, this will at best be an + extension packagers may integrate properly. - Get rid of the '?'-syntax and use another invalid Matrix char/valid XMPP char ('$'?) for escaped? - 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, - I'm all in. + XMPP->Matrix is decent, Matrix->XMPP is effectiveny not done - Consider making room/MUC admins/owners be able to plumb instead of it being restricted to Parsee admins, with permission from MUC owners, too - Limiting to admins may be a way to "control" consent for both, but this is @@ -85,6 +90,7 @@ restricted to Parsee admins, with permission from MUC owners, too support XMPP->Matrix bridging. - Manage MUC DMs in a reasonable manner. Thanks `@freeoffers4u:matrix.org` for being a fucking annoyance and DMing an old Parsee semi-anon user for no clear reason. +- Deadlocks. It's always deadlocks. ## DONATING/CONTRIBUTING If you know things about XMPP or Matrix, yet aren't familiar with C99, or just diff --git a/XEPS-TBD.TXT b/XEPS-TBD.TXT index 02a68ca..ae74f4f 100644 --- a/XEPS-TBD.TXT +++ b/XEPS-TBD.TXT @@ -1,6 +1,19 @@ XEPs current supported are in src/XMPPThread, at the IQ disco advertising. Somewhat implemented XEPs: + v https://xmpp.org/extensions/xep-0050.html + Ad-hoc commands that bridge maintainers can deal with XMPP-style are + also a nice to have. + There are commands, but not a lot of them as of now, and localisation + is missing. + v https://xmpp.org/extensions/xep-0421.html + Using the occupant ID in semi-anonymous MUCs is a desirable property. + I dont know of a lot of places that don't use the occupant ID anymore + within Parsee. + v "also it [Bifrost] doesn't respect voice either" + As far as I am aware, works. + v https://xmpp.org/extensions/xep-0425.html + As mentionned in #2, moderation _needs_ to be done. ~ https://xmpp.org/extensions/xep-0085.html Only XMPP->Matrix at the moment. Still need to figure out how to get typing indicators as an AS. @@ -12,17 +25,6 @@ Somewhat implemented XEPs: ~ https://xmpp.org/extensions/xep-0184.html Only Matrix->XMPP as of now. Requesting data from Matrix ASes without /sync seems like a non-option as of now, which _sucks_. - ~ https://xmpp.org/extensions/xep-0050.html - Ad-hoc commands that bridge maintainers can deal with XMPP-style are - also a nice to have. - There are commands, but not a lot of them as of now, and localisation - is missing. - ~ https://xmpp.org/extensions/xep-0421.html - Using the occupant ID in semi-anonymous MUCs is a desirable property. - I dont know of a lot of places that don't use the occupant ID anymore - within Parsee. - ~ https://xmpp.org/extensions/xep-0425.html - As mentionned in #2, moderation _needs_ to be done. For future XEPs: - https://xmpp.org/extensions/xep-0449.html @@ -44,9 +46,8 @@ THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT: Not XEPs, but ideas that _needs_ to be added: - v "also it [Bifrost] doesn't respect voice either" -> Send a form on moderated - MUCs (which is standard, so its not too bad!). Currently WIP, and barely tested. ~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d + Happens on Matrix. I'll need to handle that on XMPP as well. - Standalone/Static Parsee, ideally as small as it can be(if not as APE). - Kappa-like extension system(maybe bridging more than just Matrix-XMPP.) - https://www.youtube.com/watch?v=InL414iDZmY diff --git a/etc/media/README b/etc/media/README new file mode 100644 index 0000000..2ef7300 --- /dev/null +++ b/etc/media/README @@ -0,0 +1,5 @@ +This directory is for any PNG media that needs to be integrated into Parsee. +Each file here is Base64-encoded then defined as a const char[] symbol with +the name being a function of the PNG filename: N(FILE.png)=media_FILE + +NOTE: Medias should be about ~1024B MAX. Avoid to stride for anything larger. diff --git a/etc/media/parsee_logo.png b/etc/media/parsee_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1e0a8a1006e6cbf38662bf856d085c0c7baa15d3 GIT binary patch literal 871 zcmV-t1DO1YP)Px#U{Fj{MF0Q*A|NzHC_H94F^^U|W;`W3Bn?$50Q>Sb4^=&CzAzO#EK6xGQcEN< zJUKrX zreR|42KR?6M<<&Gn>et30on5YvL>0)t3*20G8y@)CdvboNI8&)Wge0+Lq)y|=3YU|doQfbjS9p68OTHW(Le@6h^^za5Z-TCDArax$yNdIRFPUGFUY|K+wdH>Gnuk1z^T%)pP+x z_V}1;EkG&ar4QiUO6vd=vR;qkb#3oH0^sSdGvF*e0(1`dw7&yfdVtG6;7N>z)433aaj)9^@7?W@j?S<0W3D;-$z~CZk0&Wl{6G1Z*sU0Kv$|P)v^+KGNIiZ xZf_0)w7Dfdxw%OZOTDZeEQfMB`CRpCzX9ghe&NM6@>~D_002ovPDHLkV1mPkhwlIY literal 0 HcmV?d00001 diff --git a/src/AS/Media.c b/src/AS/Media.c index 31c7231..4e642e9 100644 --- a/src/AS/Media.c +++ b/src/AS/Media.c @@ -19,7 +19,6 @@ ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime) HashMap *reply; if (!c || !from) { - Log(LOG_ERR, "No ASUpload input (c=%p from=%p)", c, from); return NULL; } diff --git a/src/Main.c b/src/Main.c index fd599ba..e99e627 100644 --- a/src/Main.c +++ b/src/Main.c @@ -224,8 +224,9 @@ Main(Array *args, HashMap *env) } Log(LOG_NOTICE, "Starting up local cronjobs..."); - cron = CronCreate( 10 SECONDS ); - CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs); + cron = CronCreate( 30 MINUTES ); + CronEvery(cron, 1 HOURS, ParseeCleanup, conf.handlerArgs); + ParseeCleanup(conf.handlerArgs); CronStart(cron); diff --git a/src/Parsee/HMAC.c b/src/Parsee/HMAC.c index efdd09a..abb65ef 100644 --- a/src/Parsee/HMAC.c +++ b/src/Parsee/HMAC.c @@ -13,6 +13,7 @@ ComputeKPad(char *key, uint8_t pad, uint8_t *kopad) { size_t klen; uint8_t *kp; + uint8_t kpi[64] = { 0 }; size_t i; if ((klen = strlen(key)) <= 64) { @@ -25,14 +26,16 @@ ComputeKPad(char *key, uint8_t pad, uint8_t *kopad) klen = 32; } + memset(kpi, 0x00, 64); + memcpy(kpi, kp, klen); + Free(kp); + /* Now that we have K', lets compute it XORd with opad */ for (i = 0; i < 64; i++) { - uint8_t byte = i < klen ? kp[i] : 0x00; + uint8_t byte = kpi[i]; kopad[i] = byte ^ pad; } - - Free(kp); } char * diff --git a/src/Routes/Media.c b/src/Routes/Media.c index 137ef0f..7d6bc68 100644 --- a/src/Routes/Media.c +++ b/src/Routes/Media.c @@ -23,8 +23,8 @@ RouteHead(RouteMedia, arr, argp) params = HttpRequestParams(args->ctx); hmac = HashMapGet(params, "hmac"); - /* TODO: Make it check the DB for its validicity. "Purging" would be useful. - */ + /* TODO: Make it check the DB for its validicity. "Purging" would be + * useful, alongside checking if someone isn't just a little idiotic. */ { char *concat = StrConcat(3, server, "/", identi); chkmak = ParseeHMACS(args->data->id, concat); @@ -32,12 +32,18 @@ RouteHead(RouteMedia, arr, argp) } if (!server || !identi || !hmac || !StrEquals(hmac, chkmak)) { + char *err = + hmac && StrEquals(hmac, chkmak) ? + "No server/identifier/HMAC code" : + "Hah! You _dirty_ little liar! Try a little harder!"; Free(chkmak); HttpResponseStatus(args->ctx, HTTP_BAD_REQUEST); - return MatrixCreateError("M_NOT_YET_UPLOADED", "No server/identifier"); + return MatrixCreateError("M_NOT_YET_UPLOADED", err); } Free(chkmak); + /* Proxy the media through an authenticated endpoint if the HMAC + * is valid. */ server = HttpUrlEncode(server); identi = HttpUrlEncode(identi); path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi); diff --git a/src/Routes/Root.c b/src/Routes/Root.c index 831d4b0..af89909 100644 --- a/src/Routes/Root.c +++ b/src/Routes/Root.c @@ -76,6 +76,7 @@ RouteHead(RouteRoot, arr, argp) { P("%s Lander", NAME); P(""); + P("", media_parsee_logo); P("