Compare commits

..

3 commits

Author SHA1 Message Date
lda
e36f4357ab [ADD/WIP] Start testing code
Some checks are pending
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Waiting to run
I think I'll manage PDU depth later(with an actual good way to handle
it properly(that is not just setting it to the max and calling it a
day.)
2024-05-17 23:57:32 +02:00
lda
1753c2188b [ADD/UNTESTED] Set ServerPart on room creation
Not tested as of now!
2024-05-17 13:52:25 +02:00
lda
9ffa37d7a7 [ADD/UNTESTED] Start implementing event sending
This is NOT perfect. This *will* need changes before it gets pushed.
2024-05-17 13:37:34 +02:00
7 changed files with 429 additions and 67 deletions

View file

@ -51,7 +51,8 @@
"required": true "required": true
}, },
"redacts": { "redacts": {
"type": "string" "type": "string",
"required": false
}, },
"room_id": { "room_id": {
"type": "string", "type": "string",

View file

@ -118,7 +118,10 @@ CanonicalJsonEncode(HashMap * object, Stream * out)
ArrayAdd(keys, key); ArrayAdd(keys, key);
} }
if (ArraySize(keys) != 0)
{
ArraySort(keys, CanonicalJsonKeyCompare); ArraySort(keys, CanonicalJsonKeyCompare);
}
/* The total number of bytes written */ /* The total number of bytes written */
length = 0; length = 0;

View file

@ -487,7 +487,7 @@ ParserRecomposeCommonID(CommonID id)
if (id.server.hostname) if (id.server.hostname)
{ {
char *server = ParserRecomposeServerPart(id.server); char *server = ParserRecomposeServerPart(id.server);
char *tmp = StrConcat(4, "@", ret, ":", server); char *tmp = StrConcat(3, ret, ":", server);
Free(ret); Free(ret);
Free(server); Free(server);

View file

@ -52,26 +52,39 @@
struct Room struct Room
{ {
Db *db; Db *db;
DbRef *ref;
DbRef *state_ref;
DbRef *leaves_ref; /* Reference to the leaf list */
ServerPart creator;
char *id; char *id;
int version; int version;
}; };
static char * static char *
GenerateRoomId(void) GenerateRoomId(ServerPart s)
{ {
return StrRandom(32); /* TODO: Add extra room info somehow CommonID cid;
* (I don't feel like theres really a way to char *string;
* get the server info where we're doing that.) */
cid.sigil = '!';
cid.local = StrRandom(32);
cid.server = s;
string = ParserRecomposeCommonID(cid);
Free(cid.local);
return string;
} }
Room * Room *
RoomCreate(Db * db, User *user, RoomCreateRequest * req) RoomCreate(Db * db, User *user, RoomCreateRequest * req, ServerPart s)
{ {
Room *room; Room *room;
char *version_string; char *version_string, *full_creator;
int version_num = 1; int version_num = 1;
HashMap *json;
if (!db || !req || !user) if (!db || !req || !user)
{ {
return NULL; return NULL;
@ -87,19 +100,79 @@ RoomCreate(Db * db, User *user, RoomCreateRequest * req)
} }
room = Malloc(sizeof(Room)); room = Malloc(sizeof(Room));
room->db = db; room->db = db;
room->id = GenerateRoomId(); /* TODO: Check config. */ room->creator.hostname = s.hostname ? StrDuplicate(s.hostname) : NULL;
room->creator.port = s.port ? StrDuplicate(s.port) : NULL;
room->id = GenerateRoomId(s);
room->version = version_num; room->version = version_num;
room->ref = DbCreate(db, 3, "rooms", room->id, "state"); room->state_ref = DbCreate(db, 3, "rooms", room->id, "state");
room->leaves_ref = DbCreate(db, 3, "rooms", room->id, "leaves");
json = DbJson(room->leaves_ref);
JsonSet(json, JsonValueArray(ArrayCreate()), 1, "leaves");
full_creator = ParserRecomposeServerPart(room->creator);
JsonSet(json, JsonValueString(full_creator), 1, "creator");
Free(full_creator);
/* TODO: Populate room with information */ {
HashMap *event = HashMapCreate();
HashMap *content = HashMapCreate();
CommonID sender;
char *sender_str;
sender.sigil = '@';
sender.local = UserGetName(user);
sender.server = s;
sender_str = ParserRecomposeCommonID(sender);
JsonSet(event, JsonValueString(sender_str), 1, "sender");
if (room->version <= 10)
{
JsonSet(content, JsonValueString(sender_str), 1, "creator");
}
Free(sender_str);
JsonSet(event, JsonValueString("m.room.create"), 1, "type");
JsonSet(event, JsonValueString(""), 1, "state_key");
JsonSet(event, JsonValueObject(content), 1, "content");
JsonFree(RoomEventSend(room, event));
JsonFree(event);
}
{
HashMap *event = HashMapCreate();
HashMap *content = HashMapCreate();
CommonID sender;
char *sender_str;
sender.sigil = '@';
sender.local = UserGetName(user);
sender.server = s;
sender_str = ParserRecomposeCommonID(sender);
JsonSet(event, JsonValueString(sender_str), 1, "sender");
JsonSet(content, JsonValueString("join"), 1, "membership");
JsonSet(event, JsonValueString("m.room.member"), 1, "type");
JsonSet(event, JsonValueString(sender_str), 1, "state_key");
Free(sender_str);
JsonSet(event, JsonValueObject(content), 1, "content");
JsonFree(RoomEventSend(room, event));
JsonFree(event);
}
/* TODO: The rest of the events mandated by the specification on
* POST /createRoom. Also clean up that code, so that it is more
* straightforward(and short). */
return room; return room;
} }
Room * Room *
RoomLock(Db * db, char *id) RoomLock(Db * db, char *id)
{ {
DbRef *ref; DbRef *state_ref, *leaves_ref;
HashMap *json;
Room *room; Room *room;
if (!db || !id) if (!db || !id)
@ -107,9 +180,10 @@ RoomLock(Db * db, char *id)
return NULL; return NULL;
} }
ref = DbLock(db, 3, "rooms", id, "state"); state_ref = DbLock(db, 3, "rooms", id, "state");
leaves_ref = DbLock(db, 3, "rooms", id, "leaves");
if (!ref) if (!state_ref || !leaves_ref)
{ {
return NULL; return NULL;
} }
@ -117,14 +191,21 @@ RoomLock(Db * db, char *id)
room = Malloc(sizeof(Room)); room = Malloc(sizeof(Room));
if (!room) if (!room)
{ {
DbUnlock(db, ref); DbUnlock(db, state_ref);
DbUnlock(db, leaves_ref);
return NULL; return NULL;
} }
room->db = db; room->db = db;
room->ref = ref; room->state_ref = state_ref;
room->leaves_ref = leaves_ref;
room->id = StrDuplicate(id); room->id = StrDuplicate(id);
json = DbJson(room->leaves_ref);
ParseServerPart(
JsonValueAsString(JsonGet(json, 1, "creator")),
&room->creator);
return room; return room;
} }
@ -132,7 +213,8 @@ int
RoomUnlock(Room * room) RoomUnlock(Room * room)
{ {
Db *db; Db *db;
DbRef *ref; DbRef *state_ref;
DbRef *leaves_ref;
if (!room) if (!room)
{ {
@ -140,12 +222,16 @@ RoomUnlock(Room * room)
} }
db = room->db; db = room->db;
ref = room->ref; state_ref = room->state_ref;
leaves_ref = room->leaves_ref;
Free(room->id); Free(room->id);
Free(room); Free(room);
return DbUnlock(db, ref); ServerPartFree(room->creator);
return DbUnlock(db, state_ref) &&
DbUnlock(db, leaves_ref);
} }
char * char *
@ -171,7 +257,7 @@ RoomStateGet(Room * room)
/* TODO: Consider caching the deserialised result, as doing that on a /* TODO: Consider caching the deserialised result, as doing that on a
* large state would probably eat up a lot of time! */ * large state would probably eat up a lot of time! */
database_state = DbJson(room->ref); database_state = DbJson(room->state_ref);
return StateDeserialise(database_state); return StateDeserialise(database_state);
} }
HashMap * HashMap *
@ -199,7 +285,7 @@ RoomStateGetID(Room * room, char *event_id)
do \ do \
{ \ { \
id_##n = StateGet(S, type, key); \ id_##n = StateGet(S, type, key); \
if (id_##n) \ if (!id_##n) \
{ \ { \
goto finish; \ goto finish; \
} \ } \
@ -252,6 +338,7 @@ RoomUserHasMembership(Room * room, HashMap *state, char *user, char *mbr)
membership_value = membership_value =
JsonValueAsString(JsonGet(membership, 2, "content", "membership")); JsonValueAsString(JsonGet(membership, 2, "content", "membership"));
ret = StrEquals(membership_value, mbr); ret = StrEquals(membership_value, mbr);
finish: finish:
@ -345,9 +432,12 @@ finish:
} }
static bool static bool
PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu) PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu, ServerPart serv)
{ {
char *unused; char *unused;
Array *prev_events;
size_t i;
CommonID cid;
if (PduV1FromJson(event, pdu, &unused)) if (PduV1FromJson(event, pdu, &unused))
{ {
return true; return true;
@ -358,13 +448,47 @@ PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu)
* has some ideas on how this could be done(up until stage 5). */ * has some ideas on how this could be done(up until stage 5). */
pdu->sender = pdu->sender =
StrDuplicate(JsonValueAsString(JsonGet(event, 1, "sender"))); StrDuplicate(JsonValueAsString(JsonGet(event, 1, "sender")));
pdu->type =
StrDuplicate(JsonValueAsString(JsonGet(event, 1, "type")));
pdu->redacts = NULL;
if (JsonGet(event, 1, "state_key"))
{
pdu->state_key =
StrDuplicate(JsonValueAsString(JsonGet(event, 1, "state_key")));
}
pdu->auth_events = ArrayCreate(); pdu->auth_events = ArrayCreate();
pdu->origin_server_ts = UtilTsMillis(); pdu->origin_server_ts = UtilTsMillis();
pdu->content = JsonDuplicate(event); /* Copy the original JSON data */ pdu->content =
JsonDuplicate(JsonValueAsObject(JsonGet(event, 1, "content")));
pdu->room_id = StrDuplicate(room->id);
pdu->signatures = HashMapCreate();
/* Create a random event ID for this PDU.
* TODO: Optionally verify whenever it's already used, but that
* would be unlikely considering the lengths of event IDs. */
cid.sigil = '$';
cid.local = StrRandom(32);
cid.server.hostname = StrDuplicate(serv.hostname);
cid.server.port = serv.port ? StrDuplicate(serv.port) : NULL;
pdu->event_id = ParserRecomposeCommonID(cid);
CommonIDFree(cid);
/* Fill prev_events with actual event data.
* Note that we don't actually *clear* out these from our list, as
* that should be done later. */
pdu->prev_events = ArrayCreate();
prev_events = RoomPrevEventsGet(room);
for (i = 0; i < ArraySize(prev_events); i++)
{
HashMap *event = JsonValueAsObject(ArrayGet(prev_events, i));
JsonValue *event_id = JsonGet(event, 1, "event_id");
ArrayAdd(pdu->prev_events, JsonValueDuplicate(event_id));
}
/* TODO: Signature and alldat. */ /* TODO: Signature and alldat. */
(void) room;
return false; return false;
} }
static bool static bool
@ -424,6 +548,11 @@ ValidAuthEventV1(PduV1 *auth_pdu, PduV1 *pdu)
/* TODO: Check if it's the latest in terms of [pdu] */ /* TODO: Check if it's the latest in terms of [pdu] */
return true; return true;
} }
if (IsState(auth_pdu, "m.room.member", pdu->sender))
{
/* TODO: Check if it's the latest in terms of [pdu] */
return true;
}
if (StrEquals(pdu->type, "m.room.member")) if (StrEquals(pdu->type, "m.room.member"))
{ {
char *membership = char *membership =
@ -466,7 +595,7 @@ VerifyPDUV1(PduV1 *auth_pdu)
* https://spec.matrix.org/v1.7/server-server-api/ * https://spec.matrix.org/v1.7/server-server-api/
* #checks-performed-on-receipt-of-a-pdu */ * #checks-performed-on-receipt-of-a-pdu */
(void) auth_pdu; (void) auth_pdu;
return false; /* This only shows whenever an event was rejected, not return true; /* This only shows whenever an event was rejected, not
* soft-failed */ * soft-failed */
} }
static bool static bool
@ -479,15 +608,16 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
state_keytype = HashMapCreate(); state_keytype = HashMapCreate();
for (i = 0; i < ArraySize(pdu.auth_events); i++) for (i = 0; i < ArraySize(pdu.auth_events); i++)
{ {
char *event_id = ArrayGet(pdu.auth_events, i); char *event_id = JsonValueAsString(ArrayGet(pdu.auth_events, i));
HashMap *event = RoomEventFetch(room, event_id); HashMap *event = RoomEventFetch(room, event_id);
PduV1 auth_pdu; PduV1 auth_pdu = { 0 };
char *key_type_id; char *key_type_id;
if (!PduV1FromJson(event, &auth_pdu, &ignored)) if (!PduV1FromJson(event, &auth_pdu, &ignored))
{ {
HashMapFree(event); JsonFree(event);
HashMapFree(state_keytype);
return false; /* Yeah... we aren't doing that. */ return false; /* Yeah... we aren't doing that. */
} }
@ -499,7 +629,7 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
if (HashMapGet(state_keytype, key_type_id)) if (HashMapGet(state_keytype, key_type_id))
{ {
/* Duplicate found! We actually ignore it's actual value. */ /* Duplicate found! We actually ignore it's actual value. */
HashMapFree(event); JsonFree(event);
PduV1Free(&auth_pdu); PduV1Free(&auth_pdu);
HashMapFree(state_keytype); HashMapFree(state_keytype);
@ -516,6 +646,9 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
* described in the server specification, reject. */ * described in the server specification, reject. */
if (!ValidAuthEventV1(&auth_pdu, &pdu)) if (!ValidAuthEventV1(&auth_pdu, &pdu))
{ {
JsonFree(event);
PduV1Free(&auth_pdu);
HashMapFree(state_keytype);
return false; return false;
} }
/* Step 2.3: If there are entries which were themselves rejected /* Step 2.3: If there are entries which were themselves rejected
@ -523,6 +656,9 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
* TODO */ * TODO */
if (!VerifyPDUV1(&auth_pdu)) if (!VerifyPDUV1(&auth_pdu))
{ {
PduV1Free(&auth_pdu);
JsonFree(event);
HashMapFree(state_keytype);
return false; return false;
} }
@ -533,7 +669,7 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
room_create = true; /* Here, we check for the opposite. */ room_create = true; /* Here, we check for the opposite. */
} }
HashMapFree(event); JsonFree(event);
PduV1Free(&auth_pdu); PduV1Free(&auth_pdu);
} }
HashMapFree(state_keytype); HashMapFree(state_keytype);
@ -648,8 +784,10 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state)
thirdpi_event_sender = JsonValueAsString(JsonGet(third_pi_event, 1, "sender")); thirdpi_event_sender = JsonValueAsString(JsonGet(third_pi_event, 1, "sender"));
if (!StrEquals(thirdpi_event_sender, pdu.sender)) if (!StrEquals(thirdpi_event_sender, pdu.sender))
{ {
JsonFree(third_pi_event);
return false; return false;
} }
JsonFree(third_pi_event);
/* TODO: /* TODO:
* Step 5.3.1.7: If any signature in signed matches any public key in * Step 5.3.1.7: If any signature in signed matches any public key in
@ -762,23 +900,24 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, HashMap *state)
Array *prev = pdu.prev_events; Array *prev = pdu.prev_events;
if (ArraySize(prev) == 1) if (ArraySize(prev) == 1)
{ {
char *prev_id = ArrayGet(prev, 0); /* Interperet prev properly, as a list of JsonObjects. */
char *prev_id = JsonValueAsString(ArrayGet(prev, 0));
char *ignored; char *ignored;
HashMap *prev = RoomEventFetch(room, prev_id); HashMap *prev_event = RoomEventFetch(room, prev_id);
PduV1 prev_pdu; PduV1 prev_pdu;
if (prev && PduV1FromJson(prev, &prev_pdu, &ignored)) if (prev && PduV1FromJson(prev_event, &prev_pdu, &ignored))
{ {
if (StrEquals(prev_pdu.type, "m.room.create") && if (StrEquals(prev_pdu.type, "m.room.create") &&
StrEquals(prev_pdu.sender, pdu.state_key)) StrEquals(prev_pdu.sender, pdu.state_key))
{ {
PduV1Free(&prev_pdu); PduV1Free(&prev_pdu);
JsonFree(prev); JsonFree(prev_event);
return true; return true;
} }
PduV1Free(&prev_pdu); PduV1Free(&prev_pdu);
} }
JsonFree(prev); JsonFree(prev_event);
} }
/* Step 5.2.2: If the sender does not match state_key, reject. */ /* Step 5.2.2: If the sender does not match state_key, reject. */
@ -1069,6 +1208,7 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, HashMap *state)
} }
} }
} }
JsonFree(create_event);
/* Step 4: If type is m.room.aliases */ /* Step 4: If type is m.room.aliases */
if (StrEquals(pdu.type, "m.room.aliases")) if (StrEquals(pdu.type, "m.room.aliases"))
@ -1168,29 +1308,70 @@ RoomHashEventV1(PduV1 pdu)
JsonFree(json); JsonFree(json);
return sha; return sha;
} }
static bool
EventFits(HashMap *pdu)
{
int size = CanonicalJsonEncode(pdu, NULL);
JsonValue *key;
/* Main PDU length is 65536 bytes */
if (size > 65536)
{
return false;
}
#define VerifyKey(k,s) do \
{ \
if ((key = JsonGet(pdu, 1, k)) && \
(strlen(JsonValueAsString(key)) > s)) \
{ \
return false; \
} \
} \
while (0)
VerifyKey("sender", 255);
VerifyKey("room_id", 255);
VerifyKey("state_key", 255);
VerifyKey("type", 255);
VerifyKey("event_id", 255);
return true;
#undef VerifyKey
}
static bool
EventFitsV1(PduV1 pdu)
{
HashMap *hm;
bool ret;
hm = PduV1ToJson(&pdu);
ret = EventFits(hm);
JsonFree(hm);
return ret;
}
static HashMap * static HashMap *
RoomEventSendV1(Room * room, HashMap * event) RoomEventSendV1(Room * room, HashMap * event)
{ {
PduV1 pdu = { 0 }; PduV1 pdu = { 0 };
HashMap *pdu_object = NULL; HashMap *pdu_object = NULL;
bool client_event; bool client_event, valid = false;
HashMap *state = NULL; HashMap *state = NULL;
client_event = PopulateEventV1(room, event, &pdu); client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room));
pdu_object = PduV1ToJson(&pdu); pdu_object = PduV1ToJson(&pdu);
state = StateResolve(room, pdu_object); /* Compute the state state = StateResolve(room, pdu_object); /* Compute the state
* at that point for later. */ * at that point for later. */
if (client_event) if (client_event)
{ {
char *event_id; char *ev_id;
#define AddState(type, key) do \ #define AddState(type, key) do \
{ \ { \
event_id = StateGet(state, type, key); \ ev_id = StateGet(state, type, key); \
if (event_id) \ if (ev_id) \
{ \ { \
char *dup = StrDuplicate(event_id); \ JsonValue *v = JsonValueString(ev_id); \
ArrayAdd(pdu.auth_events, dup); \ ArrayAdd(pdu.auth_events, v); \
} \ } \
} \ } \
while (0) while (0)
@ -1239,7 +1420,26 @@ RoomEventSendV1(Room * room, HashMap * event)
pdu.hashes.sha256 = RoomHashEventV1(pdu); pdu.hashes.sha256 = RoomHashEventV1(pdu);
#undef AddState #undef AddState
} }
/* TODO: Do a size check! */ /* TODO: For PDU events, we should verify their hashes. */
if (!EventFitsV1(pdu))
{
/* Reject this event as it is too large. */
goto finish;
}
if (!RoomAuthoriseEventV1(room, pdu, state))
{
/* Reject this event as the current state does not allow it.
* TODO: Make the auth check function return a string showing the
* error status to the user. */
goto finish;
}
RoomAddEventV1(room, pdu);
valid = true;
/* If it is a client event, we should make sure that we shout at
* every other homeserver about our new event. */
finish:
if (state) if (state)
{ {
JsonFree(state); JsonFree(state);
@ -1247,9 +1447,14 @@ RoomEventSendV1(Room * room, HashMap * event)
if (pdu_object) if (pdu_object)
{ {
JsonFree(pdu_object); JsonFree(pdu_object);
pdu_object = NULL;
}
if (valid)
{
pdu_object = PduV1ToJson(&pdu);
} }
PduV1Free(&pdu); PduV1Free(&pdu);
return NULL; return pdu_object;
} }
static HashMap * static HashMap *
RoomEventSendV3(Room * room, HashMap * event) RoomEventSendV3(Room * room, HashMap * event)
@ -1322,3 +1527,93 @@ finish:
Free(safe_id); Free(safe_id);
return ret; return ret;
} }
Array *
RoomPrevEventsGet(Room *room)
{
HashMap *json;
if (!room)
{
return NULL;
}
json = DbJson(room->leaves_ref);
return JsonValueAsArray(JsonGet(json, 1, "leaves"));
}
ServerPart
RoomGetCreator(Room *room)
{
ServerPart ret = { 0 };
if (!room)
{
return ret;
}
return room->creator;
}
bool
RoomAddEventV1(Room *room, PduV1 pdu)
{
DbRef *event_ref;
Array *prev_events, *leaves;
HashMap *leaves_json, *pdu_json;
JsonValue *leaves_val;
char *safe_id;
size_t i;
if (!room || room->version >= 3)
{
return false;
}
leaves_json = DbJson(room->leaves_ref);
leaves_val = JsonValueDuplicate(JsonGet(leaves_json, 1, "leaves"));
leaves = JsonValueAsArray(leaves_val);
Free(leaves_val);
prev_events = pdu.prev_events;
for (i = 0; i < ArraySize(prev_events); i++)
{
JsonValue *event_val = ArrayGet(prev_events, i);
char *event_id = JsonValueAsString(event_val);
size_t j;
ssize_t delete_index = -1;
for (j = 0; j < ArraySize(leaves); j++)
{
JsonValue *leaf_val = ArrayGet(leaves, j);
HashMap *leaf_object = JsonValueAsObject(leaf_val);
char *leaf_id =
JsonValueAsString(JsonGet(leaf_object, 1, "event_id"));
if (StrEquals(leaf_id, event_id))
{
delete_index = j;
break;
}
}
if (delete_index == -1)
{
continue;
}
JsonValueFree(ArrayDelete(leaves, delete_index));
}
safe_id = CreateSafeID(pdu.event_id);
event_ref = DbCreate(room->db, 4, "rooms", room->id, "events", safe_id);
Free(safe_id);
pdu_json = PduV1ToJson(&pdu);
DbJsonSet(event_ref, pdu_json);
ArrayAdd(leaves, JsonValueObject(pdu_json));
leaves_json = JsonDuplicate(leaves_json);
JsonValueFree(HashMapDelete(leaves_json, "leaves"));
JsonSet(leaves_json, JsonValueArray(leaves), 1, "leaves");
DbJsonSet(room->leaves_ref, leaves_json);
JsonFree(leaves_json);
DbUnlock(room->db, event_ref);
/* TODO: Store DAG relationships, somehow.
* Also keep track of PDU depth. */
return true;
}

