[ADD/WIP] Start having MUCwork half-decent

Still lots of things required(like using the users JIDs whenever
possible, otherwise dropping to occupant ID), images, replies, rich data
with HTML and whatever XMPP has, etc...
This commit is contained in:
LDA 2024-06-21 18:31:43 +02:00
commit a84ce05b9d
12 changed files with 329 additions and 40 deletions

View file

@ -251,3 +251,32 @@ ASCreateRoom(const ParseeConfig *conf, char *by, char *alias)
return id;
}
void
ASSetName(const ParseeConfig *conf, char *user, char *name)
{
HttpClientContext *ctx = NULL;
HashMap *json;
char *path;
if (!conf || !user || !name)
{
return;
}
user = HttpUrlEncode(user);
path = StrConcat(6,
"/_matrix/client/v3/profile/",
user, "/displayname", "?",
"user_id=", user
);
json = HashMapCreate();
HashMapSet(json, "displayname", JsonValueString(name));
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
Free(path);
ASAuthenticateRequest(conf, ctx);
ParseeSetRequestJSON(ctx, json);
HttpClientContextFree(ctx);
JsonFree(json);
Free(user);
}

View file

@ -45,3 +45,19 @@ MatrixCreateNameState(char *name)
return map;
}
HashMap *
MatrixCreateNickChange(char *nick)
{
HashMap *map;
if (!nick)
{
return NULL;
}
map = HashMapCreate();
HashMapSet(map, "displayname", JsonValueString(nick));
HashMapSet(map, "membership", JsonValueString("join"));
return map;
}

View file

@ -57,6 +57,7 @@ Main(void)
"%s - v%s (Cytoplasm %s)",
NAME, VERSION, CytoplasmGetVersionStr()
);
Log(LOG_INFO, "=======================");
LogConfigIndent(LogConfigGlobal());
ParseeConfigLoad("parsee.json");
@ -102,6 +103,11 @@ Main(void)
goto end;
}
Log(LOG_NOTICE, "Listening to MUCs...");
LogConfigIndent(LogConfigGlobal());
ParseeSendPresence(conf.handlerArgs);
LogConfigUnindent(LogConfigGlobal());
sigAction.sa_handler = SignalHandler;
sigfillset(&sigAction.sa_mask);
sigAction.sa_flags = SA_RESTART;
@ -123,14 +129,13 @@ Main(void)
SIGACTION(SIGUSR1, &sigAction, NULL);
#undef SIGACTION
Log(LOG_INFO, "%d", XMPPQueryMUC(jabber, "rootard@muc.ari.lt", NULL));
server = HttpServerCreate(&conf);
HttpServerStart(server);
LogConfigUnindent(LogConfigGlobal());
Log(LOG_INFO, "=======================");
HttpServerJoin(server);
end:
LogConfigUnindent(LogConfigGlobal());
Log(LOG_INFO, "Exiting...");
HttpServerFree(server);
ParseeConfigFree();

View file

