[ADD/WIP] Editing support XMPP->Matrix

This commit is contained in:
LDA 2024-06-25 17:20:00 +02:00
commit ae740878c8
13 changed files with 249 additions and 106 deletions

View file

@ -129,3 +129,31 @@ MatrixGetReply(HashMap *event)
"event_id")
));
}
HashMap *
MatrixCreateReplace(char *event, char *body)
{
HashMap *map;
HashMap *new;
HashMap *rel;
if (!body || !event)
{
return NULL;
}
map = HashMapCreate();
HashMapSet(map, "msgtype", JsonValueString("m.text"));
HashMapSet(map, "body", JsonValueString(body));
new = HashMapCreate();
HashMapSet(new, "msgtype", JsonValueString("m.text"));
HashMapSet(new, "body", JsonValueString(body));
rel = HashMapCreate();
HashMapSet(rel, "rel_type", JsonValueString("m.replace"));
HashMapSet(rel, "event_id", JsonValueString(event));
HashMapSet(map, "m.new_content", JsonValueObject(new));
HashMapSet(map, "m.relates_to", JsonValueObject(rel));
return map;
}

View file

@ -18,7 +18,6 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
char *sender = GrabString(event, 1, "sender");
char *chat_id;
const ParseeConfig *conf = data->config;
Log(LOG_INFO, "Membership '%s'->'%s'", state_key, membership);
if (StrEquals(membership, "invite") && ParseeIsPuppet(conf, state_key))
{
@ -84,11 +83,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
return;
}
if (reply_id)
{
Log(LOG_INFO, "reply=%s", reply_id);
}
ref = DbLock(data->db, 3, "rooms", id, "data");
json = DbJson(ref);
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
@ -125,8 +119,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
if (reply_id)
{
ParseeGetStanzaInfo(data, chat_id, reply_id, &stanza, &sender);
Log(LOG_INFO, "Replying to %s by %s", stanza, sender);
}
XMPPJoinMUC(jabber, jid, rev);
XMPPSendPlain(

View file

@ -285,3 +285,137 @@ ParseeXMPPify(HashMap *event)
return xepd;
}
void
ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, char *ev, char *sender)
{
DbRef *ref;
HashMap *j;
HashMap *stanzas, *obj, *events, *ids;
bool new_stanzas = false, new_events = false;
bool new_ids = false;
uint64_t age = UtilTsMillis();
if (!data || !chat_id || !stanza_id || !ev || !sender)
{
return;
}
ref = DbLock(data->db, 2, "chats", chat_id);
j = DbJson(ref);
if (!ref)
{
return;
}
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
if (!stanzas)
{
stanzas = HashMapCreate();
new_stanzas = true;
}
obj = HashMapCreate();
HashMapSet(obj, "age", JsonValueInteger(age));
JsonValueFree(HashMapSet(stanzas, stanza_id, JsonValueObject(obj)));
if (new_stanzas)
{
HashMapSet(j, "stanzas", JsonValueObject(stanzas));
}
events = JsonValueAsObject(HashMapGet(j, "events"));
if (!events)
{
events = HashMapCreate();
new_events = true;
}
obj = HashMapCreate();
HashMapSet(obj, "stanza", JsonValueString(stanza_id));
HashMapSet(obj, "origin", JsonValueString(id));
HashMapSet(obj, "sender", JsonValueString(sender));
JsonValueFree(HashMapSet(events, ev, JsonValueObject(obj)));
if (new_events)
{
HashMapSet(j, "events", JsonValueObject(events));
}
if (id)
{
ids = JsonValueAsObject(HashMapGet(j, "ids"));
if (!ids)
{
ids = HashMapCreate();
new_ids = true;
}
obj = HashMapCreate();
HashMapSet(obj, "stanza", JsonValueString(stanza_id));
HashMapSet(obj, "event", JsonValueString(ev));
JsonValueFree(HashMapSet(ids, id, JsonValueObject(obj)));
if (new_ids)
{
HashMapSet(j, "ids", JsonValueObject(ids));
}
}
DbUnlock(data->db, ref);
}
bool
ParseeVerifyStanza(ParseeData *data, char *chat_id, char *stanza_id)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *stanzas = NULL;
bool ret = true;
if (!data || !chat_id || !stanza_id)
{
return true;
}
ref = DbLock(data->db, 2, "chats", chat_id);
j = DbJson(ref);
if (!ref)
{
goto end;
}
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
if (!stanzas)
{
goto end;
}
ret = !HashMapGet(stanzas, stanza_id);
end:
DbUnlock(data->db, ref);
return ret;
}
char *
ParseeEventFromID(ParseeData *data, char *chat_id, char *id)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *ids = NULL;
char *ret = NULL;
if (!data || !chat_id || !id)
{
return NULL;
}
ref = DbLock(data->db, 2, "chats", chat_id);
j = DbJson(ref);
if (!ref)
{
goto end;
}
ids = JsonValueAsObject(HashMapGet(j, "ids"));
if (!ids)
{
goto end;
}
ret = JsonValueAsString(JsonGet(ids, 2, id, "event"));
ret = StrDuplicate(ret);
end:
DbUnlock(data->db, ref);
return ret;
}