View file

@ -27,6 +27,7 @@
#include <Routes.h> #include <Routes.h>
#include <Cytoplasm/Json.h> #include <Cytoplasm/Json.h>
#include <Cytoplasm/Log.h>
#include <Room.h> #include <Room.h>
#include <User.h> #include <User.h>
@ -41,12 +42,17 @@ ROUTE_IMPL(RouteCreateRoom, path, argp)
Db *db = args->matrixArgs->db; Db *db = args->matrixArgs->db;
RoomCreateRequest parsed; RoomCreateRequest parsed;
User *user = NULL; User *user = NULL;
Config cfg;
ServerPart server;
char *token; char *token;
char *err; char *err;
(void) path; (void) path;
ConfigLock(db, &cfg);
ParseServerPart(cfg.serverName, &server);
if (HttpRequestMethodGet(args->context) != HTTP_POST) if (HttpRequestMethodGet(args->context) != HTTP_POST)
{ {
err = "Unknown request method."; err = "Unknown request method.";
@ -86,7 +92,9 @@ ROUTE_IMPL(RouteCreateRoom, path, argp)
JsonFree(request); JsonFree(request);
request = NULL; request = NULL;
if (!(room = RoomCreate(db, user, &parsed))) Log(LOG_INFO, "Creating room...");
if (!(room = RoomCreate(db, user, &parsed, server)))
{ {
err = "Couldn't create room."; err = "Couldn't create room.";
/* Consider another error status. */ /* Consider another error status. */
@ -102,5 +110,7 @@ finish:
JsonFree(request); JsonFree(request);
RoomUnlock(room); RoomUnlock(room);
UserUnlock(user); UserUnlock(user);
ConfigUnlock(&cfg);
ServerPartFree(server);
return response; return response;
} }

View file

@ -40,9 +40,11 @@ int
V1Cmp(void *a, void *b) V1Cmp(void *a, void *b)
{ {
HashMap *e1 = a, *e2 = b; HashMap *e1 = a, *e2 = b;
int64_t depth1 = int64_t depth1, depth2;
depth1 =
JsonValueAsInteger(JsonGet(e1, 1, "depth")); JsonValueAsInteger(JsonGet(e1, 1, "depth"));
int64_t depth2 = depth2 =
JsonValueAsInteger(JsonGet(e2, 1, "depth")); JsonValueAsInteger(JsonGet(e2, 1, "depth"));
if (depth1 > depth2) if (depth1 > depth2)
@ -94,6 +96,7 @@ StateResolveV1(Room * room, Array * states)
if (HashMapGet(R, tuple)) if (HashMapGet(R, tuple))
{ {
Array *arr; Array *arr;
HashMap *hm;
/* Conflicts! */ /* Conflicts! */
HashMapDelete(R, tuple); HashMapDelete(R, tuple);
@ -102,13 +105,14 @@ StateResolveV1(Room * room, Array * states)
{ {
arr = ArrayCreate(); arr = ArrayCreate();
} }
ArrayAdd(arr, RoomEventFetch(room, event_id)); hm = RoomEventFetch(room, event_id);
ArrayAdd(arr, hm);
HashMapSet(conflicts, tuple, arr); HashMapSet(conflicts, tuple, arr);
} }
else else
{ {
/* Add to R */ /* Add to R */
HashMapSet(R, tuple, event_id); HashMapSet(R, tuple, StrDuplicate(event_id));
} }
} }
@ -138,7 +142,13 @@ StateResolveV1(Room * room, Array * states)
Free(type); Free(type);
Free(key); Free(key);
} }
if (ArraySize(events) != 0)
{
/* TODO: Cytoplasm currently doesn't behave correctly
* when the array size is 0, I'll need to file a bug
* report/PR eventually. */
ArraySort(events, V1Cmp); ArraySort(events, V1Cmp);
}
/* Add first event. */ /* Add first event. */
first = ArrayDelete(events, 0); first = ArrayDelete(events, 0);
StateSet( StateSet(
@ -146,6 +156,7 @@ StateResolveV1(Room * room, Array * states)
JsonValueAsString(JsonGet(first, 1, "type")), JsonValueAsString(JsonGet(first, 1, "type")),
JsonValueAsString(JsonGet(first, 1, "state_key")), JsonValueAsString(JsonGet(first, 1, "state_key")),
JsonValueAsString(JsonGet(first, 1, "event_id"))); JsonValueAsString(JsonGet(first, 1, "event_id")));
JsonFree(first);
for (j = 0; j < (ssize_t) ArraySize(events); j++) for (j = 0; j < (ssize_t) ArraySize(events); j++)
{ {
@ -161,10 +172,12 @@ StateResolveV1(Room * room, Array * states)
else else
{ {
PduV1Free(&pdu); PduV1Free(&pdu);
JsonFree(event);
break; break;
} }
(void) msg; (void) msg;
PduV1Free(&pdu); PduV1Free(&pdu);
JsonFree(event);
} }
ArrayFree(events); ArrayFree(events);
/* Delete all elements within a key. */ /* Delete all elements within a key. */
@ -183,13 +196,16 @@ StateResolveV1(Room * room, Array * states)
StateSet(conflicts, t, state_key, NULL); StateSet(conflicts, t, state_key, NULL);
Free(state_key); Free(state_key);
} }
Free(state_keys); ArrayFree(state_keys);
} }
ArrayFree(types); ArrayFree(types);
while (StateIterate(conflicts, &type, &key, (void **) &conflicting)) while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
{
if (ArraySize(conflicting) != 0)
{ {
ArraySort(conflicting, V1Cmp); ArraySort(conflicting, V1Cmp);
}
for (j = ArraySize(conflicting) - 1; j >= 0; j--) for (j = ArraySize(conflicting) - 1; j >= 0; j--)
{ {
HashMap *event = ArrayGet(events, j); HashMap *event = ArrayGet(events, j);
@ -210,8 +226,7 @@ StateResolveV1(Room * room, Array * states)
Free(type); Free(type);
Free(key); Free(key);
} }
while (HashMapIterate(conflicts, &type, (void **) &conflicting))
while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
{ {
for (i = 0; i < ArraySize(conflicting); i++) for (i = 0; i < ArraySize(conflicting); i++)
{ {
@ -239,6 +254,8 @@ StateResolve(Room * room, HashMap * event)
Array *prevEvents; Array *prevEvents;
HashMap *ret_state;
if (!room || !event) if (!room || !event)
{ {
return NULL; return NULL;
@ -251,26 +268,46 @@ StateResolve(Room * room, HashMap * event)
{ {
return NULL; return NULL;
} }
prevEvents = JsonValueAsArray(HashMapGet(event, "prev_events"));
prevEvents = HashMapGet(event, "prev_events");
for (i = 0; i < ArraySize(prevEvents); i++) for (i = 0; i < ArraySize(prevEvents); i++)
{ {
HashMap *prevEvent = ArrayGet(prevEvents, i); HashMap *prevEvent =
RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i)));
HashMap *state = StateResolve(room, prevEvent); HashMap *state = StateResolve(room, prevEvent);
/* TODO: Apply prevEvent to state if it is a state event */ if (HashMapGet(prevEvent, "state_key"))
{
ArrayAdd(states, state); StateSet(
state,
JsonValueAsString(HashMapGet(prevEvent, "type")),
JsonValueAsString(HashMapGet(prevEvent, "state_key")),
JsonValueAsString(HashMapGet(prevEvent, "event_id")));
} }
ArrayAdd(states, state);
JsonFree(prevEvent);
}
ret_state = NULL;
switch (RoomVersionGet(room)) switch (RoomVersionGet(room))
{ {
case 1: case 1:
return StateResolveV1(room, states); ret_state = StateResolveV1(room, states);
break;
default: default:
return StateResolveV2(states); ret_state = StateResolveV2(states);
break;
} }
for (i = 0; i < ArraySize(states); i++)
{
HashMap *state = ArrayGet(states, i);
StateFree(state);
}
ArrayFree(states);
return ret_state;
} }
bool StateIterate(HashMap *state, char **type, char **key, void **event) bool StateIterate(HashMap *state, char **type, char **key, void **event)
{ {
@ -282,11 +319,14 @@ bool StateIterate(HashMap *state, char **type, char **key, void **event)
} }
ret = HashMapIterate(state, &tuple, event); ret = HashMapIterate(state, &tuple, event);
if (ret)
{
tuple = StrDuplicate(tuple); tuple = StrDuplicate(tuple);
*(strchr(tuple, ',')) = '\0'; *(strchr(tuple, ',')) = '\0';
*type = tuple; *type = tuple;
*key = StrDuplicate(tuple + strlen(tuple) + 1); *key = StrDuplicate(tuple + strlen(tuple) + 1);
}
return ret; return ret;
} }

View file

@ -43,6 +43,7 @@
#include <Schema/RoomCreateRequest.h> #include <Schema/RoomCreateRequest.h>
#include <Schema/PduV1.h> #include <Schema/PduV1.h>
#include <Parser.h>
#include <User.h> #include <User.h>
/** /**
@ -54,7 +55,7 @@ typedef struct Room Room;
* Create a new room in the given database using the given * Create a new room in the given database using the given
* RoomCreateRequest. * RoomCreateRequest.
*/ */
extern Room * RoomCreate(Db *, User *, RoomCreateRequest *); extern Room * RoomCreate(Db *, User *, RoomCreateRequest *, ServerPart);
/** /**
* Lock the existing room in the specified database, * Lock the existing room in the specified database,
@ -151,4 +152,16 @@ extern HashMap * RoomEventFetch(Room *, char *);
*/ */
extern bool RoomAuthoriseEventV1(Room *, PduV1, HashMap *); extern bool RoomAuthoriseEventV1(Room *, PduV1, HashMap *);
/**
* Gets the room's creator as a ServerPart. This value should
* not be freed, as it lives alongside the room itself
*/
extern ServerPart RoomGetCreator(Room *);
/**
* Puts a PDUv1 into the event list, while updating the leaf
* list.
*/
extern bool RoomAddEventV1(Room *, PduV1);
#endif /* TELODENDRIA_ROOM_H */ #endif /* TELODENDRIA_ROOM_H */