[ADD/WIP] Start unifying DMs and MUCs

I really need to dispatch XMPP stanza management.
This commit is contained in:
LDA 2024-07-03 14:40:26 +02:00
commit 5f2c3a9cb8
10 changed files with 570 additions and 87 deletions

View file

@ -2,6 +2,7 @@
#include <AS.h> #include <AS.h>
#include <Cytoplasm/Log.h>
#include <stdlib.h> #include <stdlib.h>
CommandHead(CmdSetPL, cmd, argp) CommandHead(CmdSetPL, cmd, argp)
@ -26,3 +27,25 @@ CommandHead(CmdSetPL, cmd, argp)
)); ));
ASSetPL(data->config, room, map); ASSetPL(data->config, room, map);
} }
CommandHead(CmdSetMin, cmd, argp)
{
ParseeCmdArg *args = argp;
ParseeData *data = args->data;
char *event = HashMapGet(cmd->arguments, "event");
char *room = HashMapGet(cmd->arguments, "room");
char *pl_str = HashMapGet(cmd->arguments, "pl");
long pl = strtol(pl_str, NULL, 10);
HashMap *map;
if (!event || !pl_str)
{
return;
}
map = ASGetPL(data->config, room);
JsonValueFree(JsonSet(
map, JsonValueInteger(pl),
2, "events", event
));
ASSetPL(data->config, room, map);
}

View file