View file

@ -540,89 +540,6 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
return ret;
}
void
ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *ev, char *sender)
{
DbRef *ref;
HashMap *j;
HashMap *stanzas, *obj, *events;
bool new_stanzas = false, new_events = false;
uint64_t age = UtilTsMillis();
if (!data || !chat_id || !stanza_id || !ev || !sender)
{
return;
}
ref = DbLock(data->db, 2, "chats", chat_id);
j = DbJson(ref);
if (!ref)
{
return;
}
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
if (!stanzas)
{
stanzas = HashMapCreate();
new_stanzas = true;
}
obj = HashMapCreate();
HashMapSet(obj, "age", JsonValueInteger(age));
JsonValueFree(HashMapSet(stanzas, stanza_id, JsonValueObject(obj)));
if (new_stanzas)
{
HashMapSet(j, "stanzas", JsonValueObject(stanzas));
}
events = JsonValueAsObject(HashMapGet(j, "events"));
if (!events)
{
events = HashMapCreate();
new_events = true;
}
obj = HashMapCreate();
HashMapSet(obj, "stanza", JsonValueString(stanza_id));
HashMapSet(obj, "sender", JsonValueString(sender));
JsonValueFree(HashMapSet(events, ev, JsonValueObject(obj)));
if (new_events)
{
HashMapSet(j, "events", JsonValueObject(events));
}
DbUnlock(data->db, ref);
}
bool
ParseeVerifyStanza(ParseeData *data, char *chat_id, char *stanza_id)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *stanzas = NULL;
bool ret = true;
if (!data || !chat_id || !stanza_id)
{
return true;
}
ref = DbLock(data->db, 2, "chats", chat_id);
j = DbJson(ref);
if (!ref)
{
goto end;
}
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
if (!stanzas)
{
goto end;
}
ret = !HashMapGet(stanzas, stanza_id);
end:
DbUnlock(data->db, ref);
return ret;
}
void
ParseeSendPresence(ParseeData *data)

View file

