mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 21:35:10 +00:00
[MOD] Basic work to get XMPP avatars through PEP
Attaboy!
This commit is contained in:
parent
f31a9c37b6
commit
e54332e376
13 changed files with 428 additions and 25 deletions
|
|
@ -4,6 +4,7 @@
|
|||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -127,3 +128,95 @@ ASReupload(const ParseeConfig *c, char *from, char **mime)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha)
|
||||
{
|
||||
HttpClientContext *cctx;
|
||||
Stream *stream;
|
||||
Stream *fake;
|
||||
Uri *uri;
|
||||
char *path, *buf = NULL;
|
||||
unsigned char *sha1;
|
||||
size_t len;
|
||||
bool ret;
|
||||
if (!c || !mxc || !mime || !sha)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*mime = NULL;
|
||||
*sha = NULL;
|
||||
|
||||
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
path = StrConcat(3, "/_matrix/media/v3/download/", uri->host, uri->path);
|
||||
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
ASAuthenticateRequest(c, cctx);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
|
||||
*mime = StrDuplicate(
|
||||
HashMapGet(HttpResponseHeaders(cctx), "content-type")
|
||||
);
|
||||
stream = HttpClientStream(cctx);
|
||||
fake = StreamFile(open_memstream(&buf, &len));
|
||||
StreamCopy(stream, fake);
|
||||
StreamClose(fake);
|
||||
|
||||
sha1 = Sha1Raw(buf, len);
|
||||
free(buf);
|
||||
*sha = ShaToHex(sha1, HASH_SHA1);
|
||||
Free(sha1);
|
||||
|
||||
HttpClientContextFree(cctx);
|
||||
UriFree(uri);
|
||||
Free(path);
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len)
|
||||
{
|
||||
HttpClientContext *cctx;
|
||||
Stream *stream;
|
||||
Stream *fake;
|
||||
Uri *uri;
|
||||
char *path, *buf = NULL;
|
||||
bool ret;
|
||||
if (!c || !mxc || !mime || !out || !len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*mime = NULL;
|
||||
*out = NULL;
|
||||
*len = 0;
|
||||
|
||||
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
path = StrConcat(3, "/_matrix/media/v3/download/", uri->host, uri->path);
|
||||
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
ASAuthenticateRequest(c, cctx);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
|
||||
*mime = StrDuplicate(
|
||||
HashMapGet(HttpResponseHeaders(cctx), "content-type")
|
||||
);
|
||||
stream = HttpClientStream(cctx);
|
||||
fake = StreamFile(open_memstream(&buf, len));
|
||||
StreamCopy(stream, fake);
|
||||
StreamClose(fake);
|
||||
|
||||
*out = Malloc(*len);
|
||||
memcpy(*out, buf, *len);
|
||||
free(buf);
|
||||
|
||||
HttpClientContextFree(cctx);
|
||||
UriFree(uri);
|
||||
Free(path);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,3 +136,72 @@ ASGetName(const ParseeConfig *c, char *room, char *user)
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
char *
|
||||
ASGetAvatar(const ParseeConfig *c, char *room, char *user)
|
||||
{
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
char *path = NULL, *ret = NULL;
|
||||
char *u2 = user;
|
||||
if (!c || !user)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (room)
|
||||
{
|
||||
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, "avatar_url"))
|
||||
);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
user = u2;
|
||||
|
||||
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from room, got %s", ret);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/profile/", user, "/avatar_url"
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(user);
|
||||
user = u2;
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "avatar_url"))
|
||||
);
|
||||
StreamFlush(StreamStderr());
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from profile, got %s", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ Main(Array *args, HashMap *env)
|
|||
);
|
||||
ParseePrintASCII();
|
||||
Log(LOG_INFO, "=======================");
|
||||
Log(LOG_INFO, "(C)opyright 2023 LDA");
|
||||
Log(LOG_INFO, "(This program is free software, see LICENSE.)");
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
static const char *
|
||||
GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to);
|
||||
|
||||
static void
|
||||
static char *
|
||||
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name)
|
||||
{
|
||||
char *sender = GrabString(event, 1, "sender");
|
||||
|
|
@ -50,7 +50,7 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name)
|
|||
|
||||
ParseePushNickTable(muc, sender, nick);
|
||||
Free(nick);
|
||||
Free(rev);
|
||||
return (rev);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -98,13 +98,71 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
|||
{
|
||||
char *muc = ParseeGetMUCID(data, chat_id);
|
||||
char *name = ASGetName(data->config, room_id, state_key);
|
||||
char *avatar = ASGetAvatar(data->config, room_id, state_key);
|
||||
char *jabber = JoinMUC(data, event, jid, muc, name);
|
||||
|
||||
JoinMUC(data, event, jid, muc, name);
|
||||
Log(LOG_DEBUG, "MATRIX: Joining as '%s' (avatar=%s)", jabber, avatar);
|
||||
|
||||
Free(avatar);
|
||||
Free(jabber);
|
||||
Free(name);
|
||||
Free(muc);
|
||||
|
||||
/* TODO: XEP-0084 magic to advertise a new avatar if possible. */
|
||||
}
|
||||
else
|
||||
{
|
||||
char *avatar = ASGetAvatar(data->config, room_id, state_key);
|
||||
char *sha = NULL, *mime = NULL, *url = NULL;
|
||||
char *full_jid = StrConcat(3,
|
||||
jid, "@", data->config->component_host
|
||||
);
|
||||
XMLElement *elem, *pevent, *items, *item, *meta, *info;
|
||||
|
||||
Log(LOG_DEBUG, "MATRIX: Got local user '%s'(mxid=%s avatar=%s)", jid, state_key, avatar);
|
||||
|
||||
url = ParseeToUnauth(data, avatar);
|
||||
elem = XMLCreateTag("message");
|
||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||
{
|
||||
#define PUBSUB "http://jabber.org/protocol/pubsub"
|
||||
#define AVATAR "urn:xmpp:avatar:metadata"
|
||||
pevent = XMLCreateTag("event");
|
||||
XMLAddAttr(pevent, "xmlns", PUBSUB "#event");
|
||||
{
|
||||
items = XMLCreateTag("items");
|
||||
item = XMLCreateTag("item");
|
||||
XMLAddAttr(items, "node", AVATAR);
|
||||
XMLAddAttr(item, "id", sha);
|
||||
{
|
||||
meta = XMLCreateTag("metadata");
|
||||
info = XMLCreateTag("info");
|
||||
XMLAddAttr(meta, "xmlns", AVATAR);
|
||||
|
||||
XMLAddAttr(info, "id", sha);
|
||||
XMLAddAttr(info, "url", url);
|
||||
XMLAddAttr(info, "type", mime);
|
||||
|
||||
XMLAddChild(meta, info);
|
||||
XMLAddChild(item, meta);
|
||||
}
|
||||
XMLAddChild(items, item);
|
||||
XMLAddChild(pevent, items);
|
||||
}
|
||||
XMLAddChild(elem, pevent);
|
||||
#undef PUBSUB
|
||||
}
|
||||
|
||||
/* TODO: Broadcast PEP avatar change */
|
||||
ParseeBroadcastStanza(data, full_jid, elem);
|
||||
XMLFreeElement(elem);
|
||||
|
||||
Free(full_jid);
|
||||
Free(avatar);
|
||||
Free(mime);
|
||||
Free(sha);
|
||||
Free(url);
|
||||
}
|
||||
Free(jid);
|
||||
Free(chat_id);
|
||||
}
|
||||
|
|
@ -263,7 +321,7 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
}
|
||||
|
||||
matrix_name = ASGetName(data->config, room_id, matrix_sender);
|
||||
JoinMUC(data, event, *from, muc_id, matrix_name);
|
||||
Free(JoinMUC(data, event, *from, muc_id, matrix_name));
|
||||
*to = muc_id;
|
||||
|
||||
Free(matrix_name);
|
||||
|
|
@ -350,12 +408,8 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* TODO: Check the name's validity.
|
||||
* 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);
|
||||
|
||||
JoinMUC(data, event, encoded_from, muc_id, name);
|
||||
Free(JoinMUC(data, event, encoded_from, muc_id, name));
|
||||
|
||||
to = muc_id;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ static bool
|
|||
IsPubsubRequest(XMLElement *stanza)
|
||||
{
|
||||
char *type = HashMapGet(stanza ? stanza->attrs : NULL, "type");
|
||||
XMLElement *pubsub, *subscribe;
|
||||
XMLElement *pubsub;
|
||||
if (!stanza)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -58,7 +58,6 @@ IsPubsubRequest(XMLElement *stanza)
|
|||
return false;
|
||||
}
|
||||
|
||||
Log(LOG_INFO, "WOAH");
|
||||
return XMLookForUnique(pubsub, "subscribe");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
#include "XMPPThread/internal.h"
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Base64.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Db.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static char *
|
||||
SubscriptionHash(ParseeData *data, char *from, char *to)
|
||||
{
|
||||
|
|
@ -10,14 +19,28 @@ SubscriptionHash(ParseeData *data, char *from, char *to)
|
|||
len = strlen(from) + 1 + strlen(to);
|
||||
sum = Malloc(len);
|
||||
memset(sum, 0x00, len);
|
||||
memcpy(sum[0], from, strlen(from)):
|
||||
memcpy(sum[strlen(from) + 1], to, strlen(to));
|
||||
memcpy(&sum[0], from, strlen(from));
|
||||
memcpy(&sum[strlen(from) + 1], to, strlen(to));
|
||||
|
||||
hash = ParseeHMAC(data->id, sum, len);
|
||||
hash = Base64Encode(sum, len);
|
||||
Free(sum);
|
||||
|
||||
return hash;
|
||||
}
|
||||
static void
|
||||
DecodeSubscription(ParseeData *data, char *hash, char **from, char **to)
|
||||
{
|
||||
char *sum;
|
||||
if (!data || !hash || !from || !to)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sum = Base64Decode(hash, strlen(hash));
|
||||
*from = StrDuplicate(sum);
|
||||
*to = StrDuplicate(sum + strlen(sum) + 1);
|
||||
Free(sum);
|
||||
}
|
||||
|
||||
void
|
||||
AddPresenceSubscriber(ParseeData *data, char *from, char *to)
|
||||
|
|
@ -32,13 +55,18 @@ AddPresenceSubscriber(ParseeData *data, char *from, char *to)
|
|||
|
||||
database = data->db;
|
||||
hash = SubscriptionHash(data, from, to);
|
||||
ref = DbCreate(database, 2, "subscriptions", hash);
|
||||
ref = DbCreate(database, 2, "subs", hash);
|
||||
if (!ref)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
HashMapSet(DbRef(ref), "from", JsonValueString(from));
|
||||
HashMapSet(DbRef(ref), "to", JsonValueString(to));
|
||||
HashMapSet(DbJson(ref), "from", JsonValueString(from));
|
||||
HashMapSet(DbJson(ref), "to", JsonValueString(to));
|
||||
/* I don't think we need more information right now */
|
||||
|
||||
DbClose(database, ref);
|
||||
end:
|
||||
DbUnlock(database, ref);
|
||||
Free(hash);
|
||||
}
|
||||
|
||||
|
|
@ -48,15 +76,73 @@ IsSubscribed(ParseeData *data, char *user, char *to)
|
|||
Db *database;
|
||||
char *hash;
|
||||
bool ret;
|
||||
if (!data || !from || !to)
|
||||
if (!data || !user || !to)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
database = data->db;
|
||||
hash = SubscriptionHash(data, from, to);
|
||||
ret = DbExists(database, 2, "subscriptions", hash);
|
||||
hash = SubscriptionHash(data, user, to);
|
||||
ret = DbExists(database, 2, "subs", hash);
|
||||
Free(hash);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ParseeBroadcastStanza(ParseeData *data, char *from, XMLElement *stanza)
|
||||
{
|
||||
XMPPComponent *jabber = data ? data->jabber : NULL;
|
||||
Array *entries;
|
||||
size_t i;
|
||||
if (!data || !from || !stanza)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy our stanza so that we can freely modify it */
|
||||
stanza = XMLCopy(stanza);
|
||||
|
||||
/* Start doing a storm on Mt. Subs. */
|
||||
entries = DbList(data->db, 1, "subs");
|
||||
for (i = 0; i < ArraySize(entries); i++)
|
||||
{
|
||||
char *entry = ArrayGet(entries, i);
|
||||
char *entry_from = NULL, *entry_to = NULL;
|
||||
char *storm_id; /* ooe */
|
||||
XMLElement *sub;
|
||||
|
||||
DecodeSubscription(data, entry, &entry_from, &entry_to);
|
||||
|
||||
if (!StrEquals(entry_to, from))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
Log(LOG_DEBUG,
|
||||
"PRESENCE SYSTEM: "
|
||||
"We should be brotkasting straight to %s (from %s)",
|
||||
entry_from, from
|
||||
);
|
||||
sub = XMLCopy(stanza);
|
||||
XMLAddAttr(sub, "from", from);
|
||||
XMLAddAttr(sub, "to", entry_from);
|
||||
|
||||
/* TODO: Should we store IDs somewhere? */
|
||||
XMLAddAttr(sub, "id", (storm_id = StrRandom(16)));
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, sub);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
|
||||
XMLFreeElement(sub);
|
||||
Free(storm_id);
|
||||
end:
|
||||
Free(entry_from);
|
||||
Free(entry_to);
|
||||
}
|
||||
DbListFree(entries);
|
||||
XMLFreeElement(stanza);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@ void
|
|||
IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||
{
|
||||
XMPPComponent *jabber = args->jabber;
|
||||
XMLElement *pubsub;
|
||||
char *from = HashMapGet(stanza->attrs, "from");
|
||||
char *to = HashMapGet(stanza->attrs, "to");
|
||||
char *id = HashMapGet(stanza->attrs, "id");
|
||||
|
|
@ -395,6 +396,68 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
XMLFreeElement(iqVCard);
|
||||
}
|
||||
}
|
||||
#define PS "http://jabber.org/protocol/pubsub"
|
||||
else if ((pubsub = XMLookForTKV(stanza, "pubsub", "xmlns", PS)))
|
||||
{
|
||||
/* TODO: Pass this through the PEP manager */
|
||||
XMLElement *a_items = XMLookForTKV(pubsub,
|
||||
"items", "node", "urn:xmpp:avatar:data"
|
||||
);
|
||||
if (a_items)
|
||||
{
|
||||
/* Do, without regret, start shoving an avatar out the bus */
|
||||
char *to_matrix = ParseeDecodeMXID(to);
|
||||
char *avatar = ASGetAvatar(args->config, NULL, to_matrix);
|
||||
char *buf, *mime;
|
||||
char *b64;
|
||||
size_t len;
|
||||
XMLElement *reply;
|
||||
|
||||
ASGrab(args->config, avatar, &mime, &buf, &len);
|
||||
b64 = Base64Encode(buf, len);
|
||||
Free(buf);
|
||||
|
||||
Log(LOG_INFO, "FM=%s", to_matrix);
|
||||
Log(LOG_INFO, "B=%s (%dB)", b64, (int) len);
|
||||
/* Strike back with a response */
|
||||
reply = XMLCreateTag("iq");
|
||||
XMLAddAttr(reply, "type", "result");
|
||||
XMLAddAttr(reply, "to", from);
|
||||
XMLAddAttr(reply, "from", to);
|
||||
XMLAddAttr(reply, "id", HashMapGet(stanza->attrs, "id"));
|
||||
{
|
||||
XMLElement *ps = XMLCreateTag("pubsub");
|
||||
XMLElement *items = XMLCreateTag("items");
|
||||
XMLAddAttr(ps, "xmlns", PS);
|
||||
XMLAddAttr(items, "node", "urn:xmpp:avatar:data");
|
||||
{
|
||||
XMLElement *item = XMLCreateTag("item");
|
||||
XMLElement *data = XMLCreateTag("data");
|
||||
XMLAddAttr(item, "id", "TODO");
|
||||
XMLAddAttr(data, "xmlns", "urn:xmpp:avatar:data");
|
||||
|
||||
XMLAddChild(data, XMLCreateText(b64));
|
||||
|
||||
XMLAddChild(item, data);
|
||||
XMLAddChild(items, item);
|
||||
}
|
||||
XMLAddChild(ps, items);
|
||||
XMLAddChild(reply, ps);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, reply);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMLFreeElement(reply);
|
||||
|
||||
Free(to_matrix);
|
||||
Free(avatar);
|
||||
Free(mime);
|
||||
Free(b64);
|
||||
}
|
||||
}
|
||||
#undef PS
|
||||
else if (XMLookForTKV(stanza, "query", "xmlns", DISCO))
|
||||
{
|
||||
IQDiscoGet(args, jabber, stanza);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,17 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
Log(LOG_DEBUG, "<message/> usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
/*{
|
||||
XMLElement *foo = XMLCreateTag("message");
|
||||
XMLElement *body = XMLCreateTag("body");
|
||||
XMLAddAttr(foo, "type", "chat");
|
||||
XMLAddChild(foo, body);
|
||||
XMLAddChild(body, XMLCreateText("Storm on Mt. Ooe (sorry if you see this)"));
|
||||
|
||||
BroadcastStanza(args, HashMapGet(stanza->attrs, "to"), foo);
|
||||
XMLFreeElement(foo);
|
||||
}*/
|
||||
|
||||
|
||||
if (ServerHasXEP421(args, from))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -141,6 +141,14 @@ extern void ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, ch
|
|||
* Modifies: NOTHING */
|
||||
extern char * ASGetName(const ParseeConfig *c, char *room, char *user);
|
||||
|
||||
/** Returns the user's avatar in a room, or a the global user avatar, to be
|
||||
* Free'd
|
||||
* -------------
|
||||
* Returns: The user's name in the [HEAP] | NULL
|
||||
* Thrasher: Free
|
||||
* Modifies: NOTHING */
|
||||
extern char * ASGetAvatar(const ParseeConfig *c, char *room, char *user);
|
||||
|
||||
/** Uploads data to Matrix to be used later
|
||||
* ----------------
|
||||
* Returns: A valid MXC URI[HEAP] | NULL
|
||||
|
|
@ -170,4 +178,16 @@ extern Array * ASGetRelations(const ParseeConfig *c, size_t n, char *room, char
|
|||
* Thrashes: {relations}
|
||||
* See-Also: ASGetRelations */
|
||||
extern void ASFreeRelations(Array *relations);
|
||||
|
||||
/** Returns the MIME and SHA-1 hash of a media entry, in one fell swoop.
|
||||
* -----------------
|
||||
* Returns: whenever the media exists
|
||||
* Modifies: {mime}[HEAP], {sha}[HEAP] */
|
||||
extern bool ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha);
|
||||
|
||||
/** Retrieves media off an MXC link.
|
||||
* ------------
|
||||
* Returns: whenever the media exists
|
||||
* Modifies {mime}[HEAP], {out}[HEAP], {len} */
|
||||
extern bool ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -401,4 +401,10 @@ extern void ParseeSetThreads(int xmpp, int http);
|
|||
extern char * ParseeHMAC(char *key, uint8_t *msg, size_t msglen);
|
||||
#define ParseeHMACS(key, msg) ParseeHMAC(key, (uint8_t *) msg, strlen(msg))
|
||||
|
||||
/** Broadcasts a stanza from a user to any that may be interested by it
|
||||
* (PEP or subscription)
|
||||
* -------------------------------------
|
||||
* Returns: NOTHING */
|
||||
extern void ParseeBroadcastStanza(ParseeData *data, char *from, XMLElement *s);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue