mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 16:55:10 +00:00
[ADD/WIP] One-way chatstates, capabilities
No avatarwerk today because avatars are an absolute sore.
This commit is contained in:
parent
59d5ad6780
commit
6b0f08c49e
10 changed files with 349 additions and 24 deletions
|
|
@ -32,3 +32,10 @@ TODO
|
||||||
|
|
||||||
## DOCS
|
## DOCS
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
|
## TODOS
|
||||||
|
- Look at XEPS-TBD.TXT for XEPs to be done
|
||||||
|
- Achievements
|
||||||
|
### Why?
|
||||||
|
> "[...] and it [BoVeX] has an achievement system, because another thing I don't like is when software will not acknowledge that you've reached an obscure error state, essentially outsmarting it."
|
||||||
|
- Tom Murphy VII
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
XEPs current supported are in src/XMPPThread.c, at the IQ disco advertising.
|
XEPs current supported are in src/XMPPThread.c, at the IQ disco advertising.
|
||||||
|
|
||||||
Somewhat implemented XEPs:
|
Somewhat implemented XEPs:
|
||||||
|
~ https://xmpp.org/extensions/xep-0085.html
|
||||||
|
Only XMPP->Matrix
|
||||||
~ https://xmpp.org/extensions/xep-0444.html
|
~ https://xmpp.org/extensions/xep-0444.html
|
||||||
This allows reactions, which Matrix also has support to. The two
|
This allows reactions, which Matrix also has support to. The two
|
||||||
systems don't seem *too* restrictive on one-another (unlike some
|
systems don't seem *too* restrictive on one-another (unlike some
|
||||||
|
|
|
||||||
30
src/AS.c
30
src/AS.c
|
|
@ -602,3 +602,33 @@ ASReupload(const ParseeConfig *c, char *from, char **mime)
|
||||||
UriFree(uri);
|
UriFree(uri);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
void
|
||||||
|
ASType(const ParseeConfig *c, char *user, char *room, bool status)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path, *full;
|
||||||
|
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));
|
||||||
|
HashMapSet(json, "timeout", JsonValueBoolean(1 MINUTES));
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,9 +107,9 @@ ParseeCleanup(void *datp)
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
CleanupField(stanza, 1 HOURS);
|
CleanupField(stanza, 3 HOURS);
|
||||||
CleanupField(event, 1 HOURS);
|
CleanupField(event, 3 HOURS);
|
||||||
CleanupField(id, 1 HOURS);
|
CleanupField(id, 3 HOURS);
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
|
|
||||||
/* Except an IQ reply */
|
/* Except an IQ reply */
|
||||||
iq_query = XMLDecode(jabber->stream, false);
|
iq_query = XMLDecode(jabber->stream, false);
|
||||||
|
/* TODO: I've spotted presence requests spawning there. */
|
||||||
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
||||||
{
|
{
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,7 @@ XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
|
||||||
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
||||||
|
|
||||||
XMLAddChild(presence, x);
|
XMLAddChild(presence, x);
|
||||||
|
XMPPAnnotatePresence(presence);
|
||||||
|
|
||||||
XMLEncode(comp->stream, presence);
|
XMLEncode(comp->stream, presence);
|
||||||
StreamFlush(comp->stream);
|
StreamFlush(comp->stream);
|
||||||
|
|
@ -279,3 +280,23 @@ XMPPGetReply(XMLElement *elem)
|
||||||
|
|
||||||
return HashMapGet(rep->attrs, "id");
|
return HashMapGet(rep->attrs, "id");
|
||||||
}
|
}
|
||||||
|
void
|
||||||
|
XMPPAnnotatePresence(XMLElement *presence)
|
||||||
|
{
|
||||||
|
XMLElement *c;
|
||||||
|
char *ver;
|
||||||
|
if (!presence)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ver = XMPPGenerateVer();
|
||||||
|
c = XMLCreateTag("c");
|
||||||
|
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
||||||
|
XMLAddAttr(c, "hash", "sha-1");
|
||||||
|
XMLAddAttr(c, "node", REPOSITORY);
|
||||||
|
XMLAddAttr(c, "ver", ver);
|
||||||
|
|
||||||
|
Free(ver);
|
||||||
|
XMLAddChild(presence, c);
|
||||||
|
}
|
||||||
|
|
|
||||||
292
src/XMPPThread.c
292
src/XMPPThread.c
|
|
@ -4,8 +4,10 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Base64.h>
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
|
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
|
|
@ -13,15 +15,142 @@
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
#define IQ_ADVERT \
|
#define IQ_ADVERT \
|
||||||
|
AdvertiseSimple("http://jabber.org/protocol/caps") \
|
||||||
AdvertiseSimple("http://jabber.org/protocol/chatstates") \
|
AdvertiseSimple("http://jabber.org/protocol/chatstates") \
|
||||||
AdvertiseSimple("urn:xmpp:message-correct:0") \
|
AdvertiseSimple("urn:xmpp:message-correct:0") \
|
||||||
AdvertiseSimple("urn:xmpp:reactions:0") \
|
AdvertiseSimple("urn:xmpp:reactions:0") \
|
||||||
AdvertiseSimple("urn:xmpp:styling:0") \
|
AdvertiseSimple("urn:xmpp:styling:0") \
|
||||||
AdvertiseSimple("urn:xmpp:reply:0") \
|
AdvertiseSimple("urn:xmpp:reply:0") \
|
||||||
AdvertiseSimple("jabber:x:oob") \
|
AdvertiseSimple("jabber:x:oob") \
|
||||||
|
AdvertiseSimple("vcard-temp") \
|
||||||
|
AdvertiseSimple("urn:xmpp:avatar:metadata+notify") \
|
||||||
|
AdvertiseSimple("jabber:iq:version") \
|
||||||
AdvertiseSimple("urn:parsee:x-parsee:0") \
|
AdvertiseSimple("urn:parsee:x-parsee:0") \
|
||||||
AdvertiseSimple("urn:parsee:jealousy:0")
|
AdvertiseSimple("urn:parsee:jealousy:0")
|
||||||
|
|
||||||
|
/* TODO: More identities */
|
||||||
|
#define IQ_IDENTITY \
|
||||||
|
IdentitySimple("gateway", "matrix", "Parsee Matrix Gateway") \
|
||||||
|
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
|
||||||
|
IdentitySimple("component", "generic", "Parsee's component")
|
||||||
|
|
||||||
|
typedef struct XMPPIdentity {
|
||||||
|
char *category, *type, *lang, *name;
|
||||||
|
} XMPPIdentity;
|
||||||
|
|
||||||
|
static int
|
||||||
|
ICollate(unsigned char *cata, unsigned char *catb)
|
||||||
|
{
|
||||||
|
size_t al = cata ? strlen(cata) : 0;
|
||||||
|
size_t bl = catb ? strlen(catb) : 0;
|
||||||
|
|
||||||
|
if (!al && !bl)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (!al && bl)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (al && !bl)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*cata == *catb)
|
||||||
|
{
|
||||||
|
cata++;
|
||||||
|
catb++;
|
||||||
|
al--;
|
||||||
|
bl--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (*cata < *catb)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
IdentitySort(void *idap, void *idbp)
|
||||||
|
{
|
||||||
|
XMPPIdentity *ida = idap;
|
||||||
|
XMPPIdentity *idb = idbp;
|
||||||
|
unsigned char *cata = ida->category;
|
||||||
|
unsigned char *catb = idb->category;
|
||||||
|
|
||||||
|
return ICollate(cata, catb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generates a SHA-256 hash of the ver field. */
|
||||||
|
char *
|
||||||
|
XMPPGenerateVer(void)
|
||||||
|
{
|
||||||
|
char *S = NULL;
|
||||||
|
unsigned char *Sha = NULL;
|
||||||
|
Array *identities = ArrayCreate();
|
||||||
|
Array *features = ArrayCreate();
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Initialise identity table, to be sorted */
|
||||||
|
#define IdentitySimple(cat, Type, Name) { \
|
||||||
|
XMPPIdentity *id = Malloc(sizeof(*id)); \
|
||||||
|
id->category = cat; \
|
||||||
|
id->lang = NULL; \
|
||||||
|
id->type = Type; \
|
||||||
|
id->name = Name; \
|
||||||
|
ArrayAdd(identities, id); }
|
||||||
|
IQ_IDENTITY
|
||||||
|
#undef IdentitySimple
|
||||||
|
#define AdvertiseSimple(feature) ArrayAdd(features, feature);
|
||||||
|
IQ_ADVERT
|
||||||
|
#undef AdvertiseSimple
|
||||||
|
ArraySort(identities, IdentitySort);
|
||||||
|
for (i = 0; i < ArraySize(identities); i++)
|
||||||
|
{
|
||||||
|
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||||
|
char *id_chunk = StrConcat(7,
|
||||||
|
identity->category, "/",
|
||||||
|
identity->type, "/",
|
||||||
|
identity->lang, "/",
|
||||||
|
identity->name);
|
||||||
|
char *tmp = S;
|
||||||
|
S = StrConcat(3, S, id_chunk, "<");
|
||||||
|
Free(tmp);
|
||||||
|
Free(id_chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArraySort(features, ((int (*) (void *, void *)) ICollate));
|
||||||
|
for (i = 0; i < ArraySize(features); i++)
|
||||||
|
{
|
||||||
|
char *feature = ArrayGet(features, i);
|
||||||
|
char *tmp = S;
|
||||||
|
S = StrConcat(3, S, feature, "<");
|
||||||
|
Free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sha = Sha1(S);
|
||||||
|
Free(S);
|
||||||
|
S = Base64Encode(Sha, 20);
|
||||||
|
Free(Sha);
|
||||||
|
|
||||||
|
ArrayFree(features);
|
||||||
|
for (i = 0; i < ArraySize(identities); i++)
|
||||||
|
{
|
||||||
|
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||||
|
/* We don't have to do anything here. */
|
||||||
|
Free(identity);
|
||||||
|
}
|
||||||
|
ArrayFree(identities);
|
||||||
|
|
||||||
|
return S;
|
||||||
|
}
|
||||||
|
|
||||||
static pthread_mutex_t cond_var_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t cond_var_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
|
static pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
|
||||||
|
|
||||||
|
|
@ -42,6 +171,8 @@ ParseeDMHandler(char *room, char *from, XMLElement *data, const ParseeConfig *c)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Clean up all of this. We are currently separating DMs from MUCs,
|
||||||
|
* where we could unify all our code, and generalise everything. */
|
||||||
static bool
|
static bool
|
||||||
MessageStanza(ParseeData *args, XMLElement *stanza)
|
MessageStanza(ParseeData *args, XMLElement *stanza)
|
||||||
{
|
{
|
||||||
|
|
@ -54,6 +185,39 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
|
||||||
char *to, *room, *from, *from_matrix, *decode_from;
|
char *to, *room, *from, *from_matrix, *decode_from;
|
||||||
char *chat_id, *mroom_id;
|
char *chat_id, *mroom_id;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
from = HashMapGet(stanza->attrs, "from");
|
||||||
|
|
||||||
|
#define CHAT_STATES "http://jabber.org/protocol/chatstates"
|
||||||
|
if (XMLookForTKV(stanza, "composing", "xmlns", CHAT_STATES))
|
||||||
|
{
|
||||||
|
decode_from = ParseeLookupJID(from);
|
||||||
|
from_matrix = ParseeEncodeJID(args->config, decode_from, true);
|
||||||
|
chat_id = ParseeGetFromMUCID(args, from);
|
||||||
|
mroom_id = ParseeGetRoomID(args, chat_id);
|
||||||
|
|
||||||
|
ASType(args->config, from_matrix, mroom_id, true);
|
||||||
|
|
||||||
|
Free(decode_from);
|
||||||
|
Free(from_matrix);
|
||||||
|
Free(mroom_id);
|
||||||
|
Free(chat_id);
|
||||||
|
}
|
||||||
|
else if (XMLookForTKV(stanza, "active", "xmlns", CHAT_STATES))
|
||||||
|
{
|
||||||
|
decode_from = ParseeLookupJID(from);
|
||||||
|
from_matrix = ParseeEncodeJID(args->config, decode_from, true);
|
||||||
|
chat_id = ParseeGetFromMUCID(args, from);
|
||||||
|
mroom_id = ParseeGetRoomID(args, chat_id);
|
||||||
|
|
||||||
|
ASType(args->config, from_matrix, mroom_id, false);
|
||||||
|
|
||||||
|
Free(decode_from);
|
||||||
|
Free(from_matrix);
|
||||||
|
Free(mroom_id);
|
||||||
|
Free(chat_id);
|
||||||
|
}
|
||||||
|
#undef CHAT_STATES
|
||||||
body = XMLookForUnique(stanza, "body");
|
body = XMLookForUnique(stanza, "body");
|
||||||
if (!body)
|
if (!body)
|
||||||
{
|
{
|
||||||
|
|
@ -61,14 +225,13 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
|
|
||||||
from = HashMapGet(stanza->attrs, "from");
|
|
||||||
|
|
||||||
/* TODO: On semi-anonymous MUCs, it might be preferable to use a
|
/* TODO: On semi-anonymous MUCs, it might be preferable to use a
|
||||||
* form of the occupant ID as the base, as it is more unique, and
|
* form of the occupant ID as the base, as it is more unique, and
|
||||||
* less prone to trigger the character limit on Matrix.
|
* less prone to trigger the character limit on Matrix.
|
||||||
*
|
*
|
||||||
* See: https://xmpp.org/extensions/xep-0421.html */
|
* See: https://xmpp.org/extensions/xep-0421.html */
|
||||||
|
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
|
||||||
decode_from = ParseeLookupJID(from);
|
decode_from = ParseeLookupJID(from);
|
||||||
from_matrix = ParseeEncodeJID(args->config, decode_from, true);
|
from_matrix = ParseeEncodeJID(args->config, decode_from, true);
|
||||||
room = ParseeFindDMRoom(args, to, from);
|
room = ParseeFindDMRoom(args, to, from);
|
||||||
|
|
@ -205,6 +368,39 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DISCO "http://jabber.org/protocol/disco#info"
|
#define DISCO "http://jabber.org/protocol/disco#info"
|
||||||
|
static XMLElement *
|
||||||
|
IQGenerateQuery(void)
|
||||||
|
{
|
||||||
|
XMLElement *query = XMLCreateTag("query");
|
||||||
|
XMLAddAttr(query, "xmlns", DISCO);
|
||||||
|
{
|
||||||
|
XMLElement *feature;
|
||||||
|
#define IdentitySimple(c,t,n) do \
|
||||||
|
{ \
|
||||||
|
feature = XMLCreateTag("identity"); \
|
||||||
|
XMLAddAttr(feature, "category", c); \
|
||||||
|
XMLAddAttr(feature, "type", t); \
|
||||||
|
XMLAddAttr(feature, "name", n); \
|
||||||
|
XMLAddChild(query, feature); \
|
||||||
|
} \
|
||||||
|
while (0);
|
||||||
|
IQ_IDENTITY
|
||||||
|
#undef IdentitySimple
|
||||||
|
#define AdvertiseSimple(f) do \
|
||||||
|
{ \
|
||||||
|
feature = XMLCreateTag("feature"); \
|
||||||
|
XMLAddAttr(feature, "var", f); \
|
||||||
|
XMLAddChild(query, feature); \
|
||||||
|
} \
|
||||||
|
while (0);
|
||||||
|
|
||||||
|
/* TODO: Advertise more things */
|
||||||
|
IQ_ADVERT
|
||||||
|
#undef AdvertiseSimple
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
static void
|
static void
|
||||||
IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
||||||
{
|
{
|
||||||
|
|
@ -221,25 +417,17 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
||||||
XMLAddAttr(iq_reply, "from", to);
|
XMLAddAttr(iq_reply, "from", to);
|
||||||
XMLAddAttr(iq_reply, "type", "result");
|
XMLAddAttr(iq_reply, "type", "result");
|
||||||
XMLAddAttr(iq_reply, "id", id);
|
XMLAddAttr(iq_reply, "id", id);
|
||||||
{
|
|
||||||
query = XMLCreateTag("query");
|
|
||||||
XMLAddAttr(query, "xmlns", DISCO);
|
|
||||||
{
|
|
||||||
XMLElement *feature;
|
|
||||||
#define AdvertiseSimple(f) do \
|
|
||||||
{ \
|
|
||||||
feature = XMLCreateTag("feature"); \
|
|
||||||
XMLAddAttr(feature, "var", f); \
|
|
||||||
XMLAddChild(query, feature); \
|
|
||||||
} \
|
|
||||||
while (0);
|
|
||||||
|
|
||||||
/* TODO: Advertise more things */
|
query = IQGenerateQuery();
|
||||||
IQ_ADVERT
|
{
|
||||||
#undef AdvertiseSimple
|
char *ver = XMPPGenerateVer();
|
||||||
}
|
char *node = StrConcat(3, REPOSITORY, "#", ver);
|
||||||
XMLAddChild(iq_reply, query);
|
XMLAddAttr(query, "node", node);
|
||||||
|
|
||||||
|
Free(node);
|
||||||
|
Free(ver);
|
||||||
}
|
}
|
||||||
|
XMLAddChild(iq_reply, query);
|
||||||
|
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
pthread_mutex_lock(&jabber->write_lock);
|
||||||
XMLEncode(jabber->stream, iq_reply);
|
XMLEncode(jabber->stream, iq_reply);
|
||||||
|
|
@ -255,6 +443,45 @@ IQGet(ParseeData *args, XMLElement *stanza)
|
||||||
{
|
{
|
||||||
IQDiscoGet(args, jabber, stanza);
|
IQDiscoGet(args, jabber, stanza);
|
||||||
}
|
}
|
||||||
|
else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version"))
|
||||||
|
{
|
||||||
|
XMLElement *iq_reply, *query;
|
||||||
|
XMLElement *name, *version, *val;
|
||||||
|
char *from = HashMapGet(stanza->attrs, "from");
|
||||||
|
char *to = HashMapGet(stanza->attrs, "to");
|
||||||
|
char *id = HashMapGet(stanza->attrs, "id");
|
||||||
|
|
||||||
|
iq_reply = XMLCreateTag("iq");
|
||||||
|
XMLAddAttr(iq_reply, "to", from);
|
||||||
|
XMLAddAttr(iq_reply, "from", to);
|
||||||
|
XMLAddAttr(iq_reply, "type", "result");
|
||||||
|
XMLAddAttr(iq_reply, "id", id);
|
||||||
|
|
||||||
|
query = XMLCreateTag("query");
|
||||||
|
XMLAddAttr(query, "xmlns", "jabber:iq:version");
|
||||||
|
{
|
||||||
|
name = XMLCreateTag("name");
|
||||||
|
version = XMLCreateTag("version");
|
||||||
|
|
||||||
|
XMLAddChild(name, XMLCreateText(NAME));
|
||||||
|
XMLAddChild(version, XMLCreateText(VERSION));
|
||||||
|
}
|
||||||
|
XMLAddChild(query, name);
|
||||||
|
XMLAddChild(query, version);
|
||||||
|
XMLAddChild(iq_reply, query);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&jabber->write_lock);
|
||||||
|
XMLEncode(jabber->stream, iq_reply);
|
||||||
|
pthread_mutex_unlock(&jabber->write_lock);
|
||||||
|
XMLFreeElement(iq_reply);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(LOG_WARNING, "Unknown I/Q received:");
|
||||||
|
XMLEncode(StreamStdout(), stanza);
|
||||||
|
StreamPrintf(StreamStdout(),"\n");
|
||||||
|
StreamFlush(StreamStdout());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#undef DISCO
|
#undef DISCO
|
||||||
|
|
@ -282,18 +509,41 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
||||||
{
|
{
|
||||||
#define MUC_USER_NS "http://jabber.org/protocol/muc#user"
|
#define MUC_USER_NS "http://jabber.org/protocol/muc#user"
|
||||||
XMLElement *user_info;
|
XMLElement *user_info;
|
||||||
|
XMLElement *vc = XMLookForTKV(stanza, "x", "xmlns", "vcard-temp:x:update");
|
||||||
|
char *oid = HashMapGet(stanza->attrs, "from");
|
||||||
if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS)))
|
if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS)))
|
||||||
{
|
{
|
||||||
XMLElement *item = XMLookForUnique(user_info, "item");
|
XMLElement *item = XMLookForUnique(user_info, "item");
|
||||||
char *jid = item ? HashMapGet(item->attrs, "jid") : NULL;
|
char *jid = item ? HashMapGet(item->attrs, "jid") : NULL;
|
||||||
char *oid = HashMapGet(stanza->attrs, "from");
|
|
||||||
|
|
||||||
|
|
||||||
if (jid)
|
if (jid)
|
||||||
{
|
{
|
||||||
ParseePushJIDTable(oid, jid);
|
ParseePushJIDTable(oid, jid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (vc)
|
||||||
|
{
|
||||||
|
XMLElement *photo = XMLookForUnique(vc, "photo");
|
||||||
|
XMLElement *p_dat = photo ? ArrayGet(photo->children, 0) : NULL;
|
||||||
|
char *room_id = NULL, *chat_id = NULL;
|
||||||
|
|
||||||
|
if (!p_dat)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat_id = ParseeGetFromMUCID(args, oid);
|
||||||
|
room_id = ParseeGetRoomID(args, chat_id);
|
||||||
|
|
||||||
|
if (!room_id)
|
||||||
|
{
|
||||||
|
Free(chat_id);
|
||||||
|
Free(room_id);
|
||||||
|
}
|
||||||
|
/* TODO: Get the media, and shove it to the room */
|
||||||
|
Free(chat_id);
|
||||||
|
Free(room_id);
|
||||||
|
}
|
||||||
#undef MUC_USER_NS
|
#undef MUC_USER_NS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ extern HashMap * ASFind(const ParseeConfig *, char *, char *);
|
||||||
/* Sends a message event with a specific type and body.
|
/* Sends a message event with a specific type and body.
|
||||||
* Said body is freed during the function's execution. */
|
* Said body is freed during the function's execution. */
|
||||||
extern char * ASSend(const ParseeConfig *, char *, char *, char *, HashMap *);
|
extern char * ASSend(const ParseeConfig *, char *, char *, char *, HashMap *);
|
||||||
|
extern void ASType(const ParseeConfig *, char *, char *, bool);
|
||||||
|
|
||||||
/* Sets a state event with a specific type and body */
|
/* Sets a state event with a specific type and body */
|
||||||
extern void ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *event);
|
extern void ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *event);
|
||||||
|
|
|
||||||
8
src/include/Bot.h
Normal file
8
src/include/Bot.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef PARSEE_BOT_H
|
||||||
|
#define PARSEE_BOT_H
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
|
||||||
|
#define BotInitialise() profile =
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -73,4 +73,9 @@ extern char * XMPPGetReplacedID(XMLElement *);
|
||||||
/* Get the replied-to stanza ID, if existent. */
|
/* Get the replied-to stanza ID, if existent. */
|
||||||
extern char * XMPPGetReply(XMLElement *elem);
|
extern char * XMPPGetReply(XMLElement *elem);
|
||||||
|
|
||||||
|
/* Generate the B64-encoded SHA-256 hash for the 'ver' field in caps. */
|
||||||
|
extern char * XMPPGenerateVer(void);
|
||||||
|
|
||||||
|
/* Annotates a presence with https://xmpp.org/extensions/xep-0115.html */
|
||||||
|
extern void XMPPAnnotatePresence(XMLElement *presence);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue