[ADD/UNTESTED] Start implementing event sending

This is NOT perfect. This *will* need changes before it gets pushed.
This commit is contained in:
lda 2024-05-17 13:37:34 +02:00
parent 15b884b04a
commit 9ffa37d7a7
2 changed files with 194 additions and 23 deletions

View file

@ -52,7 +52,11 @@
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;
@ -70,8 +74,9 @@ Room *
RoomCreate(Db * db, User *user, RoomCreateRequest * req) RoomCreate(Db * db, User *user, RoomCreateRequest * req)
{ {
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;
@ -88,9 +93,17 @@ 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->id = GenerateRoomId(); /* TODO: Check config. */
room->creator.hostname = NULL;
room->creator.port = NULL;
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 */ /* TODO: Populate room with information */
return room; return room;
@ -99,7 +112,8 @@ RoomCreate(Db * db, User *user, RoomCreateRequest * req)
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 +121,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 +132,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 +154,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 +163,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 +198,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 *
@ -345,9 +372,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;
@ -362,9 +392,32 @@ PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu)
pdu->origin_server_ts = UtilTsMillis(); pdu->origin_server_ts = UtilTsMillis();
pdu->content = JsonDuplicate(event); /* Copy the original JSON data */ pdu->content = JsonDuplicate(event); /* Copy the original JSON data */
/* 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 = ArrayGet(prev_events, i);
char *event_id = JsonValueAsString(JsonGet(event, 1, "event_id"));
ArrayAdd(pdu->prev_events, JsonValueString(event_id));
}
/* TODO: Signature and alldat. */ /* TODO: Signature and alldat. */
(void) room;
return false; return false;
} }
static bool static bool
@ -762,23 +815,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. */
@ -1168,15 +1222,56 @@ 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
@ -1239,7 +1334,23 @@ 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;
finish:
if (state) if (state)
{ {
JsonFree(state); JsonFree(state);
@ -1247,9 +1358,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 +1438,45 @@ 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;
char *safe_id;
if (!room || room->version >= 3)
{
return false;
}
/* TODO: Update leaf events. */
safe_id = CreateSafeID(pdu.event_id);
event_ref = DbLock(room->db, 4, "rooms", room->id, "events", safe_id);
Free(safe_id);
DbJsonSet(event_ref, PduV1ToJson(&pdu));
DbUnlock(room->db, event_ref);
/* TODO: Store DAG relationships, somehow. */
return true;
}

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>
/** /**
@ -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 */