From 1f658ece76b18e9f44f3df77d83eb91c1e9b98ae Mon Sep 17 00:00:00 2001 From: LDA Date: Fri, 5 Jul 2024 03:10:43 +0200 Subject: [PATCH] [ADD/WIP] XMPP->Matrix avatar, start bridging bans I still hate XEP-0084. --- Makefile | 4 +- README.MD | 4 + XEPS-TBD.TXT | 14 +- src/AS.c | 171 +++++++++++++++- src/Routes/UserAck.c | 3 +- src/Streams/Reader.c | 4 +- src/XMPPThread.c | 408 +++++++++++++++++++++++++++++++++++-- src/include/AS.h | 5 + src/include/StringStream.h | 3 +- 9 files changed, 588 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index b70d8cc..c6047dc 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,8 @@ SOURCE=src OBJECT=build INCLUDES=src/include CC=cc -CFLAGS=-I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -g -ggdb -LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -Wl,--export-dynamic +CFLAGS=-I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -O3 +LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -Wl,--export-dynamic -O3 BINARY=parsee # ============================ Compilation ================================= SRC_FILES:=$(shell find $(SOURCE) -name '*.c') diff --git a/README.MD b/README.MD index b6beb95..d61e9d5 100644 --- a/README.MD +++ b/README.MD @@ -35,6 +35,10 @@ TODO TODO ## TODOS +- 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. - Look at XEPS-TBD.TXT for XEPs to be done - Achievements ### Why? diff --git a/XEPS-TBD.TXT b/XEPS-TBD.TXT index 6194573..61c04fe 100644 --- a/XEPS-TBD.TXT +++ b/XEPS-TBD.TXT @@ -25,14 +25,20 @@ For future XEPs: - https://xmpp.org/extensions/xep-0080.html Doxxing people over two protocols is great! - - https://xmpp.org/extensions/xep-0084.html - Avatar support would be extremely useful, if just a QoL improvment. - Matrix and XMPP both have support for these. - - https://xmpp.org/extensions/xep-0449.html Stickers are great. Matrix and XMPP somewhat has support for them, so might be a nice-to-have, and also to push over XMPP support. +ON STANDBY BECAUSE THESE HAVE BEEN TERRIBLE TO DEAL WITH AND WHO KEEPS WRITING +THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT: + (x) https://xmpp.org/extensions/xep-0084.html + Avatar support would be extremely useful, if just a QoL improvment. + Matrix and XMPP both have support for these. + + XEP-0084 is a pain in the ass to implement and seems generally just + unreliable, however. + + Not XEPs, but ideas that _needs_ to be added: - "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d "also it [Bifrost] doesn't respect voice either" diff --git a/src/AS.c b/src/AS.c index cc37230..3fc4d12 100644 --- a/src/AS.c +++ b/src/AS.c @@ -188,6 +188,41 @@ ASBan(const ParseeConfig *conf, char *id, char *banned) 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->homeserver_host + ); + 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); +} +void ASJoin(const ParseeConfig *conf, char *id, char *masquerade) { HttpClientContext *ctx = NULL; @@ -331,6 +366,35 @@ ASCreateRoom(const ParseeConfig *conf, char *by, char *alias) 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; @@ -510,6 +574,7 @@ ASUpload(const ParseeConfig *c, Stream *from, unsigned int size) HashMap *reply; if (!c || !from) { + Log(LOG_INFO, "Obvious upload fail"); return NULL; } @@ -526,13 +591,26 @@ ASUpload(const ParseeConfig *c, Stream *from, unsigned int size) } HttpRequestSendHeaders(ctx); - StreamCopy(from, HttpClientStream(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()); + Log(LOG_INFO, "Less obvious upload fail"); + } HttpClientContextFree(ctx); JsonFree(reply); Free(size_str); @@ -632,3 +710,92 @@ ASType(const ParseeConfig *c, char *user, char *room, bool status) HttpClientContextFree(ctx); Free(user); } + + +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->homeserver_host + ); + 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; + HashMap *json; + char *path; + if (!c || !key || !map) + { + JsonFree(map); + return; + } + + if (!user) + { + char *raw = StrConcat(4, + "@", c->sender_localpart, + ":", c->homeserver_host + ); + 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; +} diff --git a/src/Routes/UserAck.c b/src/Routes/UserAck.c index cc60d71..b31bea1 100644 --- a/src/Routes/UserAck.c +++ b/src/Routes/UserAck.c @@ -68,9 +68,10 @@ RouteHead(RouteRoomAck, arr, argp) } muc = ParseeDecodeLocalMUC(args->data->config, room); - if (!ParseeManageBan(args->data, muc, NULL)) + if (ParseeManageBan(args->data, muc, NULL)) { HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED); + Log(LOG_INFO, "Nofly..."); response = MatrixCreateError( "M_NOT_FOUND", "XMPP MUC is banned from being accessed on this instance" diff --git a/src/Streams/Reader.c b/src/Streams/Reader.c index 27d92d1..19fcefe 100644 --- a/src/Streams/Reader.c +++ b/src/Streams/Reader.c @@ -113,7 +113,7 @@ const static IoFunctions Functions = { }; Stream * -StrStreamReader(char *buffer) +StrStreamReaderN(char *buffer, int n) { Io *raw_io; ReaderCookie *cookie; @@ -124,7 +124,7 @@ StrStreamReader(char *buffer) cookie = Malloc(sizeof(*cookie)); cookie->buffer = buffer; - cookie->length = strlen(buffer); + cookie->length = n ? n : strlen(buffer); cookie->offset = 0; raw_io = IoCreate(cookie, Functions); return StreamIo(raw_io); diff --git a/src/XMPPThread.c b/src/XMPPThread.c index af4ac92..4ed0360 100644 --- a/src/XMPPThread.c +++ b/src/XMPPThread.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,15 +11,21 @@ #include #include +#include #include #include #include #include +/* TODO: Rewrite this avatar code. + * XEP-0084 sucks. */ #define IQ_ADVERT \ AdvertiseSimple("http://jabber.org/protocol/chatstates") \ AdvertiseSimple("http://jabber.org/protocol/caps") \ AdvertiseSimple("urn:xmpp:avatar:metadata+notify") \ + AdvertiseSimple("urn:xmpp:avatar:metadata") \ + AdvertiseSimple("urn:xmpp:avatar:data+notify") \ + AdvertiseSimple("urn:xmpp:avatar:data") \ AdvertiseSimple("urn:xmpp:message-correct:0") \ AdvertiseSimple("urn:xmpp:reactions:0") \ AdvertiseSimple("urn:xmpp:styling:0") \ @@ -290,8 +297,104 @@ typedef struct XMPPThread { XMPPThreadInfo *info; } XMPPThread; -/* TODO: Clean up all of this. We are currently separating DMs from MUCs, - * where we could unify all our code, and generalise everything. */ +/* Manages an avatar metadata pubsub item */ +static XMLElement * +CreateAvatarRequest(char *from, char *to, char *avatar_id) +{ + XMLElement *iq_req, *pubsub, *items, *item; + 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", "get"); + + pubsub = XMLCreateTag("pubsub"); + XMLAddAttr(pubsub, "xmlns", "http://jabber.org/protocol/pubsub"); + XMLAddChild(iq_req, pubsub); + + items = XMLCreateTag("items"); + XMLAddAttr(items, "node", "urn:xmpp:avatar:data"); + XMLAddChild(pubsub, items); + + + + item = XMLCreateTag("item"); + XMLAddAttr(item, "id", avatar_id); + XMLAddChild(items, item); + + Free(id); + return iq_req; +} +static 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); + + //Log(LOG_INFO, "Subscribed to %s's %s node", to, node); + + Free(id); + return iq_req; +} +static void +ManageProfileItem(ParseeData *args, XMLElement *item, XMLElement *stanza, XMPPThread *thr) +{ + XMPPComponent *jabber = args->jabber; + DbRef *avatars; + HashMap *json; + + char *publisher = HashMapGet(item->attrs, "publisher"); + char *id = HashMapGet(item->attrs, "id"); + char *mxid; + + avatars = DbLock(args->db, 1, "avatars"); + if (!avatars) + { + avatars = DbCreate(args->db, 1, "avatars"); + } + json = DbJson(avatars); + + mxid = GrabString(json, 1, publisher); + if (mxid && StrEquals(mxid, id)) + { + /* We have nothing to do. */ + goto end; + } + + /* We need to download the media to push it. Let's submit a pubsub request. */ + { + char *from = HashMapGet(stanza->attrs, "to"); + char *to = HashMapGet(stanza->attrs, "from"); + char *url = HashMapGet(item->attrs, "url"); + XMLElement *request = CreateAvatarRequest(from, to, id); + + pthread_mutex_lock(&jabber->write_lock); + XMLEncode(jabber->stream, request); + StreamFlush(jabber->stream); + pthread_mutex_unlock(&jabber->write_lock); + + XMLFreeElement(request); + } + +end: + DbUnlock(args->db, avatars); +} + static bool MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) { @@ -300,6 +403,7 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) XMLElement *reactions = NULL; XMLElement *body = NULL; XMLElement *data = NULL; + XMLElement *event = NULL; char *to, *room, *from, *from_matrix, *decode_from; char *chat_id = NULL, *mroom_id = NULL; @@ -312,6 +416,28 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) from = HashMapGet(stanza->attrs, "from"); + event = XMLookForTKV(stanza, "event", + "xmlns", "http://jabber.org/protocol/pubsub#event" + ); + if (event) + { + size_t i; + XMLElement *items = + XMLookForTKV(event, "items", "node", "urn:xmpp:avatar:metadata"); + if (items) + { + for (i = 0; i < ArraySize(items->children); i++) + { + ManageProfileItem( + args, + ArrayGet(items->children, i), + stanza, + thr + ); + } + } + } + #define CHAT_STATES "http://jabber.org/protocol/chatstates" if (XMLookForTKV(stanza, "composing", "xmlns", CHAT_STATES)) { @@ -395,6 +521,21 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr) chat = true; } + { + char *parsee = StrConcat(2, "parsee@", args->config->component_host); + + XMLElement *ps = CreatePubsubRequest( + parsee, decode_from, "urn:xmpp:avatar:metadata" + ); + pthread_mutex_lock(&jabber->write_lock); + XMLEncode(jabber->stream, ps); + StreamFlush(jabber->stream); + pthread_mutex_unlock(&jabber->write_lock); + + XMLFreeElement(ps); + Free(parsee); + } + pthread_mutex_lock(&thr->info->chk_lock); if (ParseeVerifyAllStanza(args, stanza) && !replaced) { @@ -581,10 +722,158 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza) pthread_mutex_lock(&jabber->write_lock); XMLEncode(jabber->stream, iq_reply); + StreamFlush(jabber->stream); pthread_mutex_unlock(&jabber->write_lock); XMLFreeElement(iq_reply); } +static char * +TrimBase64(char *b64) +{ + char *ret, *tmp; + + ret = NULL; + while (*b64) + { + char ch[2] = { *b64, 0 }; + if (isspace(*b64)) + { + b64++; + continue; + } + + tmp = ret; + ret = StrConcat(2, ret, ch); + Free(tmp); + b64++; + } + return ret; +} +static void +IQResult(ParseeData *args, XMLElement *stanza) +{ + XMPPComponent *jabber = args->jabber; + XMLElement *vcard = XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"); + + XMLElement *event = XMLookForTKV(stanza, "pubsub", + "xmlns", "http://jabber.org/protocol/pubsub" + ); + if (event) + { + size_t i; + XMLElement *retrieve = + XMLookForTKV(event, "items", "node", "urn:xmpp:avatar:data"); + if (retrieve) + { + for (i = 0; i < ArraySize(retrieve->children); i++) + { + XMLElement *item = + ArrayGet(retrieve->children, i); + XMLElement *avatar_data = XMLookForTKV( + item, "data", "xmlns", "urn:xmpp:avatar:data" + ); + XMLElement *data = ArrayGet(avatar_data->children, 0); + char *id = HashMapGet(item->attrs, "id"); + char *from = HashMapGet(stanza->attrs, "from"); + char *base64; + unsigned char *bdata; + size_t length, b64len; + Stream *datastream; + char *mxc, *from_matrix, *jid; + DbRef *avatars; + HashMap *json; + + if (!data || !data->data) + { + return; + } + avatars = DbLock(args->db, 1, "avatars"); + if (!avatars) + { + avatars = DbCreate(args->db, 1, "avatars"); + } + json = DbJson(avatars); + + if (StrEquals(GrabString(json, 1, from), id)) + { + DbUnlock(args->db, avatars); + return; + } + + base64 = TrimBase64(data->data); + b64len = base64 ? strlen(base64) : 0; + length = Base64DecodedSize(base64, b64len); + + /* TODO: Bound checks! */ + bdata = Base64Decode(base64, b64len); + datastream = StrStreamReaderN(bdata, length); + mxc = ASUpload(args->config, datastream, length); + + jid = ParseeLookupJID(from); + from_matrix = ParseeEncodeJID(args->config, jid, false); + ASSetAvatar(args->config, from_matrix, mxc); + + JsonValueFree(JsonSet( + json, JsonValueString(id), + 1, from) + ); + DbUnlock(args->db, avatars); + + Free(mxc); + Free(jid); + Free(bdata); + Free(from_matrix); + Free(base64); + StreamClose(datastream); + } + + } + } + if (vcard) + { + XMLElement *photo = XMLookForUnique(vcard, "PHOTO"); + XMLElement *nickname = XMLookForUnique(vcard, "NICKNAME"); + + if (nickname) + { + XMLElement *data = ArrayGet(nickname->children, 0); + } + if (photo) + { + XMLElement *binval = XMLookForUnique(photo, "BINVAL"); + XMLElement *data = ArrayGet(binval->children, 0); + char *base64; + unsigned char *bdata; + size_t length, b64len; + Stream *datastream; + char *mxc, *from_matrix, *jid; + if (!data || !data->data) + { + return; + } + base64 = data->data; + b64len = base64 ? strlen(base64) : 0; + length = Base64DecodedSize(base64, b64len); + + bdata = Base64Decode(base64, b64len); + datastream = StrStreamReaderN(bdata, length); + mxc = ASUpload(args->config, datastream, length); + + jid = ParseeLookupJID(HashMapGet(stanza->attrs, "from")); + from_matrix = ParseeEncodeJID(args->config, jid, false); + ASSetAvatar(args->config, from_matrix, mxc); + + /* TODO: Check if already set. */ + + Free(bdata); + StreamClose(datastream); + + Free(from_matrix); + Free(jid); + Free(mxc); + } + } +} static void IQGet(ParseeData *args, XMLElement *stanza) { @@ -622,6 +911,7 @@ IQGet(ParseeData *args, XMLElement *stanza) pthread_mutex_lock(&jabber->write_lock); XMLEncode(jabber->stream, iq_reply); + StreamFlush(jabber->stream); pthread_mutex_unlock(&jabber->write_lock); XMLFreeElement(iq_reply); } @@ -634,6 +924,11 @@ IQGet(ParseeData *args, XMLElement *stanza) } } +static void +IQError(ParseeData *args, XMLElement *stanza) +{ + /* TODO */ +} #undef DISCO static void IQStanza(ParseeData *args, XMLElement *stanza) @@ -651,9 +946,30 @@ IQStanza(ParseeData *args, XMLElement *stanza) while (0) OnType(get, IQGet); + OnType(error, IQError); + OnType(result, IQResult); #undef OnType } +static XMLElement * +CreateVCardRequest(char *from, char *to) +{ + XMLElement *vcard_request, *vcard; + char *id; + vcard_request = XMLCreateTag("iq"); + XMLAddAttr(vcard_request, "from", from); + XMLAddAttr(vcard_request, "to", to); + XMLAddAttr(vcard_request, "id", (id = StrRandom(16))); + XMLAddAttr(vcard_request, "type", "get"); + + vcard = XMLCreateTag("vCard"); + XMLAddAttr(vcard, "xmlns", "vcard-temp"); + XMLAddChild(vcard_request, vcard); + + Free(id); + + return vcard_request; +} static void PresenceStanza(ParseeData *args, XMLElement *stanza) { @@ -664,35 +980,95 @@ PresenceStanza(ParseeData *args, XMLElement *stanza) if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS))) { XMLElement *item = XMLookForUnique(user_info, "item"); + XMPPComponent *jabber = args->jabber; char *jid = item ? HashMapGet(item->attrs, "jid") : NULL; + char *from, *best = jid ? jid : oid; + char *type = HashMapGet(stanza->attrs, "type"); + + if (StrEquals(type, "unavailable")) + { + /* TODO: Treat as a ban if the role is outcast */ + char *room = ParseeGetBridgedRoom(args, stanza); + char *decode_from = ParseeLookupJID(oid); + char *from_matrix = ParseeDecodeMXID(decode_from); + char *affiliation = HashMapGet(item->attrs, "affiliation"); + + if (!from_matrix || *from_matrix != '@') + { + Free(from_matrix); + from_matrix = ParseeEncodeJID(args->config, oid, true); + } + + if (StrEquals(affiliation, "outcast")) + { + ASBan(args->config, room, from_matrix); + } + else + { + ASKick(args->config, room, from_matrix); + } + + Free(decode_from); + Free(from_matrix); + Free(room); + } if (jid) { ParseePushJIDTable(oid, jid); } + from = StrConcat(2, "parsee@", args->config->component_host); + Free(from); } else if (vc) { XMLElement *photo = XMLookForUnique(vc, "photo"); XMLElement *p_dat = photo ? ArrayGet(photo->children, 0) : NULL; - char *room_id = NULL, *chat_id = NULL; + XMLElement *vcard_request; + XMPPComponent *jabber = args->jabber; + char *from; + + DbRef *avatars; + HashMap *json; + char *avatar_id; if (!p_dat) { return; } - chat_id = ParseeGetFromMUCID(args, oid); - room_id = ParseeGetRoomID(args, chat_id); - - if (!room_id) + avatars = DbLock(args->db, 1, "avatars"); + if (!avatars) { - Free(chat_id); - Free(room_id); + avatars = DbCreate(args->db, 1, "avatars"); } - /* TODO: Get the media, and shove it to the room */ - Free(chat_id); - Free(room_id); + json = DbJson(avatars); + + avatar_id = GrabString(json, 1, oid); + if (StrEquals(avatar_id, p_dat->data)) + { + DbUnlock(args->db, avatars); + return; + } + JsonValueFree(JsonSet( + json, JsonValueString(p_dat->data), + 1, oid) + ); + + DbUnlock(args->db, avatars); + + from = StrConcat(2, "parsee@", args->config->component_host); + + vcard_request = CreateVCardRequest( + from, HashMapGet(stanza->attrs, "from") + ); + pthread_mutex_lock(&jabber->write_lock); + XMLEncode(jabber->stream, vcard_request); + StreamFlush(jabber->stream); + pthread_mutex_unlock(&jabber->write_lock); + + XMLFreeElement(vcard_request); + Free(from); } #undef MUC_USER_NS } @@ -728,13 +1104,12 @@ XMPPDispatcher(void *argp) XMLElement *stanza = RetrieveStanza(thread); if (!stanza) { - UtilSleepMillis(10); + UtilSleepMillis(1); continue; } if (StrEquals(stanza->name, "presence")) { - /* TODO: Manage presence */ PresenceStanza(args, stanza); XMLFreeElement(stanza); continue; @@ -770,7 +1145,8 @@ ParseeXMPPThread(void *argp) XMLElement *stanza = NULL; XMPPThreadInfo info; pthread_mutex_t stanzas_lock = PTHREAD_MUTEX_INITIALIZER; - size_t i; + size_t i, j = 0; + /* Initialise the FIFO */ info.stanzas = ArrayCreate(); @@ -795,6 +1171,7 @@ ParseeXMPPThread(void *argp) pthread_create(thr, NULL, XMPPDispatcher, &info.dispatchers[i]); } + while (true) { char *to, *room, *from, *from_matrix; @@ -829,7 +1206,6 @@ ParseeXMPPThread(void *argp) } PushStanza(&info, stanza); - //XMLFreeElement(stanza); } info.running = false; diff --git a/src/include/AS.h b/src/include/AS.h index cdc6619..ca8b38c 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -28,6 +28,7 @@ extern void ASJoin(const ParseeConfig *, char *, char *); /* Bans from a room a specific user */ extern void ASBan(const ParseeConfig *, char *, char *); +extern void ASKick(const ParseeConfig *, char *, char *); /* Invites from a room a specific user */ extern void ASInvite(const ParseeConfig *, char *, char *); @@ -53,6 +54,7 @@ extern char * ASCreateRoom(const ParseeConfig *c, char *by, char *alias); /* Sets a user's displayname */ extern void ASSetName(const ParseeConfig *c, char *user, char *name); +extern void ASSetAvatar(const ParseeConfig *c, char *user, char *mxc); /* Returns the user's name in a room, or a copy of the MXID itself, to be * Free'd. */ @@ -63,4 +65,7 @@ extern char * ASUpload(const ParseeConfig *c, Stream *from, unsigned int size); /* Reuploads a HTTP URL to Matrix, with an optional MIME type returned. */ 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); #endif diff --git a/src/include/StringStream.h b/src/include/StringStream.h index 7f43313..007a66b 100644 --- a/src/include/StringStream.h +++ b/src/include/StringStream.h @@ -8,6 +8,7 @@ extern Stream * StrStreamWriter(char **buffer); /* Creates a string stream reader. The referenced buffer may be everywhere. */ -extern Stream * StrStreamReader(char *buffer); +extern Stream * StrStreamReaderN(char *buffer, int n); +#define StrStreamReader(buf) StrStreamReaderN(buf, 0) #endif