@ -35,9 +35,7 @@ RouteHead(RouteTxns, arr, argp)
for (i = 0; i < ArraySize(events); i++)
{
HashMap *event = JsonValueAsObject(ArrayGet(events, i));
event = JsonDuplicate(event);
ParseeEventHandler(args->data, event);
JsonFree(event);
}
/* TODO: Store TXN ID somewhere so that we can commit

View file

@ -34,6 +34,7 @@ SignalHandler(int signal)
Log(LOG_INFO, "Killing thread...");
XMPPKillThread(jabber, "killer");
pthread_join(xmpp_thr, NULL);
Log(LOG_INFO, "Stopping server...");
HttpServerStop(server);
break;
}

View file

@ -137,7 +137,7 @@ XMLookForTKV(XMLElement *parent, char *tag, char *k, char *v)
{
size_t i;
if (!parent || !tag || !k || !v)
if (!parent || !k || !v)
{
return NULL;
}
@ -147,11 +147,14 @@ XMLookForTKV(XMLElement *parent, char *tag, char *k, char *v)
XMLElement *child = ArrayGet(parent->children, i);
HashMap *attrs = child->attrs;
char *value = HashMapGet(attrs, k);
if (StrEquals(child->name, tag) && StrEquals(value, v))
if (StrEquals(child->name, tag) || !tag)
{
if (StrEquals(value, v))
{
return child;
}
}
}
return NULL;
}

View file

@ -730,12 +730,14 @@ XMLParseAttQuote(XMLexer *lexer)
char *code = NULL;
int c2;
int p2 = XMLInitialiseBuffer(lexer);
while ((c2 = XMLGetc(lexer)) && c2 != EOF)
int j = 0;
while ((c2 = XMLGetc(lexer)) && c2 != EOF && j < 8)
{
if (c2 == ';')
{
break;
}
j++;
}
if (c2 != ';')
{
@ -775,12 +777,14 @@ XMLParseAttDouble(XMLexer *lexer)
char *code = NULL;
int c2;
int p2 = XMLInitialiseBuffer(lexer);
while ((c2 = XMLGetc(lexer)) && c2 != EOF)
int j = 0;
while ((c2 = XMLGetc(lexer)) && c2 != EOF && j < 8)
{
if (c2 == ';')
{
break;
}
j++;
}
if (c2 != ';')
{

View file

@ -199,3 +199,38 @@ XMPPIsParseeStanza(XMLElement *stanza)
return !!XMLookForUnique(stanza, "x-parsee");
}
char *
XMPPGetStanzaID(XMLElement *stanza)
{
XMLElement *stanza_id;
if (!stanza)
{
return NULL;
}
stanza_id = XMLookForTKV(stanza, "stanza-id", "xmlns", "urn:xmpp:sid:0");
return stanza_id ? HashMapGet(stanza_id->attrs, "id") : NULL;
}
char *
XMPPGetOriginID(XMLElement *stanza)
{
XMLElement *origin_id;
if (!stanza)
{
return NULL;
}
origin_id = XMLookForTKV(stanza, "origin-id", "xmlns", "urn:xmpp:sid:0");
return origin_id ? HashMapGet(origin_id->attrs, "id") : NULL;
}
char *
XMPPGetReplacedID(XMLElement *stanza)
{
XMLElement *replace = XMLookForTKV(
stanza, "replace",
"xmlns", "urn:xmpp:message-correct:0"
);
return replace ? HashMapGet(replace->attrs, "id") : NULL;
}

View file

@ -40,7 +40,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
XMLElement *body = NULL;
XMLElement *data = NULL;
XMLElement *stanza_id = NULL;
char *to, *room, *from, *from_matrix;
char *chat_id, *mroom_id;
@ -51,7 +50,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
XMLFreeElement(stanza);
return false;
}
stanza_id = XMLookForUnique(stanza, "stanza-id");
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
from = HashMapGet(stanza->attrs, "from");
@ -70,11 +68,14 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
{
char *res = ParseeGetResource(from);
char *encoded = ParseeEncodeJID(args->config, from, false);
char *s_id_str = HashMapGet(stanza_id->attrs, "id");
char *s_id_str = XMPPGetStanzaID(stanza);
char *o_id_str = XMPPGetOriginID(stanza);
char *id_str = HashMapGet(stanza->attrs, "id");
char *event_id;
char *replaced = XMPPGetReplacedID(stanza);
/* TODO: Create smarter puppet names */
if (ParseeVerifyStanza(args, chat_id, s_id_str))
if (ParseeVerifyStanza(args, chat_id, s_id_str) && !replaced)
{
XMLElement *oob, *oob_data;
@ -108,7 +109,18 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
"m.room.message", MatrixCreateMessage(data->data)
);
}
ParseePushStanza(args, chat_id, s_id_str, event_id, from);
ParseePushStanza(args, chat_id, s_id_str, id_str, event_id, from);
Free(event_id);
}
else if (replaced)
{
event_id = ParseeEventFromID(args, chat_id, replaced);
Log(LOG_WARNING, "RETRACTION REQUEST TO %s->%s", replaced, event_id);
Free(ASSend(
args->config, mroom_id, encoded,
"m.room.message", MatrixCreateReplace(event_id, data->data)
));
Free(event_id);
}
@ -154,8 +166,10 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
} \
while (0)
AdvertiseSimple("urn:xmpp:reply:0");
AdvertiseSimple("urn:xmpp:message-correct:0");
AdvertiseSimple("urn:xmpp:styling:0");
AdvertiseSimple("urn:xmpp:reply:0");
AdvertiseSimple("urn:xmpp:sid:0");
AdvertiseSimple("urn:parsee:x-parsee:0");
AdvertiseSimple("urn:parsee:jealousy:0");
@ -234,6 +248,7 @@ ParseeXMPPThread(void *argp)
from = HashMapGet(stanza->attrs, "from");
if (!strncmp(from, killer, strlen(killer)))
{
Log(LOG_INFO, "Killer detected.");
XMLFreeElement(stanza);
break;
}

View file

@ -12,6 +12,9 @@ extern HashMap * MatrixCreateNotice(char *body);
/* Creates the content for a normal message. */
extern HashMap * MatrixCreateMessage(char *body);
/* Creates the content for a replace message. */
extern HashMap * MatrixCreateReplace(char *event, char *body);
/* Creates the content for a media file. */
extern HashMap * MatrixCreateMedia(char *mxc, char *body, char *mime);

View file

@ -145,7 +145,7 @@ extern char * ParseeGetRoomID(ParseeData *, char *chat_id);
extern char * ParseeGetMUCID(ParseeData *, char *chat_id);
/* Pushes a stanza ID to a chat ID */
extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *event, char *sender);
extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *origin_id, char *event, char *sender);
/* Checks if a stanza is not duplicated in a chat ID */
extern bool ParseeVerifyStanza(ParseeData *, char *chat_id, char *stanza_id);
@ -167,4 +167,7 @@ extern int ParseeFindDatastart(char *data);
/* XMPP-ifies a message event to XEP-0393 if possible. */
extern char * ParseeXMPPify(HashMap *event);
/* Finds an event ID from an ID in the stanza's attributes */
extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id);
#endif

View file

@ -61,4 +61,14 @@ extern void XMPPFreeMUCInfo(MUCInfo info);
/* Checks if a stanza has an x-parsee element */
extern bool XMPPIsParseeStanza(XMLElement *);
/* Returns the stanza ID of a stanza, if existent */
extern char * XMPPGetStanzaID(XMLElement *);
/* Returns the origin ID of a stanza, if existent */
extern char * XMPPGetOriginID(XMLElement *);
/* Returns the origin ID of the replaced stanza, if the current one
* is a replacement notice */
extern char * XMPPGetReplacedID(XMLElement *);
#endif