mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 13:45:10 +00:00
[ADD/WIP] Continue MUCwerk
oh man this is gonna be painful to do... xmpp is fine iff youre doing DMs isnt it?
This commit is contained in:
parent
3ac11fd269
commit
d3b7f2fee0
19 changed files with 707 additions and 44 deletions
|
|
@ -24,6 +24,9 @@ $ make # This generates a 'parsee' executable.
|
|||
$
|
||||
```
|
||||
|
||||
### DEPENDENCIES
|
||||
Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendria.io/Telodendria/Cytoplasm).
|
||||
|
||||
## RUNNING
|
||||
TODO
|
||||
|
||||
|
|
|
|||
81
src/AS.c
81
src/AS.c
|
|
@ -144,6 +144,30 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
JsonFree(json);
|
||||
}
|
||||
void
|
||||
ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
char *path, *params;
|
||||
if (!conf || !id || !type || !mask || !state)
|
||||
{
|
||||
JsonFree(state);
|
||||
return;
|
||||
}
|
||||
|
||||
path = StrConcat(9,
|
||||
"/_matrix/client/v3/rooms/", id, "/state/",
|
||||
type, "/", key, "?", "user_id=", mask
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, state);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(state);
|
||||
}
|
||||
void
|
||||
ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
|
|
@ -151,6 +175,7 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
|
|||
char *txn;
|
||||
if (!conf || !id || !type || !user || !c)
|
||||
{
|
||||
JsonFree(c);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -170,3 +195,59 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
|
|||
HttpClientContextFree(ctx);
|
||||
JsonFree(c);
|
||||
}
|
||||
char *
|
||||
ASCreateRoom(const ParseeConfig *conf, char *by, char *alias)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *id;
|
||||
if (!conf || !by)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/createRoom",
|
||||
"?user_id=", by
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
if (alias)
|
||||
{
|
||||
char *trimmed = StrDuplicate(alias);
|
||||
if (*alias == '#')
|
||||
{
|
||||
char *tmp, cb[2] = { 0 };
|
||||
alias++;
|
||||
Free(trimmed);
|
||||
trimmed = NULL;
|
||||
|
||||
while (*alias && *alias != ':')
|
||||
{
|
||||
cb[0] = *alias;
|
||||
tmp = trimmed;
|
||||
trimmed = StrConcat(2, trimmed, cb);
|
||||
Free(tmp);
|
||||
alias ++;
|
||||
}
|
||||
}
|
||||
HashMapSet(json, "room_alias_name", JsonValueString(trimmed));
|
||||
Free(trimmed);
|
||||
}
|
||||
HashMapSet(json, "visibility", JsonValueString("public"));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
JsonFree(json);
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
|
|
|||
14
src/Events.c
14
src/Events.c
|
|
@ -31,3 +31,17 @@ MatrixCreateMessage(char *body)
|
|||
|
||||
return map;
|
||||
}
|
||||
HashMap *
|
||||
MatrixCreateNameState(char *name)
|
||||
{
|
||||
HashMap *map;
|
||||
if (!name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
map = HashMapCreate();
|
||||
HashMapSet(map, "name", JsonValueString(name));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
|
|
|||
19
src/Main.c
19
src/Main.c
|
|
@ -1,5 +1,7 @@
|
|||
#include <Cytoplasm/HttpServer.h>
|
||||
#include <Cytoplasm/Cytoplasm.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
|
|
@ -34,7 +36,9 @@ SignalHandler(int signal)
|
|||
return;
|
||||
}
|
||||
/* Create a loopback stanza, forcing the thread to die */
|
||||
XMPPKillThread(jabber);
|
||||
Log(LOG_INFO, "Killing thread...");
|
||||
XMPPKillThread(jabber, "killer");
|
||||
pthread_join(xmpp_thr, NULL);
|
||||
HttpServerStop(server);
|
||||
break;
|
||||
}
|
||||
|
|
@ -49,7 +53,12 @@ Main(void)
|
|||
Stream *yaml;
|
||||
struct sigaction sigAction;
|
||||
|
||||
Log(LOG_INFO, "%s - v%s", NAME, VERSION);
|
||||
Log(LOG_INFO,
|
||||
"%s - v%s (Cytoplasm %s)",
|
||||
NAME, VERSION, CytoplasmGetVersionStr()
|
||||
);
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
|
||||
ParseeConfigLoad("parsee.json");
|
||||
ParseeConfigInit();
|
||||
|
||||
|
|
@ -90,7 +99,7 @@ Main(void)
|
|||
if (pthread_create(&xmpp_thr, NULL, ParseeXMPPThread, conf.handlerArgs))
|
||||
{
|
||||
Log(LOG_ERR, "Couldn't start XMPP listener thread.");
|
||||
/* TODO: Die */
|
||||
goto end;
|
||||
}
|
||||
|
||||
sigAction.sa_handler = SignalHandler;
|
||||
|
|
@ -101,6 +110,7 @@ Main(void)
|
|||
if (sigaction(sig, act, oact) < 0) \
|
||||
{ \
|
||||
Log(LOG_ERR, "Unable to install signal handler: %s", #sig); \
|
||||
goto end; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
|
|
@ -113,12 +123,15 @@ Main(void)
|
|||
SIGACTION(SIGUSR1, &sigAction, NULL);
|
||||
|
||||
#undef SIGACTION
|
||||
Log(LOG_INFO, "%d", XMPPQueryMUC(jabber, "rootard@muc.ari.lt", NULL));
|
||||
|
||||
server = HttpServerCreate(&conf);
|
||||
HttpServerStart(server);
|
||||
HttpServerJoin(server);
|
||||
|
||||
end:
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
Log(LOG_INFO, "Exiting...");
|
||||
HttpServerFree(server);
|
||||
ParseeConfigFree();
|
||||
ParseeFreeData(conf.handlerArgs);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
|||
char *membership = GrabString(event, 2, "content", "membership");
|
||||
char *room_id = GrabString(event, 1, "room_id");
|
||||
char *sender = GrabString(event, 1, "sender");
|
||||
char *chat_id;
|
||||
const ParseeConfig *conf = data->config;
|
||||
Log(LOG_INFO, "Membership '%s'->'%s'", state_key, membership);
|
||||
|
||||
|
|
@ -41,6 +42,24 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
|||
Free(jid);
|
||||
}
|
||||
}
|
||||
else if (StrEquals(membership, "join") && !ParseeIsPuppet(conf, state_key))
|
||||
{
|
||||
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);
|
||||
Free(muc);
|
||||
}
|
||||
Free(jid);
|
||||
Free(chat_id);
|
||||
}
|
||||
}
|
||||
static void
|
||||
ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||
|
|
@ -53,6 +72,9 @@ 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;
|
||||
|
||||
bool direct = false;
|
||||
|
||||
if (ParseeIsPuppet(data->config, sender))
|
||||
{
|
||||
|
|
@ -62,9 +84,10 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
|
||||
ref = DbLock(data->db, 3, "rooms", id, "data");
|
||||
json = DbJson(ref);
|
||||
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
|
||||
if (JsonValueAsBoolean(HashMapGet(json, "is_direct")))
|
||||
if (direct)
|
||||
{
|
||||
char *user = GrabString(json, 1, "xmpp_user");
|
||||
char *local = ParseeEncodeMXID(sender);
|
||||
|
|
@ -73,9 +96,17 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
XMPPSendPlain(jabber, local, user, body, NULL);
|
||||
|
||||
Free(local);
|
||||
return;
|
||||
}
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
/* Try to find the chat ID */
|
||||
chat_id = ParseeGetFromRoomID(data, id);
|
||||
if (!chat_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Log(LOG_INFO, "Chat ID=%s", chat_id);
|
||||
Free(chat_id);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -88,6 +119,7 @@ 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);
|
||||
|
|
|
|||
185
src/ParseeUser.c
185
src/ParseeUser.c
|
|
@ -38,10 +38,14 @@ ParseeIsPuppet(const ParseeConfig *conf, char *user)
|
|||
return flag;
|
||||
}
|
||||
|
||||
static char *
|
||||
DecodeJID(char *str, char term)
|
||||
char *
|
||||
ParseeDecodeJID(char *str, char term)
|
||||
{
|
||||
char *out = NULL;
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#define Okay(c) ((c) && ((c) != term))
|
||||
while (Okay(*str))
|
||||
{
|
||||
|
|
@ -101,7 +105,37 @@ ParseeDecodeLocalJID(const ParseeConfig *c, char *mxid)
|
|||
}
|
||||
data_start++;
|
||||
/* Until the ':', data_start now is an encoded JID */
|
||||
return DecodeJID(data_start, ':');
|
||||
return ParseeDecodeJID(data_start, ':');
|
||||
}
|
||||
char *
|
||||
ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias)
|
||||
{
|
||||
char *localpart, *jid_flags, *data_start;
|
||||
bool plain_jid = false;
|
||||
if (!c || !alias || *alias != '#')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
localpart = alias + 1;
|
||||
jid_flags = localpart + strlen(c->namespace_base) + 1;
|
||||
data_start = jid_flags;
|
||||
while (*data_start && *data_start != '_')
|
||||
{
|
||||
/* TODO: Make this a macro */
|
||||
if (*data_start == 'm')
|
||||
{
|
||||
plain_jid = true;
|
||||
}
|
||||
data_start++;
|
||||
}
|
||||
if (!*data_start || !plain_jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
data_start++;
|
||||
/* Until the ':', data_start now is an encoded JID */
|
||||
return ParseeDecodeJID(data_start, ':');
|
||||
}
|
||||
|
||||
char *
|
||||
|
|
@ -325,3 +359,148 @@ ParseeTrimJID(char *jid)
|
|||
|
||||
return ret;
|
||||
}
|
||||
char *
|
||||
ParseePushMUC(ParseeData *data, char *room_id, char *jid)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *chatid = NULL;
|
||||
if (!data || !room_id || !jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chatid = ParseeGetDMID(room_id, jid);
|
||||
ref = DbCreate(data->db, 2, "chats", chatid);
|
||||
j = DbJson(ref);
|
||||
|
||||
HashMapSet(j, "room_id", JsonValueString(room_id));
|
||||
HashMapSet(j, "jabber_id", JsonValueString(jid));
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
/* Create a double-mapping */
|
||||
ref = DbLock(data->db, 1, "chats");
|
||||
if (!ref)
|
||||
{
|
||||
ref = DbCreate(data->db, 1, "chats");
|
||||
if (!ref)
|
||||
{
|
||||
Free(chatid);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
j = DbJson(ref);
|
||||
{
|
||||
HashMap *table;
|
||||
|
||||
table = JsonValueAsObject(HashMapGet(j, "mucs"));
|
||||
if (!table)
|
||||
{
|
||||
table = HashMapCreate();
|
||||
}
|
||||
HashMapSet(table, jid, JsonValueString(chatid));
|
||||
HashMapSet(j, "mucs", JsonValueObject(table));
|
||||
|
||||
|
||||
table = JsonValueAsObject(HashMapGet(j, "rooms"));
|
||||
if (!table)
|
||||
{
|
||||
table = HashMapCreate();
|
||||
}
|
||||
HashMapSet(table, room_id, JsonValueString(chatid));
|
||||
HashMapSet(j, "rooms", JsonValueObject(table));
|
||||
|
||||
DbJsonSet(ref, (j = JsonDuplicate(j)));
|
||||
JsonFree(j);
|
||||
}
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
return chatid;
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeGetFromMUCID(ParseeData *data, char *jid)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *cid;
|
||||
if (!data || !jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jid = ParseeTrimJID(jid);
|
||||
|
||||
ref = DbLock(data->db, 1, "chats");
|
||||
j = DbJson(ref);
|
||||
|
||||
cid = JsonValueAsString(JsonGet(j, 2, "mucs", jid));
|
||||
cid = StrDuplicate(cid);
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
Free(jid);
|
||||
|
||||
return cid;
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeGetFromRoomID(ParseeData *data, char *room_id)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *cid;
|
||||
if (!data || !room_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref = DbLock(data->db, 1, "chats");
|
||||
j = DbJson(ref);
|
||||
|
||||
cid = JsonValueAsString(JsonGet(j, 2, "rooms", room_id));
|
||||
cid = StrDuplicate(cid);
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
return cid;
|
||||
}
|
||||
char *
|
||||
ParseeGetRoomID(ParseeData *data, char *chat_id)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *ret;
|
||||
if (!data || !chat_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref = DbLock(data->db, 2, "chats", chat_id);
|
||||
j = DbJson(ref);
|
||||
|
||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(j, "room_id")));
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeGetMUCID(ParseeData *data, char *chat_id)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *ret;
|
||||
if (!data || !chat_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref = DbLock(data->db, 2, "chats", chat_id);
|
||||
j = DbJson(ref);
|
||||
|
||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(j, "jabber_id")));
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ 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
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
#include <Routes.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
#include <XMPP.h>
|
||||
#include <AS.h>
|
||||
|
||||
RouteHead(RouteUserAck, arr, argp)
|
||||
|
|
@ -43,8 +46,13 @@ RouteHead(RouteRoomAck, arr, argp)
|
|||
HashMap *response = NULL;
|
||||
Array *events;
|
||||
size_t i;
|
||||
MUCInfo info = { .exists = false };
|
||||
|
||||
char *room = ArrayGet(arr, 0), *muc = NULL, *id = NULL;
|
||||
char *creator = NULL, *muc_name = NULL, *chatid = NULL;
|
||||
|
||||
Log(LOG_INFO, "Ack");
|
||||
|
||||
char *room = ArrayGet(arr, 0);
|
||||
response = ASVerifyRequest(args);
|
||||
if (response)
|
||||
{
|
||||
|
|
@ -60,14 +68,70 @@ RouteHead(RouteRoomAck, arr, argp)
|
|||
goto end;
|
||||
}
|
||||
|
||||
Log(LOG_INFO, "room=%s", room);
|
||||
|
||||
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
||||
response = MatrixCreateError(
|
||||
"M_UNRECOGNIZED",
|
||||
"Path /users only accepts GET as a valid method."
|
||||
muc = ParseeDecodeLocalMUC(args->data->config, room);
|
||||
Log(LOG_INFO, "room=%s", muc);
|
||||
if (!XMPPQueryMUC(args->data->jabber, muc, &info))
|
||||
{
|
||||
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
||||
response = MatrixCreateError(
|
||||
"M_UNRECOGNIZED",
|
||||
"Room does not map to a real XMPP MUC"
|
||||
);
|
||||
Log(LOG_INFO, "No MUC");
|
||||
goto end;
|
||||
}
|
||||
creator = StrConcat(
|
||||
4,
|
||||
"@", args->data->config->sender_localpart, ":",
|
||||
args->data->config->homeserver_host
|
||||
);
|
||||
id = ASCreateRoom(
|
||||
args->data->config,
|
||||
creator, room
|
||||
);
|
||||
if (!id)
|
||||
{
|
||||
Log(LOG_INFO, "No ID");
|
||||
HttpResponseStatus(args->ctx, HTTP_INTERNAL_SERVER_ERROR);
|
||||
response = MatrixCreateError(
|
||||
"M_UNKNOWN",
|
||||
"Could not create the room."
|
||||
);
|
||||
goto end;
|
||||
}
|
||||
muc_name = XMPPGetMUCName(info);
|
||||
if (muc_name)
|
||||
{
|
||||
ASSetState(
|
||||
args->data->config, id, "m.room.name", "", creator,
|
||||
MatrixCreateNameState(muc_name)
|
||||
);
|
||||
Free(muc_name);
|
||||
}
|
||||
|
||||
/* Creates a mapping */
|
||||
chatid = ParseePushMUC(args->data, id, muc);
|
||||
Log(LOG_INFO, "Chat ID=%s", chatid);
|
||||
|
||||
response = HashMapCreate();
|
||||
end:
|
||||
if (chatid)
|
||||
{
|
||||
Free(chatid);
|
||||
}
|
||||
if (muc)
|
||||
{
|
||||
Free(muc);
|
||||
}
|
||||
if (id)
|
||||
{
|
||||
Free(id);
|
||||
}
|
||||
if (creator)
|
||||
{
|
||||
Free(creator);
|
||||
}
|
||||
XMPPFreeMUCInfo(info);
|
||||
JsonFree(request);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,3 +110,24 @@ XMLFreeElement(XMLElement *element)
|
|||
|
||||
Free(element);
|
||||
}
|
||||
XMLElement *
|
||||
XMLookForUnique(XMLElement *parent, char *tag)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!parent || !tag)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(parent->children); i++)
|
||||
{
|
||||
XMLElement *child = ArrayGet(parent->children, i);
|
||||
if (StrEquals(child->name, tag))
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Array.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
|
@ -122,6 +123,7 @@ XMLCrank(XMLexer *lexer)
|
|||
{
|
||||
XMLEvent *event = NULL;
|
||||
char c;
|
||||
int ch;
|
||||
char *attrname;
|
||||
HashMap *props;
|
||||
char *key, *val;
|
||||
|
|
@ -136,6 +138,7 @@ XMLCrank(XMLexer *lexer)
|
|||
return NULL;
|
||||
}
|
||||
event = XMLCreateRelax(lexer);
|
||||
|
||||
switch (lexer->state)
|
||||
{
|
||||
case XML_STATE_NONE:
|
||||
|
|
@ -235,6 +238,10 @@ XMLCrank(XMLexer *lexer)
|
|||
event = XMLCreateStart(lexer, props);
|
||||
break;
|
||||
}
|
||||
else if (XMLookahead(lexer, "'", true))
|
||||
{
|
||||
while (true);
|
||||
}
|
||||
break;
|
||||
case XML_STATE_ATTRTAIL:
|
||||
attrname = XMLParseName(lexer);
|
||||
|
|
@ -490,7 +497,7 @@ HashMap *
|
|||
XMLReadProps(XMLexer *lexer)
|
||||
{
|
||||
ssize_t point = XMLInitialiseBuffer(lexer);
|
||||
HashMap *map = HashMapCreate();
|
||||
HashMap *map = NULL;
|
||||
bool error = false;
|
||||
while (true)
|
||||
{
|
||||
|
|
@ -523,10 +530,14 @@ XMLReadProps(XMLexer *lexer)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!map)
|
||||
{
|
||||
map = HashMapCreate();
|
||||
}
|
||||
HashMapSet(map, name, value);
|
||||
Free(name);
|
||||
}
|
||||
if (error)
|
||||
if (error || !map)
|
||||
{
|
||||
XMLReset(lexer, point);
|
||||
}
|
||||
|
|
@ -660,7 +671,20 @@ XMLParseAttQuote(XMLexer *lexer)
|
|||
|
||||
while ((c = XMLGetc(lexer)))
|
||||
{
|
||||
if (!IsNormalQ(c))
|
||||
if (c == '&')
|
||||
{
|
||||
char *code = NULL;
|
||||
int c2;
|
||||
int p2 = XMLInitialiseBuffer(lexer);
|
||||
while ((c2 = XMLGetc(lexer)))
|
||||
{
|
||||
if (c2 == ';')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!IsNormalQ(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
@ -687,7 +711,21 @@ XMLParseAttDouble(XMLexer *lexer)
|
|||
|
||||
while ((c = XMLGetc(lexer)))
|
||||
{
|
||||
if (!IsNormalD(c))
|
||||
if (c == '&')
|
||||
{
|
||||
char *code = NULL;
|
||||
int c2;
|
||||
int p2 = XMLInitialiseBuffer(lexer);
|
||||
while ((c2 = XMLGetc(lexer)))
|
||||
{
|
||||
if (c2 == ';')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (!IsNormalD(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ XMPPInitialiseCompStream(char *host, int port)
|
|||
sd = -1;
|
||||
continue;
|
||||
}
|
||||
Log(LOG_INFO, "Connected to port %s", serv);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -68,7 +67,6 @@ XMPPInitialiseCompStream(char *host, int port)
|
|||
freeaddrinfo(res0);
|
||||
|
||||
stream = StreamFd(sd);
|
||||
Log(LOG_INFO, "Got %d via %p", sd, stream);
|
||||
if (!stream)
|
||||
{
|
||||
close(sd);
|
||||
|
|
@ -143,7 +141,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
|||
|
||||
stream_id = StrDuplicate(HashMapGet(ev->attrs, "id"));
|
||||
handshake = ComputeHandshake(shared, stream_id);
|
||||
/* y no stream id */
|
||||
|
||||
Log(LOG_NOTICE, "- sID='%s'", stream_id);
|
||||
StreamPrintf(stream, "<handshake>%s</handshake>", handshake);
|
||||
StreamFlush(stream);
|
||||
|
|
|
|||
109
src/XMPP/MUC.c
Normal file
109
src/XMPP/MUC.c
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#include <XMPP.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
bool
|
||||
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||
{
|
||||
XMLElement *iq_query, *query;
|
||||
char *uuid, *from;
|
||||
if (!jabber || !muc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
iq_query = XMLCreateTag("iq");
|
||||
query = XMLCreateTag("query");
|
||||
|
||||
XMLAddAttr(iq_query, "from",(from = StrConcat(2,"parsee@",jabber->host)));
|
||||
XMLAddAttr(iq_query, "to", muc);
|
||||
XMLAddAttr(iq_query, "id", (uuid = StrRandom(8)));
|
||||
XMLAddAttr(iq_query, "type", "get");
|
||||
|
||||
XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info");
|
||||
|
||||
XMLAddChild(iq_query, query);
|
||||
Free(from);
|
||||
Free(uuid);
|
||||
|
||||
/* Pause the XMPP thread */
|
||||
XMPPKillThread(jabber, "suspend");
|
||||
UtilSleepMillis(500);
|
||||
{
|
||||
XMLElement *identity;
|
||||
/* -- WE ARE ON OUR OWN HERE WITH STANZAS -- */
|
||||
XMLEncode(jabber->stream, iq_query);
|
||||
StreamFlush(jabber->stream);
|
||||
XMLFreeElement(iq_query);
|
||||
|
||||
/* Except an IQ reply */
|
||||
iq_query = XMLDecode(jabber->stream, false);
|
||||
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
||||
{
|
||||
XMLFreeElement(iq_query);
|
||||
ParseeWakeupThread();
|
||||
return false;
|
||||
}
|
||||
query = XMLookForUnique(iq_query, "query");
|
||||
identity = XMLookForUnique(query, "identity");
|
||||
|
||||
if (!identity ||
|
||||
!StrEquals(HashMapGet(identity->attrs, "category"),
|
||||
"conference"))
|
||||
{
|
||||
XMLFreeElement(iq_query);
|
||||
ParseeWakeupThread();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We found a MUC! */
|
||||
if (out)
|
||||
{
|
||||
out->exists = true;
|
||||
out->jabber = jabber;
|
||||
out->iq_reply = iq_query;
|
||||
}
|
||||
else
|
||||
{
|
||||
XMLFreeElement(iq_query);
|
||||
}
|
||||
}
|
||||
/* Wake it up once we're done */
|
||||
ParseeWakeupThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
XMPPFreeMUCInfo(MUCInfo info)
|
||||
{
|
||||
if (!info.exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
XMLFreeElement(info.iq_reply);
|
||||
|
||||
info.exists = false;
|
||||
}
|
||||
|
||||
char *
|
||||
XMPPGetMUCName(MUCInfo info)
|
||||
{
|
||||
XMLElement *query, *identity;
|
||||
char *name;
|
||||
if (!info.exists)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
query = XMLookForUnique(info.iq_reply, "query");
|
||||
identity = XMLookForUnique(query, "identity");
|
||||
|
||||
name = StrDuplicate(HashMapGet(identity->attrs, "name"));
|
||||
|
||||
return name;
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <XML.h>
|
||||
|
||||
|
|
@ -32,11 +33,11 @@ XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type)
|
|||
Free(from);
|
||||
}
|
||||
void
|
||||
XMPPSendPresence(XMPPComponent *comp, char *fr, char *to)
|
||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
|
||||
{
|
||||
XMLElement *presence, *x;
|
||||
char *from;
|
||||
if (!comp || !fr || !to)
|
||||
if (!comp || !fr || !muc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -44,28 +45,33 @@ XMPPSendPresence(XMPPComponent *comp, char *fr, char *to)
|
|||
presence = XMLCreateTag("presence");
|
||||
x = XMLCreateTag("x");
|
||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||
XMLAddAttr(presence, "to", to);
|
||||
XMLAddAttr(presence, "to", muc);
|
||||
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 */
|
||||
}
|
||||
void
|
||||
XMPPKillThread(XMPPComponent *jabber)
|
||||
XMPPKillThread(XMPPComponent *jabber, char *killer)
|
||||
{
|
||||
XMLElement *message, *body, *data;
|
||||
char *from;
|
||||
|
||||
if (!jabber)
|
||||
if (!jabber || !killer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
from = StrConcat(2, "jabber_die@", jabber->host);
|
||||
from = StrConcat(3, killer, "@", jabber->host);
|
||||
message = XMLCreateTag("message");
|
||||
XMLAddAttr(message, "from", from);
|
||||
XMLAddAttr(message, "to", from);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
|
@ -9,6 +12,28 @@
|
|||
#include <XML.h>
|
||||
#include <AS.h>
|
||||
|
||||
|
||||
static pthread_mutex_t cond_var_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
void
|
||||
ParseeWakeupThread(void)
|
||||
{
|
||||
pthread_mutex_lock(&cond_var_lock);
|
||||
pthread_cond_signal(&cond_var);
|
||||
pthread_mutex_unlock(&cond_var_lock);
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
void *
|
||||
ParseeXMPPThread(void *argp)
|
||||
{
|
||||
|
|
@ -21,26 +46,41 @@ ParseeXMPPThread(void *argp)
|
|||
XMLElement *body = NULL;
|
||||
XMLElement *data = NULL;
|
||||
char *to, *room, *from, *from_matrix;
|
||||
char *chat_id, *mroom_id;
|
||||
|
||||
/* Decoding XML is blocking, which will cause memory issues when we
|
||||
* want to murder this thread.
|
||||
* We could ping the server, and die on a "response" stanza, however.
|
||||
*/
|
||||
stanza = XMLDecode(jabber->stream, false);
|
||||
if (!stanza)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StrEquals(stanza->name, "presence"))
|
||||
{
|
||||
XMLFreeElement(stanza);
|
||||
continue;
|
||||
}
|
||||
if (StrEquals(stanza->name, "message"))
|
||||
{
|
||||
size_t i;
|
||||
if (XMPPIsKiller(stanza))
|
||||
{
|
||||
/* There's not a lot of scenarios where we loopback */
|
||||
Log(LOG_INFO, "Dropping thread...");
|
||||
XMLFreeElement(stanza);
|
||||
break;
|
||||
const char *killer = "killer";
|
||||
const char *suspend="suspend";
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
if (!strncmp(from, killer, strlen(killer)))
|
||||
{
|
||||
Log(LOG_INFO, "Dropping thread...");
|
||||
XMLFreeElement(stanza);
|
||||
break;
|
||||
}
|
||||
else if (!strncmp(from, suspend, strlen(suspend)))
|
||||
{
|
||||
XMLFreeElement(stanza);
|
||||
pthread_mutex_lock(&cond_var_lock);
|
||||
pthread_cond_wait(&cond_var, &cond_var_lock);
|
||||
pthread_mutex_unlock(&cond_var_lock);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ArraySize(stanza->children); i++)
|
||||
{
|
||||
|
|
@ -56,6 +96,8 @@ ParseeXMPPThread(void *argp)
|
|||
XMLFreeElement(stanza);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO: Check the type */
|
||||
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
from_matrix = ParseeEncodeJID(args->config, from);
|
||||
|
|
@ -63,10 +105,20 @@ ParseeXMPPThread(void *argp)
|
|||
data = ArrayGet(body->children, 0);
|
||||
|
||||
/* TODO: Consider having rich messages */
|
||||
ASSend(
|
||||
args->config, room, from_matrix,
|
||||
"m.room.message", MatrixCreateMessage(data->data)
|
||||
);
|
||||
chat_id = ParseeGetFromMUCID(args, from);
|
||||
mroom_id = ParseeGetRoomID(args, chat_id);
|
||||
if (room)
|
||||
{
|
||||
ParseeDMHandler(room, from_matrix, data, args->config);
|
||||
}
|
||||
if (mroom_id)
|
||||
{
|
||||
Log(LOG_INFO, "Send to %s", mroom_id);
|
||||
/* TODO: Grab username, create a puppet, and go from
|
||||
* there */
|
||||
}
|
||||
Free(chat_id);
|
||||
Free(mroom_id);
|
||||
Free(from_matrix);
|
||||
Free(room);
|
||||
Free(to);
|
||||
|
|
|
|||
|
|
@ -30,4 +30,10 @@ extern void ASJoin(const ParseeConfig *, char *, char *);
|
|||
* Said body is freed during the function's execution. */
|
||||
extern void ASSend(const ParseeConfig *, char *, char *, char *, HashMap *);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -11,4 +11,7 @@ extern HashMap * MatrixCreateNotice(char *body);
|
|||
|
||||
/* Creates the content for a normal message. */
|
||||
extern HashMap * MatrixCreateMessage(char *body);
|
||||
|
||||
/* Creates the content for a m.room.name state event */
|
||||
extern HashMap * MatrixCreateNameState(char *name);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ extern void ParseeRequest(HttpServerContext *, void *);
|
|||
/* A pthread callback used for listening to a component */
|
||||
extern void * ParseeXMPPThread(void *data);
|
||||
|
||||
/* Wakes up the XMPP thread from a "suspend" killer stanza */
|
||||
extern void ParseeWakeupThread(void);
|
||||
|
||||
/* Finds the room a DM is associated to, from a Matrix user and a Jabber
|
||||
* ID. */
|
||||
|
|
@ -108,4 +110,26 @@ extern void ParseePushDMRoom(ParseeData *, char *mxid, char *jid, char *r);
|
|||
|
||||
/* Trims the component from a JID */
|
||||
extern char * ParseeTrimJID(char *jid);
|
||||
|
||||
/* Decodes a room alias into a MUC JID */
|
||||
extern char * ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias);
|
||||
|
||||
/* Decodes an encoded string(from an MXID/Room ID) into a JID */
|
||||
extern char * ParseeDecodeJID(char *str, char term);
|
||||
|
||||
/* Creates a Room/MUC mapping, and returns it's chat ID. */
|
||||
extern char * ParseePushMUC(ParseeData *, char *room_id, char *jid);
|
||||
|
||||
/* Finds a chat ID from a MUC JID */
|
||||
extern char * ParseeGetFromMUCID(ParseeData *, char *jid);
|
||||
|
||||
/* Finds a chat ID from a room ID */
|
||||
extern char * ParseeGetFromRoomID(ParseeData *, char *room_id);
|
||||
|
||||
/* Finds the room ID from a chat ID */
|
||||
extern char * ParseeGetRoomID(ParseeData *, char *chat_id);
|
||||
|
||||
/* Finds the MUC JID from a chat ID */
|
||||
extern char * ParseeGetMUCID(ParseeData *, char *chat_id);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ extern XMLElement * XMLCreateText(char *data);
|
|||
extern void XMLAddAttr(XMLElement *element, char *key, char *val);
|
||||
extern void XMLAddChild(XMLElement *element, XMLElement *child);
|
||||
|
||||
extern XMLElement * XMLookForUnique(XMLElement *parent, char *tag);
|
||||
|
||||
/* Frees an XML element properly. */
|
||||
extern void XMLFreeElement(XMLElement *element);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ extern XMPPComponent * XMPPInitialiseCompStream(char *host, int port);
|
|||
* after XMPPInitialiseCompStream. */
|
||||
extern bool XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared);
|
||||
|
||||
/* Sends a presence to a user */
|
||||
extern void XMPPSendPresence(XMPPComponent *comp, char *fr, char *to);
|
||||
/* Makes a user join a MUC */
|
||||
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);
|
||||
|
|
@ -30,8 +30,25 @@ extern void XMPPEndCompStream(XMPPComponent *stream);
|
|||
|
||||
/* Sends a loopback stanza (a "killstanza"), used to kill an XMPP listener
|
||||
* thread. */
|
||||
extern void XMPPKillThread(XMPPComponent *jabber);
|
||||
extern void XMPPKillThread(XMPPComponent *jabber, char *killer);
|
||||
|
||||
/* Checks if a stanza is a "killstanza". */
|
||||
extern bool XMPPIsKiller(XMLElement *);
|
||||
|
||||
typedef struct MUCInfo {
|
||||
bool exists;
|
||||
|
||||
XMPPComponent *jabber;
|
||||
XMLElement *iq_reply;
|
||||
} MUCInfo;
|
||||
|
||||
/* Queries a MUC's existence, and if $[out] is set, stores information
|
||||
* pertaining the MUC itself from an <iq> query, to be freed by
|
||||
* XMPPFreeMUCInfo */
|
||||
extern bool XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out);
|
||||
|
||||
/* Retrieves the MUC's name from an IQ reply */
|
||||
extern char * XMPPGetMUCName(MUCInfo info);
|
||||
|
||||
extern void XMPPFreeMUCInfo(MUCInfo info);
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue