From b719a16d9f5abc6a677a85849c13d760872f9c57 Mon Sep 17 00:00:00 2001 From: LDA Date: Wed, 21 Aug 2024 11:02:28 +0200 Subject: [PATCH] [MOD] Revamping the state a bit --- src/Room.c | 39 +++-- src/Routes/RouteSync.c | 2 +- src/State.c | 369 ++++++++++++++++++++++++----------------- src/include/Room.h | 8 +- src/include/State.h | 21 ++- 5 files changed, 261 insertions(+), 178 deletions(-) diff --git a/src/Room.c b/src/Room.c index d04864b..856fbb9 100644 --- a/src/Room.c +++ b/src/Room.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -430,7 +431,7 @@ RoomVersionGet(Room * room) return room ? room->version : 0; } -HashMap * +State * RoomStateGet(Room * room) { HashMap *database_state; @@ -446,10 +447,11 @@ RoomStateGet(Room * room) database_state = DbJson(room->state_ref); return StateDeserialise(database_state); } -HashMap * +State * RoomStateGetID(Room * room, char *event_id) { - HashMap *event, *state; + HashMap *event; + State *state; if (!room || !event_id) { return NULL; @@ -490,7 +492,7 @@ RoomStateGetID(Room * room, char *event_id) } \ return ret static bool -RoomIsJoinRule(Room * room, HashMap *state, char *jr) +RoomIsJoinRule(Room * room, State *state, char *jr) { HashMap *joinrule = NULL; char *id_joinrule = NULL; @@ -510,7 +512,7 @@ finish: } /* Verifies if user has a specific membership before [e_id] in the room. */ static bool -RoomUserHasMembership(Room * room, HashMap *state, char *user, char *mbr) +RoomUserHasMembership(Room * room, State *state, char *user, char *mbr) { HashMap *membership = NULL; char *id_membership = NULL; @@ -559,7 +561,7 @@ ParsePL(JsonValue *v, int64_t def) } /* Computes the smallest PL needed to do something somewhere */ static int64_t -RoomMinPL(Room * room, HashMap *state, char *type, char *act) +RoomMinPL(Room * room, State *state, char *type, char *act) { HashMap *pl = NULL; JsonValue *val; @@ -599,7 +601,7 @@ finish: /* Finds the power level of an user before [e_id] was sent. */ /* TODO: The creator should have PL100 by default. */ static int64_t -RoomUserPL(Room * room, HashMap *state, char *user) +RoomUserPL(Room * room, State *state, char *user) { HashMap *pl = NULL; char *id_pl; @@ -919,7 +921,7 @@ AuthoriseAliasV1(PduV1 pdu) return true; } static bool -AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state) +AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state) { int64_t invite_level; int64_t pdu_level; @@ -1018,7 +1020,7 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state) return false; } static bool -AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, HashMap *state) +AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, State *state) { int64_t ban_level = RoomMinPL(room, state, NULL, "ban"); int64_t kick_level = RoomMinPL(room, state, NULL, "kick"); @@ -1061,7 +1063,7 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, HashMap *state) return false; } static bool -AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state) +AuthorizeBanMembershipV1(Room * room, PduV1 pdu, State *state) { int64_t ban_pl, pdu_pl, target_pl; @@ -1086,7 +1088,7 @@ AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state) return false; } static bool -AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, HashMap *state) +AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, State *state) { /* Step 5.2.1: If the only previous event is an m.room.create and the * state_key is the creator, allow. */ @@ -1144,7 +1146,7 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, HashMap *state) return false; } static bool -AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state) +AuthoriseMemberV1(Room * room, PduV1 pdu, State *state) { JsonValue *membership; char *membership_str; @@ -1186,7 +1188,7 @@ AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state) #undef JumpIfMembership } static bool -AuthorisePowerLevelsV1(Room * room, PduV1 pdu, HashMap *state) +AuthorisePowerLevelsV1(Room * room, PduV1 pdu, State *state) { /* Step 10.1: If the users property in content is not an object with * keys that are valid user IDs with values that are integers @@ -1362,7 +1364,7 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu, HashMap *state) return true; } bool -RoomAuthoriseEventV1(Room * room, PduV1 pdu, HashMap *state) +RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state) { HashMap *create_event; char *create_event_id; @@ -1563,12 +1565,13 @@ RoomEventSendV1(Room * room, HashMap * event) PduV1 pdu = { 0 }; HashMap *pdu_object = NULL; bool client_event, valid = false; - HashMap *state = NULL; + State *state = NULL; client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room)); pdu_object = PduV1ToJson(&pdu); state = StateResolve(room, pdu_object); + if (client_event) { char *ev_id; @@ -1851,7 +1854,7 @@ RoomAddEventV1(Room *room, PduV1 pdu) } { - HashMap *state; + State *state; char *type, *state_key, *event_id; pdu_json = PduV1ToJson(&pdu); @@ -2133,7 +2136,7 @@ RoomGetDB(Room *room) bool RoomContainsUser(Room *room, char *user) { - HashMap *state; + State *state; bool ret; if (!room || !user) { @@ -2151,7 +2154,7 @@ RoomContainsUser(Room *room, char *user) bool RoomCanJoin(Room *room, char *user) { - HashMap *state; + State *state; HashMap *joinRule = NULL; char *joinRuleV; bool ret; diff --git a/src/Routes/RouteSync.c b/src/Routes/RouteSync.c index 043ae30..1ba46bc 100644 --- a/src/Routes/RouteSync.c +++ b/src/Routes/RouteSync.c @@ -184,7 +184,7 @@ ROUTE_IMPL(RouteSync, path, argp) char *firstEvent = NULL; char *type, *key, *id; HashMap *joinedObj; - HashMap *state; + State *state; Array *el; size_t j; Room *r; diff --git a/src/State.c b/src/State.c index 32d36af..a0ca473 100644 --- a/src/State.c +++ b/src/State.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,21 @@ #include #include +struct State { + /* A hashmap of event IDs for a state */ + HashMap *table; +}; + + +static State * +InitialiseState(void) +{ + State *ret = Malloc(sizeof(*ret)); + + ret->table = HashMapCreate(); + return ret; +} + int V1Cmp(void *a, void *b) { @@ -76,30 +92,60 @@ V1Cmp(void *a, void *b) return ret; } } -static HashMap * -StateResolveV1(Room * room, Array * states) + +static char * +EncodeStateTuple(char *type, char *stateKey) +{ + size_t typeLen = type ? strlen(type) : 0; + size_t stateKeyLen = stateKey ? strlen(stateKey) : 0; + size_t length = typeLen + 1 + stateKeyLen + 1; + char *tuple = Malloc(length); + char *base64; + + memset(tuple, '\0', length); + memcpy(tuple, type, typeLen); + memcpy(tuple + typeLen + 1, stateKey, stateKeyLen); + + base64 = Base64Encode((const char *) tuple, length); + Free(tuple); + return base64; +} +static void +DecodeStateTuple(char *base64, char **key, char **val) +{ + size_t base64Size = base64 ? strlen(base64) : 0; + char *decodeBuffer = NULL; + if (!base64 || !key || !val) + { + return; + } + + decodeBuffer = Base64Decode((const char *) base64, base64Size); + *key = StrDuplicate(decodeBuffer); + *val = StrDuplicate(decodeBuffer + strlen(decodeBuffer) + 1); + + Free(decodeBuffer); +} + +static void +BuildBaseAndConflictV1(Room *room, Array *states, State *R, HashMap *conflicts) { - HashMap *R = HashMapCreate(); - HashMap *conflicts = HashMapCreate(); - Array *events = NULL, *types = NULL, *conflicting = NULL; size_t i; - ssize_t j; - - char *type, *key, *event_id; - + char *type = NULL, *key = NULL; for (i = 0; i < ArraySize(states); i++) { - HashMap *state = ArrayGet(states, i); - char *tuple; - while (HashMapIterate(state, &tuple, (void **) &event_id)) + char *event_id; + State *state = ArrayGet(states, i); + while (StateIterate(state, &type, &key, (void **) &event_id)) { - if (HashMapGet(R, tuple)) + char *tuple = EncodeStateTuple(type, key); + if (StateGet(R, type, key) || HashMapGet(conflicts, tuple)) { Array *arr; HashMap *hm; /* Conflicts! */ - HashMapDelete(R, tuple); + StateSet(R, type, key, NULL); arr = HashMapGet(conflicts, tuple); if (!arr) { @@ -112,98 +158,136 @@ StateResolveV1(Room * room, Array * states) else { /* Add to R */ - HashMapSet(R, tuple, StrDuplicate(event_id)); - } - - } - } - /* R and conflicts are now configured */ - types = ArrayCreate(); - ArrayAdd(types, "m.room.power_levels"); - ArrayAdd(types, "m.room.join_rules"); - ArrayAdd(types, "m.room.member"); - for (i = 0; i < ArraySize(types); i++) - { - char *t = ArrayGet(types, i); - HashMap *first; - Array *state_keys; - - events = ArrayCreate(); - while (StateIterate(conflicts, &type, &key, (void **) &conflicting)) - { - if (StrEquals(type, t)) - { - for (j = 0; j < (ssize_t) ArraySize(conflicting); j++) - { - HashMap *event = ArrayGet(conflicting, j); - ArrayAdd(events, event); - } + StateSet(R, type, key, event_id); } + Free(tuple); Free(type); Free(key); } - ArraySort(events, V1Cmp); - /* Add first event. */ - first = ArrayDelete(events, 0); - StateSet( - R, - JsonValueAsString(JsonGet(first, 1, "type")), - JsonValueAsString(JsonGet(first, 1, "state_key")), - JsonValueAsString(JsonGet(first, 1, "event_id"))); - JsonFree(first); - - for (j = 0; j < (ssize_t) ArraySize(events); j++) - { - HashMap *event = ArrayGet(events, j); - PduV1 pdu; - char *msg; + } +} +static void +FixoutConflictV1(char *t, Room *room, State *R, HashMap *conflicts) +{ + HashMap *first; + Array *state_keys, *conflicting, *events; + char *tuple; + size_t i; - PduV1FromJson(event, &pdu, &msg); - if (RoomAuthoriseEventV1(room, pdu, R)) + events = ArrayCreate(); + while (HashMapIterate(conflicts, &tuple, (void **) &conflicting)) + { + char *type, *key; + DecodeStateTuple(tuple, &type, &key); + + if (StrEquals(type, t)) + { + for (i = 0; i < ArraySize(conflicting); i++) { - StateSet(R, pdu.type, pdu.state_key, pdu.event_id); + HashMap *event = ArrayGet(conflicting, i); + ArrayAdd(events, event); } - else - { - PduV1Free(&pdu); - JsonFree(event); - break; - } - (void) msg; + } + Free(type); + Free(key); + } + ArraySort(events, V1Cmp); + /* Add first event. */ + first = ArrayDelete(events, 0); + StateSet( + R, + JsonValueAsString(JsonGet(first, 1, "type")), + JsonValueAsString(JsonGet(first, 1, "state_key")), + JsonValueAsString(JsonGet(first, 1, "event_id")) + ); + JsonFree(first); + + for (i = 0; i < ArraySize(events); i++) + { + HashMap *event = ArrayGet(events, i); + PduV1 pdu; + char *msg; + + PduV1FromJson(event, &pdu, &msg); + if (RoomAuthoriseEventV1(room, pdu, R)) + { + StateSet(R, pdu.type, pdu.state_key, pdu.event_id); + } + else + { PduV1Free(&pdu); JsonFree(event); + break; } - ArrayFree(events); - /* Delete all elements within a key. */ - state_keys = ArrayCreate(); - while (StateIterate(conflicts, &type, &key, (void **) &event_id)) - { - if (StrEquals(type, t)) - { - ArrayAdd(state_keys, key); - } - Free(type); - } - for (j = 0; j < (ssize_t) ArraySize(state_keys); j++) - { - char *state_key = ArrayGet(state_keys, j); - StateSet(conflicts, t, state_key, NULL); - Free(state_key); - } - ArrayFree(state_keys); + (void) msg; + PduV1Free(&pdu); + JsonFree(event); } - ArrayFree(types); + ArrayFree(events); - while (StateIterate(conflicts, &type, &key, (void **) &conflicting)) + state_keys = ArrayCreate(); + while (HashMapIterate(conflicts, &tuple, (void **) &conflicting)) { - ArraySort(conflicting, V1Cmp); - for (j = ArraySize(conflicting) - 1; j >= 0; j--) + char *type, *key; + DecodeStateTuple(tuple, &type, &key); + + if (StrEquals(type, t)) { - HashMap *event = ArrayGet(events, j); - PduV1 pdu; + ArrayAdd(state_keys, key); + } + else + { + Free(key); + } + Free(type); + } + for (i = 0; i < ArraySize(state_keys); i++) + { + char *state_key = ArrayGet(state_keys, i); + char *tuple = EncodeStateTuple(t, state_key); + Array *conflict = HashMapDelete(conflicts, tuple); + + /* All of the other values are already freed */ + ArrayFree(conflict); + + Free(state_key); + Free(tuple); + } + ArrayFree(state_keys); +} + + +static State * +StateResolveV1(Room * room, Array * states) +{ + State *R = InitialiseState(); + HashMap *conflicts = HashMapCreate(); + Array *events = NULL, *conflicting = NULL; + char *type, *key, *tuple; + + BuildBaseAndConflictV1(room, states, R, conflicts); + + /* R and conflicts are now configured */ + FixoutConflictV1("m.room.power_levels", room, R, conflicts); + FixoutConflictV1("m.room.join_rules", room, R, conflicts); + FixoutConflictV1("m.room.member", room, R, conflicts); + + while (HashMapIterate(conflicts, &tuple, (void **) &conflicting)) + { + ssize_t i; + DecodeStateTuple(tuple, &type, &key); + + ArraySort(conflicting, V1Cmp); + for (i = ArraySize(conflicting) - 1; i >= 0; i--) + { + HashMap *event = ArrayGet(events, i); + PduV1 pdu = { 0 }; char *msg; - PduV1FromJson(event, &pdu, &msg); + if (!PduV1FromJson(event, &pdu, &msg)) + { + continue; + } if (RoomAuthoriseEventV1(room, pdu, R)) { @@ -217,8 +301,10 @@ StateResolveV1(Room * room, Array * states) Free(type); Free(key); } - while (HashMapIterate(conflicts, &type, (void **) &conflicting)) + + while (HashMapIterate(conflicts, &tuple, (void **) &conflicting)) { + size_t i; for (i = 0; i < ArraySize(conflicting); i++) { JsonFree(ArrayGet(conflicting, i)); @@ -230,14 +316,14 @@ StateResolveV1(Room * room, Array * states) return R; } -static HashMap * +static State * StateResolveV2(Array * states) { (void) states; return NULL; } -static HashMap * +static State * StateFromPrevs(Room *room, Array *states) { switch (RoomVersionGet(room)) @@ -249,7 +335,7 @@ StateFromPrevs(Room *room, Array *states) } } -HashMap * +State * StateResolve(Room * room, HashMap * event) { Array *states; @@ -257,7 +343,7 @@ StateResolve(Room * room, HashMap * event) Array *prevEvents; - HashMap *ret_state; + State *ret_state; char *room_id, *event_id; @@ -300,7 +386,7 @@ StateResolve(Room * room, HashMap * event) { HashMap *prevEvent = RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i))); - HashMap *state = StateResolve(room, prevEvent); + State *state = StateResolve(room, prevEvent); if (HashMapGet(prevEvent, "state_key")) { @@ -308,7 +394,8 @@ StateResolve(Room * room, HashMap * event) state, JsonValueAsString(HashMapGet(prevEvent, "type")), JsonValueAsString(HashMapGet(prevEvent, "state_key")), - JsonValueAsString(HashMapGet(prevEvent, "event_id"))); + JsonValueAsString(HashMapGet(prevEvent, "event_id")) + ); } ArrayAdd(states, state); @@ -319,7 +406,7 @@ StateResolve(Room * room, HashMap * event) for (i = 0; i < ArraySize(states); i++) { - HashMap *state = ArrayGet(states, i); + State *state = ArrayGet(states, i); StateFree(state); } ArrayFree(states); @@ -335,13 +422,13 @@ StateResolve(Room * room, HashMap * event) return ret_state; } -HashMap * +State * StateCurrent(Room *room) { Array *prevEvents; Array *states; size_t i; - HashMap *ret; + State *ret; if (!room) { return NULL; @@ -353,8 +440,7 @@ StateCurrent(Room *room) { HashMap *event = JsonValueAsObject(ArrayGet(prevEvents, i)); - HashMap *state = StateResolve(room, event); - + State *state = StateResolve(room, event); if (HashMapGet(event, "state_key")) { @@ -362,7 +448,8 @@ StateCurrent(Room *room) state, JsonValueAsString(HashMapGet(event, "type")), JsonValueAsString(HashMapGet(event, "state_key")), - JsonValueAsString(HashMapGet(event, "event_id"))); + JsonValueAsString(HashMapGet(event, "event_id")) + ); } ArrayAdd(states, state); @@ -372,7 +459,7 @@ StateCurrent(Room *room) for (i = 0; i < ArraySize(states); i++) { - HashMap *state = ArrayGet(states, i); + State *state = ArrayGet(states, i); StateFree(state); } ArrayFree(states); @@ -380,90 +467,79 @@ StateCurrent(Room *room) return ret; } -/* TODO: USING A ',' DELIMITED HASHMAP IS A _BAD_ IDEA! - * I'll need to change these functions, and some things in the room - * implementation, thankfully _that_ was abstracted away, so I don't have - * to think about it too much... */ bool -StateIterate(HashMap *state, char **type, char **key, void **event) +StateIterate(State *state, char **type, char **key, void **event) { - char *tuple; bool ret; + char *tuple; if (!state || !type || !key || !event) { return false; } - - ret = HashMapIterate(state, &tuple, event); + + ret = HashMapIterate(state->table, &tuple, event); if (ret) { - tuple = StrDuplicate(tuple); - *(strchr(tuple, ',')) = '\0'; - - *type = tuple; - *key = StrDuplicate(tuple + strlen(tuple) + 1); + DecodeStateTuple(tuple, type, key); } - return ret; } char * -StateGet(HashMap *state, char *type, char *key) +StateGet(State *state, char *type, char *key) { - char *full_string; + char *tableKey; char *ret; if (!state || !type || !key) { return NULL; } - full_string = StrConcat(3, type, ",", key); - ret = HashMapGet(state, full_string); - Free(full_string); + tableKey = EncodeStateTuple(type, key); + ret = HashMapGet(state->table, tableKey); + Free(tableKey); return ret; } + void -StateSet(HashMap *state, char *type, char *key, char *event) +StateSet(State *state, char *type, char *key, char *event) { - char *full_string, *old; + char *tableKey; if (!state || !type || !key) { return; } - - full_string = StrConcat(3, type, ",", key); - old = HashMapDelete(state, full_string); - if (old) - { - Free(old); - } + tableKey = EncodeStateTuple(type, key); + Free(HashMapDelete(state->table, tableKey)); if (event) { - HashMapSet(state, full_string, StrDuplicate(event)); + HashMapSet(state->table, tableKey, StrDuplicate(event)); } - Free(full_string); + Free(tableKey); } void -StateFree(HashMap *state) +StateFree(State *state) { - char *full; - char *event_id; + char *tuple; + char *eventID; if (!state) { return; } - while (HashMapIterate(state, &full, (void **) &event_id)) + + while (HashMapIterate(state->table, &tuple, (void **) &eventID)) { - Free(event_id); + Free(eventID); } - HashMapFree(state); + HashMapFree(state->table); + Free(state); } -HashMap * +State * StateDeserialise(HashMap *json_state) { - HashMap *raw_state; + State *raw_state; char *state_type; JsonValue *state_keys; @@ -473,7 +549,7 @@ StateDeserialise(HashMap *json_state) return NULL; } - raw_state = HashMapCreate(); + raw_state = InitialiseState(); while (HashMapIterate(json_state, &state_type, (void **) &state_keys)) { @@ -484,18 +560,15 @@ StateDeserialise(HashMap *json_state) while (HashMapIterate(state_keys_obj, &state_key, (void **) &event_id)) { char *eid_string = JsonValueAsString(event_id); - char *key_name = StrConcat(3, state_type, ",", state_key); - HashMapSet(raw_state, key_name, StrDuplicate(eid_string)); - - Free(key_name); + StateSet(raw_state, state_type, state_key, eid_string); } } return raw_state; } HashMap * -StateSerialise(HashMap *rawState) +StateSerialise(State *rawState) { HashMap *returned; char *type, *key, *event; diff --git a/src/include/Room.h b/src/include/Room.h index ec66fc4..e9c93e3 100644 --- a/src/include/Room.h +++ b/src/include/Room.h @@ -51,6 +51,8 @@ */ typedef struct Room Room; +#include + /** * Create a new room in the given database using the given * RoomCreateRequest. @@ -104,14 +106,14 @@ extern int RoomVersionGet(Room *); * which is used to select auth events on incoming * client events. */ -extern HashMap * RoomStateGet(Room *); +extern State * RoomStateGet(Room *); /** * Resolves the room's state before a specific point, * (with the event hashmap taking priority), * like * .Fn RoomStateGet . */ -extern HashMap * RoomStateGetID(Room *, char *); +extern State * RoomStateGetID(Room *, char *); /** * Get a list of the most recent events in the @@ -168,7 +170,7 @@ extern HashMap * RoomEventClientify(HashMap *); * Verifies whenever an event(as a PDUv1) is * authorised by a room. */ -extern bool RoomAuthoriseEventV1(Room *, PduV1, HashMap *); +extern bool RoomAuthoriseEventV1(Room *, PduV1, State *); /** * Gets the room's creator as a ServerPart. This value should diff --git a/src/include/State.h b/src/include/State.h index 54d6529..9a78388 100644 --- a/src/include/State.h +++ b/src/include/State.h @@ -37,17 +37,22 @@ #include +/** + * An opaque structure to hold information about the state. + */ +typedef struct State State; + #include /** * Retrieve the value of a state tuple. */ -extern char *StateGet(HashMap *, char *, char *); +extern char *StateGet(State *, char *, char *); /** * Set a state tuple to a value. */ -extern void StateSet(HashMap *, char *, char *, char *); +extern void StateSet(State *, char *, char *, char *); /** * Iterates through a statemap, with (type, key) -> event. @@ -56,22 +61,22 @@ extern void StateSet(HashMap *, char *, char *, char *); * This function behaves like * .Fn HashMapIterate . */ -extern bool StateIterate(HashMap *, char **, char **, void **); +extern bool StateIterate(State *, char **, char **, void **); /** * Compute the room state before the specified event was sent. */ -extern HashMap * StateResolve(Room *, HashMap *); +extern State * StateResolve(Room *, HashMap *); /** * Computes the current state from the room's leaves. */ -extern HashMap * StateCurrent(Room *); +extern State * StateCurrent(Room *); /** * Frees an entire state table from the heap. */ -extern void StateFree(HashMap *); +extern void StateFree(State *); /** * Deserialises a state map from JSON to the internal format @@ -81,11 +86,11 @@ extern void StateFree(HashMap *); * and should be freed with * .Fn StateFree . */ -extern HashMap * StateDeserialise(HashMap *); +extern State * StateDeserialise(HashMap *); /** * Serialises a state map from the internal format to JSON * used for the database, for example */ -extern HashMap * StateSerialise(HashMap *); +extern HashMap * StateSerialise(State *); #endif /* TELODENDRIA_STATE_H */