@ -46,12 +46,10 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
{
char *jid = ParseeEncodeMXID(state_key);
chat_id = ParseeGetFromRoomID(data, room_id);
Log(LOG_INFO, "JID=%s", jid);
if (chat_id)
{
char *muc = ParseeGetMUCID(data, chat_id);
char *rev = StrConcat(2, muc, "/parsee");
Log(LOG_INFO, "chat_id=%s muc=%s", chat_id, muc);
/* Make the user join the MUC */
XMPPJoinMUC(data->jabber, jid, rev);
Free(rev);
@ -72,13 +70,12 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
char *body = GrabString(event, 2, "content", "body");
char *id = GrabString(event, 1, "room_id");
char *sender = GrabString(event, 1, "sender");
char *chat_id;
char *chat_id, *muc_id, *jid;
bool direct = false;
if (ParseeIsPuppet(data->config, sender))
{
Log(LOG_INFO, "Do not bridge the puppet");
return;
}
@ -92,7 +89,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
char *user = GrabString(json, 1, "xmpp_user");
char *local = ParseeEncodeMXID(sender);
Log(LOG_INFO, "Sending to %s on XMPP", user);
XMPPSendPlain(jabber, local, user, body, NULL);
Free(local);
@ -101,12 +97,21 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
/* Try to find the chat ID */
chat_id = ParseeGetFromRoomID(data, id);
muc_id = ParseeGetMUCID(data, chat_id);
if (!chat_id)
{
return;
}
Log(LOG_INFO, "Chat ID=%s", chat_id);
jid = ParseeEncodeMXID(sender);
{
char *rev = StrConcat(3, muc_id, "/", jid);
XMPPJoinMUC(jabber, jid, rev);
XMPPSendPlain(jabber, jid, muc_id, body, "groupchat");
Free(rev);
}
Free(chat_id);
Free(muc_id);
Free(jid);
}
void
@ -119,7 +124,6 @@ ParseeEventHandler(ParseeData *data, HashMap *event)
}
event_type = GrabString(event, 1, "type");
Log(LOG_INFO, "E->%s", GrabString(event, 1, "sender"));
if (StrEquals(event_type, "m.room.member"))
{
ParseeMemberHandler(data, event);

View file

@ -4,6 +4,7 @@
#include <Cytoplasm/Json.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Sha.h>
#include <Cytoplasm/Log.h>
#include <string.h>
#include <stdlib.h>
@ -37,7 +38,22 @@ ParseeIsPuppet(const ParseeConfig *conf, char *user)
* room. */
return flag;
}
bool
ParseeIsJabberPuppet(const ParseeConfig *config, char *jid)
{
char *resource;
bool ret;
if (!config || !jid)
{
return false;
}
resource = ParseeGetResource(jid);
ret = *resource == '%';
Free(resource);
return ret;
}
char *
ParseeDecodeJID(char *str, char term)
{
@ -139,7 +155,7 @@ ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias)
}
char *
ParseeEncodeJID(const ParseeConfig *c, char *jid)
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
{
char *ret, *tmp;
size_t i;
@ -155,7 +171,7 @@ ParseeEncodeJID(const ParseeConfig *c, char *jid)
char cpy = jid[i];
char cs[4] = { 0 };
cs[0] = cpy;
if (*cs == '/')
if (*cs == '/' && trim)
{
/* RID: Break everything and die. */
break;
@ -360,6 +376,24 @@ ParseeTrimJID(char *jid)
return ret;
}
char *
ParseeGetResource(char *jid)
{
if (!jid)
{
return NULL;
}
while (*jid && *jid != '/')
{
jid++;
}
if (!*jid)
{
return NULL;
}
return StrDuplicate(jid + 1);
}
char *
ParseePushMUC(ParseeData *data, char *room_id, char *jid)
{
DbRef *ref;
@ -504,3 +538,97 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
DbUnlock(data->db, ref);
return ret;
}
void
ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id)
{
DbRef *ref;
HashMap *j;
HashMap *stanzas;
bool new_stanzas = false;
if (!data || !chat_id || !stanza_id)
{
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;
}
JsonValueFree(HashMapSet(stanzas, stanza_id, JsonValueNull()));
if (new_stanzas)
{
HashMapSet(j, "stanzas", JsonValueObject(stanzas));
}
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)
{
DbRef *ref;
HashMap *j, *mucs;
JsonValue *val;
char *chatid = NULL, *muc;
if (!data)
{
return;
}
ref = DbLock(data->db, 1, "chats");
j = DbJson(ref);
mucs = JsonValueAsObject(HashMapGet(j, "mucs"));
while (HashMapIterate(mucs, &muc, (void **) &val))
{
char *rev = StrConcat(2, muc, "/parsee");
/* Make a fake user join the MUC */
Log(LOG_NOTICE, "Sending presence to %s", rev);
XMPPJoinMUC(data->jabber, "parsee", rev);
Free(rev);
}
DbUnlock(data->db, ref);
}

View file