@ -23,7 +23,7 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
if (StrEquals(membership, "invite") && ParseeIsPuppet(conf, state_key)) if (StrEquals(membership, "invite") && ParseeIsPuppet(conf, state_key))
{ {
DbRef *ref; DbRef *ref = NULL;
HashMap *json; HashMap *json;
char *jid; char *jid;
bool direct = GrabBoolean(event, 2, "content", "is_direct"); bool direct = GrabBoolean(event, 2, "content", "is_direct");
@ -125,18 +125,24 @@ static void
ParseeMessageHandler(ParseeData *data, HashMap *event) ParseeMessageHandler(ParseeData *data, HashMap *event)
{ {
XMPPComponent *jabber = data->jabber; XMPPComponent *jabber = data->jabber;
DbRef *ref; DbRef *ref = NULL;
HashMap *json; HashMap *json;
char *msgtype = GrabString(event, 2, "content", "msgtype"); char *msgtype = GrabString(event, 2, "content", "msgtype");
char *body = GrabString(event, 2, "content", "body"); char *body = GrabString(event, 2, "content", "body");
char *id = GrabString(event, 1, "room_id"); char *id = GrabString(event, 1, "room_id");
char *ev_id = GrabString(event, 1, "event_id"); char *ev_id = GrabString(event, 1, "event_id");
char *sender = GrabString(event, 1, "sender"); char *m_sender = GrabString(event, 1, "sender");
char *chat_id, *muc_id, *jid; char *chat_id, *muc_id, *jid;
char *reply_id = MatrixGetReply(event); char *reply_id = MatrixGetReply(event);
char *xepd = ParseeXMPPify(event); char *xepd = ParseeXMPPify(event);
char *cmd_lp = data->config->sender_localpart; char *cmd_lp = data->config->sender_localpart;
char *type, *user, *xmppified_user = NULL, *to;
char *unauth = NULL;
char *origin_id = NULL, *stanza = NULL;
char *sender = NULL;
char *unedited_id = MatrixGetEdit(event);
char *url = GrabString(event, 2, "content", "url");
bool direct = false; bool direct = false;
@ -146,13 +152,14 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
json = DbJson(ref); json = DbJson(ref);
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct")); direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
if (ParseeIsPuppet(data->config, sender) || if (ParseeIsPuppet(data->config, m_sender) ||
ParseeManageBan(data, sender, id)) ParseeManageBan(data, m_sender, id))
{ {
DbUnlock(data->db, ref); DbUnlock(data->db, ref);
Free(chat_id); Free(chat_id);
Free(reply_id); Free(reply_id);
Free(xepd); Free(xepd);
Free(unedited_id);
return; return;
} }
@ -164,74 +171,99 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
Free(chat_id); Free(chat_id);
Free(reply_id); Free(reply_id);
Free(xepd); Free(xepd);
Free(unedited_id);
return; return;
} }
/* TODO: Reunite DM/MUC code to bring feature parity. */
type = direct ? "chat" : "groupchat";
user = GrabString(json, 1, "xmpp_user");
unauth = ParseeToUnauth(data, url);
if (direct) if (direct)
{ {
char *user = GrabString(json, 1, "xmpp_user"); xmppified_user = ParseeEncodeMXID(m_sender);
char *local = ParseeEncodeMXID(sender); to = StrDuplicate(user);
Log(LOG_INFO," Sending to %s as %s", user, local);
XMPPSendPlain(jabber, local, user, body, "chat", NULL, NULL, ev_id, NULL, NULL);
DbUnlock(data->db, ref);
Free(chat_id); Free(chat_id);
Free(local);
Free(reply_id);
Free(xepd);
return;
} }
else
DbUnlock(data->db, ref); {
char *name, *rev, *stanza;
/* Try to find the chat ID */ /* Try to find the chat ID */
muc_id = ParseeGetMUCID(data, chat_id); muc_id = ParseeGetMUCID(data, chat_id);
if (!chat_id) if (!chat_id)
{ {
Free(reply_id); goto end;
Free(xepd);
return;
} }
jid = ParseeEncodeMXID(sender); xmppified_user = ParseeEncodeMXID(m_sender);
{
/* TODO: Check the name's validity */
char *name = ASGetName(data->config, id, sender);
char *rev = StrConcat(4, muc_id, "/", name, "[p]");
char *stanza = NULL, *sender = NULL;
char *url = GrabString(event, 2, "content", "url");
char *unauth = ParseeToUnauth(data, url);
char *unedited_id = MatrixGetEdit(event); /* TODO: Check the name's validity */
char *origin_id = NULL; name = ASGetName(data->config, id, m_sender);
rev = StrConcat(4, muc_id, "/", name, "[p]");
XMPPJoinMUC(jabber, xmppified_user, rev);
to = muc_id;
Free(name);
Free(rev);
}
if (reply_id) if (reply_id)
{ {
ParseeGetStanzaInfo(data, chat_id, reply_id, &stanza, &sender); /* TODO: Monocles chat DM users HATE this trick!
* Replies don't work there. Go figure why. */
if (!ParseeGetStanzaInfo(data, chat_id, reply_id, &stanza, &sender))
{
ParseeGetDMStanzaInfo(data, id, reply_id, &stanza, &sender);
}
} }
else if (unedited_id) else if (unedited_id)
{ {
ParseeGetOrigin(data, chat_id, unedited_id, &origin_id); if (!ParseeGetOrigin(data, chat_id, unedited_id, &origin_id))
{
ParseeGetDMOrigin(data, id, unedited_id, &origin_id);
} }
XMPPJoinMUC(jabber, jid, rev); }
XMPPSendPlain(
jabber, jid, muc_id, if (direct && sender)
xepd ? xepd : body, "groupchat", {
stanza, sender, ev_id, unauth, origin_id char *sndr_tmp = sender;
sender = ParseeTrimJID(sender);
Free(sndr_tmp);
}
{
char *xmpp_ident = StrRandom(32);
XMPPSendPlainID(
jabber, xmppified_user, to,
xepd ? xepd : body, type,
stanza, sender, ev_id, unauth, origin_id,
xmpp_ident
); );
Free(rev); /* Culprit */
Free(name); if (direct)
{
ParseePushDMStanza(
data, id, NULL,
xmpp_ident, ev_id,
xmppified_user
);
}
Free(xmpp_ident);
}
end:
Free(origin_id);
Free(xmppified_user);
Free(chat_id);
Free(to);
Free(reply_id);
Free(xepd);
Free(stanza); Free(stanza);
Free(sender); Free(sender);
Free(unauth); Free(unauth);
Free(origin_id);
Free(unedited_id); Free(unedited_id);
}
Free(chat_id); DbUnlock(data->db, ref);
Free(muc_id);
Free(jid);
Free(reply_id);
Free(xepd);
} }
void void

View file

@ -81,8 +81,9 @@ ParseeCleanup(void *datp)
ref = DbLock(data->db, 2, "chats", chat); ref = DbLock(data->db, 2, "chats", chat);
json = DbJson(ref); json = DbJson(ref);
#define CleanupField(field, timeout) do \ #define CleanupField(field, timeout, threshold) do \
{ \ { \
size_t fields = 0, cleaned = 0; \
field##s = JsonValueAsObject(HashMapGet(json, #field"s")); \ field##s = JsonValueAsObject(HashMapGet(json, #field"s")); \
to_delete = ArrayCreate(); \ to_delete = ArrayCreate(); \
while (HashMapIterate(field##s, &field, (void **) &val)) \ while (HashMapIterate(field##s, &field, (void **) &val)) \
@ -90,31 +91,90 @@ ParseeCleanup(void *datp)
HashMap *obj = JsonValueAsObject(val); \ HashMap *obj = JsonValueAsObject(val); \
uint64_t age = JsonValueAsInteger(HashMapGet(obj, "age")); \ uint64_t age = JsonValueAsInteger(HashMapGet(obj, "age")); \
uint64_t dur = ts - age; \ uint64_t dur = ts - age; \
fields++; \
\ \
if ((dur > (timeout))) \ if ((dur > (timeout))) \
{ \ { \
ArrayAdd(to_delete, StrDuplicate(field)); \ ArrayAdd(to_delete, StrDuplicate(field)); \
cleaned++; \
} \ } \
} \ } \
\ \
for (j = 0; j < ArraySize(to_delete); j++) \ for (j = 0; j < ArraySize(to_delete); j++) \
{ \ { \
field = ArrayGet(to_delete, j); \ field = ArrayGet(to_delete, j); \
if (cleaned > threshold) \
{ \
JsonValueFree(HashMapDelete(field##s, field)); \ JsonValueFree(HashMapDelete(field##s, field)); \
} \
Free(field); \ Free(field); \
} \ } \
ArrayFree(to_delete); \ ArrayFree(to_delete); \
} \ } \
while (0) while (0)
CleanupField(stanza, 3 HOURS); CleanupField(stanza, 30 MINUTES, 50);
CleanupField(event, 3 HOURS); CleanupField(event, 30 MINUTES, 50);
CleanupField(id, 3 HOURS); CleanupField(id, 30 MINUTES, 50);
DbUnlock(data->db, ref); DbUnlock(data->db, ref);
} }
DbListFree(chats); DbListFree(chats);
/* TODO */ /* TODO */
chats = DbList(data->db, 1, "rooms");
for (i = 0; i < ArraySize(chats); i++)
{
DbRef *ref;
HashMap *json, *stanzas, *events, *ids;
char *stanza, *event, *id;
JsonValue *val;
Array *to_delete;
size_t j;
chat = ArrayGet(chats, i);
ref = DbLock(data->db, 3, "rooms", chat, "data");
json = DbJson(ref);
#define CleanupField(field, timeout, threshold) do \
{ \
size_t fields = 0, cleaned = 0; \
field##s = JsonValueAsObject(HashMapGet(json, #field"s")); \
to_delete = ArrayCreate(); \
while (HashMapIterate(field##s, &field, (void **) &val)) \
{ \
HashMap *obj = JsonValueAsObject(val); \
uint64_t age = JsonValueAsInteger(HashMapGet(obj, "age")); \
uint64_t dur = ts - age; \
fields++; \
\
if ((dur > (timeout))) \
{ \
ArrayAdd(to_delete, StrDuplicate(field)); \
cleaned++; \
} \
} \
\
for (j = 0; j < ArraySize(to_delete); j++) \
{ \
field = ArrayGet(to_delete, j); \
if (cleaned > threshold) \
{ \
JsonValueFree(HashMapDelete(field##s, field)); \
} \
Free(field); \
} \
ArrayFree(to_delete); \
} \
while (0)
CleanupField(stanza, 3 HOURS, 50);
CleanupField(event, 3 HOURS, 50);
CleanupField(id, 3 HOURS, 50);
DbUnlock(data->db, ref);
}
DbListFree(chats);
} }
int int
ParseeFindDatastart(char *data) ParseeFindDatastart(char *data)
@ -353,6 +413,85 @@ ParseeXMPPify(HashMap *event)
return xepd; return xepd;
} }
void void
ParseePushDMStanza(ParseeData *data, char *room_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 || !room_id || !ev || !sender)
{
return;
}
ref = DbLock(data->db, 3, "rooms", room_id, "data");
if (!ref)
{
return;
}
j = DbJson(ref);
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
if (!stanzas)
{
stanzas = HashMapCreate();
new_stanzas = true;
}
if (stanza_id)
{
obj = HashMapCreate();
HashMapSet(obj, "age", JsonValueInteger(age));
HashMapSet(obj, "event", JsonValueString(ev));
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));
HashMapSet(obj, "age", JsonValueInteger(age));
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));
HashMapSet(obj, "sender", JsonValueString(sender));
HashMapSet(obj, "age", JsonValueInteger(age));
JsonValueFree(HashMapSet(ids, id, JsonValueObject(obj)));
if (new_ids)
{
HashMapSet(j, "ids", JsonValueObject(ids));
}
}
DbUnlock(data->db, ref);
}
void
ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, char *ev, char *sender) ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, char *ev, char *sender)
{ {
DbRef *ref; DbRef *ref;
@ -429,6 +568,36 @@ ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, cha
DbUnlock(data->db, ref); DbUnlock(data->db, ref);
} }
bool
ParseeVerifyDMStanza(ParseeData *data, char *room_id, char *id)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *stanzas = NULL;
bool ret = true;
if (!data || !room_id || !id)
{
return true;
}
ref = DbLock(data->db, 3, "rooms", room_id, "data");
j = DbJson(ref);
if (!ref)
{
goto end;
}
stanzas = JsonValueAsObject(HashMapGet(j, "ids"));
if (!stanzas)
{
goto end;
}
ret = !HashMapGet(stanzas, id);
end:
DbUnlock(data->db, ref);
return ret;
}
bool bool
ParseeVerifyStanza(ParseeData *data, char *chat_id, char *stanza_id) ParseeVerifyStanza(ParseeData *data, char *chat_id, char *stanza_id)
{ {
@ -490,6 +659,38 @@ end:
DbUnlock(data->db, ref); DbUnlock(data->db, ref);
return ret; return ret;
} }
char *
ParseeDMEventFromID(ParseeData *data, char *room_id, char *id)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *ids = NULL;
char *ret = NULL;
if (!data || !room_id || !id)
{
return NULL;
}
ref = DbLock(data->db, 3, "rooms", room_id, "data");
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;
}
char * char *
ParseeEventFromSID(ParseeData *data, char *chat_id, char *id) ParseeEventFromSID(ParseeData *data, char *chat_id, char *id)
{ {
@ -521,6 +722,38 @@ end:
DbUnlock(data->db, ref); DbUnlock(data->db, ref);
return ret; return ret;
} }
char *
ParseeDMEventFromSID(ParseeData *data, char *room_id, char *id)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *ids = NULL;
char *ret = NULL;
if (!data || !room_id || !id)
{
return NULL;
}
ref = DbLock(data->db, 3, "rooms", room_id, "data");
j = DbJson(ref);
if (!ref)
{
goto end;
}
ids = JsonValueAsObject(HashMapGet(j, "stanzas"));
if (!ids)
{
goto end;
}
ret = JsonValueAsString(JsonGet(ids, 2, id, "event"));
ret = StrDuplicate(ret);
end:
DbUnlock(data->db, ref);
return ret;
}
void void
ParseeGlobalBan(ParseeData *data, char *user) ParseeGlobalBan(ParseeData *data, char *user)
{ {

View file

@ -550,6 +550,38 @@ ParseeSendPresence(ParseeData *data)
DbUnlock(data->db, ref); DbUnlock(data->db, ref);
} }
bool bool
ParseeGetDMStanzaInfo(ParseeData *data, char *room_id, char *ev, char **st, char **se)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *event = NULL;
bool ret = false;
if (!data || !room_id || !ev || !st || !se)
{
return false;
}
ref = DbLock(data->db, 3, "rooms", room_id, "data");
j = DbJson(ref);
if (!ref)
{
goto end;
}
event = JsonValueAsObject(JsonGet(j, 2, "events", ev));
if (!event)
{
goto end;
}
*st = StrDuplicate(JsonValueAsString(HashMapGet(event, "origin")));
*se = StrDuplicate(JsonValueAsString(HashMapGet(event, "sender")));
ret = true;
end:
DbUnlock(data->db, ref);
return ret;
}
bool
ParseeGetStanzaInfo(ParseeData *data, char *chat_id, char *ev, char **st, char **se) ParseeGetStanzaInfo(ParseeData *data, char *chat_id, char *ev, char **st, char **se)
{ {
DbRef *ref = NULL; DbRef *ref = NULL;
@ -582,6 +614,38 @@ end:
return ret; return ret;
} }
bool bool
ParseeGetDMOrigin(ParseeData *data, char *room_id, char *ev, char **o)
{
DbRef *ref = NULL;
HashMap *j = NULL;
HashMap *event = NULL;
bool ret = false;
if (!data || !room_id || !ev || !o)
{
return false;
}
ref = DbLock(data->db, 3, "rooms", room_id, "data");
j = DbJson(ref);
if (!ref)
{
goto end;
}
event = JsonValueAsObject(JsonGet(j, 2, "events", ev));
if (!event)
{
goto end;
}
*o = StrDuplicate(JsonValueAsString(HashMapGet(event, "origin")));
ret = true;
end:
DbUnlock(data->db, ref);
return ret;
}
bool
ParseeGetOrigin(ParseeData *data, char *chat_id, char *ev, char **o) ParseeGetOrigin(ParseeData *data, char *chat_id, char *ev, char **o)
{ {
DbRef *ref = NULL; DbRef *ref = NULL;

View file

@ -8,6 +8,7 @@
static HttpServer *server = NULL; static HttpServer *server = NULL;
static pthread_t xmpp_thr; static pthread_t xmpp_thr;
static bool valid = true;
static XMPPComponent *jabber = NULL; static XMPPComponent *jabber = NULL;
static void static void
@ -34,6 +35,7 @@ SignalHandler(int signal)
Log(LOG_INFO, "Killing thread..."); Log(LOG_INFO, "Killing thread...");
XMPPKillThread(jabber, "killer"); XMPPKillThread(jabber, "killer");
pthread_join(xmpp_thr, NULL); pthread_join(xmpp_thr, NULL);
valid = false;
Log(LOG_INFO, "Stopping server..."); Log(LOG_INFO, "Stopping server...");
HttpServerStop(server); HttpServerStop(server);
break; break;
@ -50,6 +52,8 @@ ParseeInitialiseSignals(HttpServer *s, pthread_t xmpp, XMPPComponent *j)
xmpp_thr = xmpp; xmpp_thr = xmpp;
jabber = j; jabber = j;
valid = true;
sigAction.sa_handler = SignalHandler; sigAction.sa_handler = SignalHandler;
sigfillset(&sigAction.sa_mask); sigfillset(&sigAction.sa_mask);
sigAction.sa_flags = SA_RESTART; sigAction.sa_flags = SA_RESTART;

View file

@ -9,10 +9,18 @@
void void
XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *edit) XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *edit)
{
char *ident = StrRandom(32);
XMPPSendPlainID(comp, fr, to, msg, type, rst, rse, event_id, oob, edit, ident);
Free(ident);
}
void
XMPPSendPlainID(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *edit, char *ident)
{ {
XMLElement *message, *body, *data, *parsee; XMLElement *message, *body, *data, *parsee;
char *from; char *from;
if (!comp || !fr || !to || !msg) if (!comp || !fr || !to || !msg || !ident)
{ {
return; return;
} }
@ -23,6 +31,7 @@ XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, ch
XMLAddAttr(message, "from", (from = StrConcat(3, fr, "@", comp->host))); XMLAddAttr(message, "from", (from = StrConcat(3, fr, "@", comp->host)));
XMLAddAttr(message, "to", to); XMLAddAttr(message, "to", to);
XMLAddAttr(message, "type", type); XMLAddAttr(message, "type", type);
XMLAddAttr(message, "id", ident);
body = XMLCreateTag("body"); body = XMLCreateTag("body");
data = XMLCreateText(oob ? oob : msg); data = XMLCreateText(oob ? oob : msg);
@ -115,6 +124,8 @@ XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, ch
XMLAddChild(body, data); XMLAddChild(body, data);
XMLEncode(comp->stream, message); XMLEncode(comp->stream, message);
XMLEncode(StreamStdout(), message);
Log(LOG_INFO, "");
StreamFlush(comp->stream); StreamFlush(comp->stream);
XMLFreeElement(message); XMLFreeElement(message);
Free(from); Free(from);

View file

@ -15,15 +15,15 @@
#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("http://jabber.org/protocol/caps") \
AdvertiseSimple("urn:xmpp:avatar:metadata+notify") \
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("vcard-temp") \
AdvertiseSimple("urn:xmpp:avatar:metadata+notify") \
AdvertiseSimple("jabber:iq:version") \ 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")
@ -162,13 +162,112 @@ ParseeWakeupThread(void)
pthread_mutex_unlock(&cond_var_lock); pthread_mutex_unlock(&cond_var_lock);
} }
static void static char *
ParseeDMHandler(char *room, char *from, XMLElement *data, const ParseeConfig *c) ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza)
{ {
Free(ASSend( char *to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
c, room, from, char *from = (HashMapGet(stanza->attrs, "from"));
"m.room.message", MatrixCreateMessage(data->data) char *chat_id = ParseeGetFromMUCID(data, from);
)); char *mroom_id = ParseeGetRoomID(data, chat_id);
char *ret;
Free(chat_id);
if (mroom_id)
{
Free(to);
return mroom_id;
}
/* Not a MUC, find the DMd room. */
ret = ParseeFindDMRoom(data, to, from);
Free(to);
return ret;
}
static char *
ParseeGetEventFromID(ParseeData *data, XMLElement *stanza, char *id)
{
char *from = (HashMapGet(stanza->attrs, "from"));
char *chat_id = ParseeGetFromMUCID(data, from);
char *ret = ParseeEventFromSID(data, chat_id, id);
char *mroom_id = ParseeGetBridgedRoom(data, stanza);
if (!ret)
{
Free(ret);
ret = ParseeEventFromID(data, chat_id, id);
}
Free(chat_id);
if (ret)
{
Free(mroom_id);
return ret;
}
ret = ParseeDMEventFromID(data, mroom_id, id);
Free(mroom_id);
return ret;
}
static char *
ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza)
{
XMLElement *reactions = XMLookForTKV(stanza,
"reactions", "xmlns", "urn:xmpp:reactions:0"
);
char *from = (HashMapGet(stanza->attrs, "from"));
char *reacted_id = reactions ? HashMapGet(reactions->attrs, "id") : NULL;
return ParseeGetEventFromID(data, stanza, reacted_id);
}
static void
ParseePushAllStanza(ParseeData *args, XMLElement *stanza, char *event)
{
char *xmpp_from = HashMapGet(stanza->attrs, "from");
char *mroom_id = ParseeGetBridgedRoom(args, stanza);
char *chat_id = ParseeGetFromMUCID(args, xmpp_from);
char *id_str = HashMapGet(stanza->attrs, "id");
char *s_id_str = XMPPGetStanzaID(stanza);
char *o_id_str = XMPPGetOriginID(stanza);
if (!chat_id)
{
ParseePushDMStanza(
args, mroom_id, s_id_str,
id_str, event, xmpp_from
);
}
ParseePushStanza(args, chat_id, s_id_str, id_str, event, xmpp_from);
Free(mroom_id);
Free(chat_id);
}
static bool
ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
{
char *to, *room;
char *from = HashMapGet(stanza->attrs, "from");
char *id = HashMapGet(stanza->attrs, "id");
char *chat_id = ParseeGetFromMUCID(args, from);
bool ret;
if (chat_id)
{
char *s_id_str = XMPPGetStanzaID(stanza);
ret = ParseeVerifyStanza(args, chat_id, s_id_str);
Free(chat_id);
return ret;
}
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
room = ParseeFindDMRoom(args, to, from);
ret = ParseeVerifyDMStanza(args, chat_id, id);
Free(room);
Free(to);
return ret;
} }
/* TODO: Clean up all of this. We are currently separating DMs from MUCs, /* TODO: Clean up all of this. We are currently separating DMs from MUCs,
@ -225,7 +324,6 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
return false; return false;
} }
/* 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.
@ -237,12 +335,7 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
room = ParseeFindDMRoom(args, to, from); room = ParseeFindDMRoom(args, to, from);
data = ArrayGet(body->children, 0); data = ArrayGet(body->children, 0);
chat_id = ParseeGetFromMUCID(args, from); mroom_id = ParseeGetBridgedRoom(args, stanza);
mroom_id = ParseeGetRoomID(args, chat_id);
if (room)
{
ParseeDMHandler(room, from_matrix, data, args->config);
}
if (mroom_id && !XMPPIsParseeStanza(stanza)) if (mroom_id && !XMPPIsParseeStanza(stanza))
{ {
char *res = ParseeGetResource(from); char *res = ParseeGetResource(from);
@ -253,13 +346,24 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
char *event_id = NULL; char *event_id = NULL;
char *replaced = XMPPGetReplacedID(stanza); char *replaced = XMPPGetReplacedID(stanza);
char *reply_to = XMPPGetReply(stanza); char *reply_to = XMPPGetReply(stanza);
bool chat = false;
if (ParseeVerifyStanza(args, chat_id, s_id_str) && !replaced) if (StrEquals(HashMapGet(stanza->attrs, "type"), "chat"))
{
Free(encoded);
encoded = StrDuplicate(from_matrix);
chat = true;
}
if (ParseeVerifyAllStanza(args, stanza) && !replaced)
{ {
XMLElement *oob, *oob_data; XMLElement *oob, *oob_data;
ASRegisterUser(args->config, encoded); ASRegisterUser(args->config, encoded);
if (!chat)
{
ASSetName(args->config, encoded, res); ASSetName(args->config, encoded, res);
}
ASInvite(args->config, mroom_id, encoded); ASInvite(args->config, mroom_id, encoded);
ASJoin(args->config, mroom_id, encoded); ASJoin(args->config, mroom_id, encoded);
@ -290,7 +394,7 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
char *reacted_id = HashMapGet(reactions->attrs, "id"); char *reacted_id = HashMapGet(reactions->attrs, "id");
Array *react_child = reactions->children; Array *react_child = reactions->children;
size_t reacts = ArraySize(react_child); size_t reacts = ArraySize(react_child);
event_id = ParseeEventFromSID(args, chat_id, reacted_id); event_id = ParseeGetReactedEvent(args, stanza);
for (i = 0; i < reacts; i++) for (i = 0; i < reacts; i++)
{ {
XMLElement *reaction, *react_data; XMLElement *reaction, *react_data;
@ -311,15 +415,13 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
{ {
/* TODO: Use HTML-formatted bodies, and respect the fallback /* TODO: Use HTML-formatted bodies, and respect the fallback
* trims the stanza provides us if possible. Element does * trims the stanza provides us if possible. Element does
* not like raw bodies on replies. Go figure. */ * not like raw bodies on replies too. Go figure. */
size_t off = size_t off =
reply_to ? ParseeFindDatastart(data->data) : 0; reply_to ? ParseeFindDatastart(data->data) : 0;
HashMap *ev = MatrixCreateMessage(data->data + off); HashMap *ev = MatrixCreateMessage(data->data + off);
if (reply_to) if (reply_to)
{ {
char *reply_id = ParseeEventFromSID( char *reply_id = ParseeGetEventFromID(args, stanza, reply_to);
args, chat_id, reply_to
);
MatrixSetReply(ev, reply_id); MatrixSetReply(ev, reply_id);
Free(reply_id); Free(reply_id);
} }
@ -328,16 +430,17 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
"m.room.message", ev "m.room.message", ev
); );
} }
ParseePushStanza(args, chat_id, s_id_str, id_str, event_id, from); ParseePushAllStanza(args, stanza, event_id);
Free(event_id); Free(event_id);
} }
else if (replaced) else if (replaced)
{ {
event_id = ParseeEventFromID(args, chat_id, replaced); event_id = ParseeGetEventFromID(args, stanza, replaced);
Free(ASSend( Free(ASSend(
args->config, mroom_id, encoded, args->config, mroom_id, encoded,
"m.room.message", MatrixCreateReplace(event_id, data->data) "m.room.message", MatrixCreateReplace(event_id, data->data)
)); ));
ParseePushAllStanza(args, stanza, event_id);
Free(event_id); Free(event_id);
} }
@ -345,19 +448,16 @@ MessageStanza(ParseeData *args, XMLElement *stanza)
Free(res); Free(res);
Free(encoded); Free(encoded);
} }
else if (mroom_id) else
{ {
XMLElement *parsee = XMLookForUnique(stanza, "x-parsee"); XMLElement *parsee = XMLookForUnique(stanza, "x-parsee");
XMLElement *event = XMLookForUnique(parsee, "event-id"); XMLElement *event = XMLookForUnique(parsee, "event-id");
XMLElement *e_d = ArrayGet(event ? event->children : NULL, 0); XMLElement *e_d = ArrayGet(event ? event->children : NULL, 0);
char *s_id_str = XMPPGetStanzaID(stanza); char *s_id_str = XMPPGetStanzaID(stanza);
char *id_str = HashMapGet(stanza->attrs, "id"); char *id_str = HashMapGet(stanza->attrs, "id");
if (ParseeVerifyStanza(args, chat_id, s_id_str))
{ ParseePushAllStanza(args, stanza, e_d->data);
ParseePushStanza(args, chat_id, s_id_str, id_str, e_d->data, from);
} }
}
Free(chat_id);
Free(mroom_id); Free(mroom_id);
Free(from_matrix); Free(from_matrix);
Free(decode_from); Free(decode_from);
@ -579,6 +679,7 @@ ParseeXMPPThread(void *argp)
const char *killer = "killer"; const char *killer = "killer";
const char *suspend="suspend"; const char *suspend="suspend";
from = HashMapGet(stanza->attrs, "from"); from = HashMapGet(stanza->attrs, "from");
Log(LOG_INFO, "Killer.");
if (!strncmp(from, killer, strlen(killer))) if (!strncmp(from, killer, strlen(killer)))
{ {
XMLFreeElement(stanza); XMLFreeElement(stanza);

View file

@ -159,13 +159,16 @@ extern char * ParseeGetMUCID(ParseeData *, char *chat_id);
/* Pushes a stanza ID to a chat ID */ /* Pushes a stanza ID to a chat ID */
extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *origin_id, char *event, char *sender); extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *origin_id, char *event, char *sender);
extern void ParseePushDMStanza(ParseeData *, char *room_id, char *stanza_id, char *origin_id, char *event, char *sender);
/* Checks if a stanza is not duplicated in a chat ID */ /* Checks if a stanza is not duplicated in a chat ID */
extern bool ParseeVerifyStanza(ParseeData *, char *chat_id, char *stanza_id); extern bool ParseeVerifyStanza(ParseeData *, char *chat_id, char *stanza_id);
/* Gets the stanza ID and sender of an event */ /* Gets the stanza ID and sender of an event */
extern bool ParseeGetStanzaInfo(ParseeData *, char *c_id, char *e, char **st, char **se); extern bool ParseeGetStanzaInfo(ParseeData *, char *c_id, char *e, char **st, char **se);
extern bool ParseeGetDMStanzaInfo(ParseeData *, char *r_id, char *e, char **st, char **se);
extern bool ParseeGetOrigin(ParseeData *data, char *chat_id, char *ev, char **o); extern bool ParseeGetOrigin(ParseeData *data, char *chat_id, char *ev, char **o);
extern bool ParseeGetDMOrigin(ParseeData *data, char *chat_id, char *ev, char **o);
/* Sends presence requests for every MUC around as a fake JID */ /* Sends presence requests for every MUC around as a fake JID */
extern void ParseeSendPresence(ParseeData *); extern void ParseeSendPresence(ParseeData *);
@ -186,6 +189,9 @@ extern char * ParseeXMPPify(HashMap *event);
extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id); extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id);
extern char * ParseeEventFromSID(ParseeData *d, char *c_id, char *ori_id); extern char * ParseeEventFromSID(ParseeData *d, char *c_id, char *ori_id);
extern char * ParseeDMEventFromID(ParseeData *d, char *r_id, char *ori_id);
extern char * ParseeDMEventFromSID(ParseeData *d, char *r_id, char *ori_id);
/* Gets a Parsee "shim" link to an MXC, usable as unauth for a limited time */ /* Gets a Parsee "shim" link to an MXC, usable as unauth for a limited time */
extern char * ParseeToUnauth(ParseeData *data, char *mxc); extern char * ParseeToUnauth(ParseeData *data, char *mxc);
@ -201,4 +207,7 @@ extern void ParseeGlobalBan(ParseeData *, char *user);
/* Verifies if a user was globally banned. If so, then apply actions to the /* Verifies if a user was globally banned. If so, then apply actions to the
* room ID */ * room ID */
extern bool ParseeManageBan(ParseeData *, char *user, char *room); extern bool ParseeManageBan(ParseeData *, char *user, char *room);
/* Same as ParseeVerifyStanza, but DMs */
extern bool ParseeVerifyDMStanza(ParseeData *data, char *room_id, char *id);
#endif #endif

View file

@ -30,6 +30,11 @@ typedef struct ParseeCmdArg {
"set-pl", CmdSetPL, \ "set-pl", CmdSetPL, \
"Sets the power level in a Parsee room" \ "Sets the power level in a Parsee room" \
) \ ) \
X_COMMAND( \
"set-min-pl", CmdSetMin, \
"Sets the power level to send a specific event " \
"in a Parsee room" \
) \
X_COMMAND( \ X_COMMAND( \
"help", CmdHelp, \ "help", CmdHelp, \
"Shows the command list" \ "Shows the command list" \

View file

@ -29,6 +29,7 @@ extern void XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc);
/* TODO: XMPP stuff, I don't fucking know, I'm not a Jabbernerd. */ /* TODO: XMPP stuff, I don't fucking know, I'm not a Jabbernerd. */
extern void XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *id); extern void XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *id);
extern void XMPPSendPlainID(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *id, char *sid);
/* Closes a raw component stream. */ /* Closes a raw component stream. */
extern void XMPPEndCompStream(XMPPComponent *stream); extern void XMPPEndCompStream(XMPPComponent *stream);