forked from Telodendria/Telodendria
[ADD/UNTESTED] Start implementing event sending
This is NOT perfect. This *will* need changes before it gets pushed.
This commit is contained in:
parent
15b884b04a
commit
9ffa37d7a7
2 changed files with 194 additions and 23 deletions
204
src/Room.c
204
src/Room.c
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in a new issue