@ -598,6 +598,47 @@ XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs)
return event;
}
static char *
XMLDecodeString(char *s)
{
char *ret = NULL, *tmp;
char c, cs[2] = { 0 };
while (*s)
{
cs[0] = *s;
if (!strncmp(s, "&apos;", 6))
{
cs[0] = '\'';
s += 6;
}
else if (!strncmp(s, "&lt;", 4))
{
cs[0] = '<';
s += 4;
}
else if (!strncmp(s, "&gt;", 4))
{
cs[0] = '>';
s += 4;
}
else if (!strncmp(s, "&amp;", 5))
{
cs[0] = '&';
s += 5;
}
else
{
s++;
}
tmp = ret;
ret = StrConcat(2, ret, cs);
Free(tmp);
}
return ret;
}
XMLEvent *
XMLCreateData(XMLexer *lexer)
{
@ -609,13 +650,14 @@ XMLCreateData(XMLexer *lexer)
StrDuplicate(ArrayGet(lexer->data.elements, elements - 1)) :
NULL;
event->attrs = NULL;
event->data = lexer->data.str;
event->data = XMLDecodeString(lexer->data.str);
/* TODO */
event->line = 0;
event->col = 0;
event->offset = 0;
Free(lexer->data.str);
lexer->data.str = NULL;
return event;

View file

@ -33,10 +33,40 @@ XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type)
Free(from);
}
void
XMPPSendMUC(XMPPComponent *comp, char *fr, char *as, char *to, char *msg, char *type)
{
XMLElement *message, *body, *data;
char *from, *id;
if (!comp || !fr || !to || !as || !msg)
{
return;
}
message = XMLCreateTag("message");
XMLAddAttr(message, "from",
(from = StrConcat(5, fr, "@", comp->host, "/", as)));
XMLAddAttr(message, "to", to);
XMLAddAttr(message, "type", type);
XMLAddAttr(message, "id", (id = StrRandom(8)));
body = XMLCreateTag("body");
data = XMLCreateText(msg);
XMLAddChild(message, body);
XMLAddChild(body, data);
XMLEncode(comp->stream, message);
StreamFlush(comp->stream);
XMLFreeElement(message);
Free(from);
Free(id);
}
void
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
{
XMLElement *presence, *x;
char *from;
char *from, *id;
if (!comp || !fr || !muc)
{
return;
@ -46,19 +76,17 @@ XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
x = XMLCreateTag("x");
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
XMLAddAttr(presence, "to", muc);
XMLAddAttr(presence, "id", (id = StrRandom(8)));
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
XMLAddChild(presence, x);
/* A*/
XMLEncode(comp->stream, presence);
Log(LOG_INFO, "Flushing...");
/* B */
StreamFlush(comp->stream);
XMLFreeElement(presence);
Free(from);
/* How is that leaking */
Free(id);
}
void
XMPPKillThread(XMPPComponent *jabber, char *killer)

View file

@ -13,7 +13,7 @@
#include <AS.h>
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;
void
@ -27,7 +27,6 @@ ParseeWakeupThread(void)
static void
ParseeDMHandler(char *room, char *from, XMLElement *data, const ParseeConfig *c)
{
Log(LOG_INFO, "Trying to send %s %s", room, from);
ASSend(
c, room, from,
"m.room.message", MatrixCreateMessage(data->data)
@ -42,9 +41,9 @@ ParseeXMPPThread(void *argp)
XMLElement *stanza = NULL;
while (true)
{
/* TODO: pthread cancelation points */
XMLElement *body = NULL;
XMLElement *data = NULL;
XMLElement *stanza_id = NULL;
char *to, *room, *from, *from_matrix;
char *chat_id, *mroom_id;
@ -59,7 +58,7 @@ ParseeXMPPThread(void *argp)
XMLFreeElement(stanza);
continue;
}
if (StrEquals(stanza->name, "message"))
else if (StrEquals(stanza->name, "message"))
{
size_t i;
if (XMPPIsKiller(stanza))
@ -69,7 +68,6 @@ ParseeXMPPThread(void *argp)
from = HashMapGet(stanza->attrs, "from");
if (!strncmp(from, killer, strlen(killer)))
{
Log(LOG_INFO, "Dropping thread...");
XMLFreeElement(stanza);
break;
}
@ -82,40 +80,50 @@ ParseeXMPPThread(void *argp)
continue;
}
}
for (i = 0; i < ArraySize(stanza->children); i++)
{
XMLElement *child = ArrayGet(stanza->children, i);
if (StrEquals(child->name, "body"))
{
body = child;
break;
}
}
body = XMLookForUnique(stanza, "body");
if (!body)
{
XMLFreeElement(stanza);
continue;
}
stanza_id = XMLookForUnique(stanza, "stanza-id");
/* TODO: Check the type */
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
from = HashMapGet(stanza->attrs, "from");
from_matrix = ParseeEncodeJID(args->config, from);
from_matrix = ParseeEncodeJID(args->config, from, true);
room = ParseeFindDMRoom(args, to, from);
data = ArrayGet(body->children, 0);
/* TODO: Consider having rich messages */
/* TODO: Consider having rich messages, and move that out
* to separate functions */
chat_id = ParseeGetFromMUCID(args, from);
mroom_id = ParseeGetRoomID(args, chat_id);
if (room)
{
ParseeDMHandler(room, from_matrix, data, args->config);
}
if (mroom_id)
if (mroom_id && !ParseeIsJabberPuppet(args->config, from))
{
Log(LOG_INFO, "Send to %s", mroom_id);
/* TODO: Grab username, create a puppet, and go from
* there */
char *res = ParseeGetResource(from);
char *encoded = ParseeEncodeJID(args->config, from, false);
char *s_id_str = HashMapGet(stanza_id->attrs, "id");
/* TODO: Create smarter puppet names, and send presence
* in every room at Parsee's bootup. */
if (ParseeVerifyStanza(args, chat_id, s_id_str))
{
ASRegisterUser(args->config, encoded);
ASJoin(args->config, mroom_id, encoded);
ASSetName(args->config, encoded, res);
ASSend(
args->config, mroom_id, encoded,
"m.room.message", MatrixCreateMessage(data->data)
);
ParseePushStanza(args, chat_id, s_id_str);
}
Free(res);
Free(encoded);
}
Free(chat_id);
Free(mroom_id);
@ -123,6 +131,13 @@ ParseeXMPPThread(void *argp)
Free(room);
Free(to);
}
else
{
Log(LOG_WARNING, "Unknown stanza '%s':", stanza->name);
XMLEncode(StreamStdout(), stanza);
StreamPrintf(StreamStdout(), "\n");
StreamFlush(StreamStdout());
}
XMLFreeElement(stanza);
}

View file

@ -36,4 +36,7 @@ extern void ASSetState(const ParseeConfig *conf, char *id, char *type, char *key
/* Creates a room, with a masquerade user as its creator. This function
* returns it's ID if it exists. */
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);
#endif

View file

@ -14,4 +14,7 @@ extern HashMap * MatrixCreateMessage(char *body);
/* Creates the content for a m.room.name state event */
extern HashMap * MatrixCreateNameState(char *name);
/* Creates a join membership with a specific nickname */
extern HashMap * MatrixCreateNickChange(char *nick);
#endif

View file

@ -73,11 +73,14 @@ extern void ParseeEventHandler(ParseeData *, HashMap *);
/* Verifies if a user is a Parsee puppet user. */
extern bool ParseeIsPuppet(const ParseeConfig *, char *);
/* Verifies if a user is a Parsee puppet user on XMPP. */
extern bool ParseeIsJabberPuppet(const ParseeConfig *, char *);
/* Decodes a local JID for a user into a string. */
extern char * ParseeDecodeLocalJID(const ParseeConfig *, char *);
/* Encodes a JID into a Parsee localpart */
extern char * ParseeEncodeJID(const ParseeConfig *, char *);
extern char * ParseeEncodeJID(const ParseeConfig *, char *, bool);
/* Gets the localpart of a MXID */
extern char * ParseeGetLocal(char *);
@ -111,6 +114,9 @@ extern void ParseePushDMRoom(ParseeData *, char *mxid, char *jid, char *r);
/* Trims the component from a JID */
extern char * ParseeTrimJID(char *jid);
/* Gets the component from a JID */
extern char * ParseeGetResource(char *jid);
/* Decodes a room alias into a MUC JID */
extern char * ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias);
@ -132,4 +138,13 @@ extern char * ParseeGetRoomID(ParseeData *, char *chat_id);
/* Finds the MUC JID from a 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);
/* Checks if a stanza is not duplicated in a chat ID */
extern bool ParseeVerifyStanza(ParseeData *, char *chat_id, char *stanza_id);
/* Sends presence requests for every MUC around as a fake JID */
extern void ParseeSendPresence(ParseeData *);
#endif

View file

@ -24,6 +24,7 @@ extern void XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc);
/* TODO: XMPP stuff, I don't fucking know, I'm not a Jabbernerd. */
extern void XMPPSendPlain(XMPPComponent *c, char *f, char *t, char *m, char *type);
extern void XMPPSendMUC(XMPPComponent *comp, char *fr, char *as, char *to, char *msg, char *type);
/* Closes a raw component stream. */
extern void XMPPEndCompStream(XMPPComponent *stream);