mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 16:55:10 +00:00
[ADD/WIP] XMPP->Matrix avatar, start bridging bans
I still hate XEP-0084.
This commit is contained in:
parent
a3bef5c0c1
commit
1f658ece76
9 changed files with 587 additions and 27 deletions
4
Makefile
4
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')
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
14
XEPS-TBD.TXT
14
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"
|
||||
|
|
|
|||
171
src/AS.c
171
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
408
src/XMPPThread.c
408
src/XMPPThread.c
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Base64.h>
|
||||
|
|
@ -10,15 +11,21 @@
|
|||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <Matrix.h>
|
||||
#include <XMPP.h>
|
||||
#include <XML.h>
|
||||
#include <AS.h>
|
||||
|
||||
/* 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue