[ADD/UNTESTED] Barebones State Resolution V1
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Has been cancelled Details
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Has been cancelled Details

NOTE: It is probably NOT in an usable state. Even if it is, it should
not be PR'd into Telodendria right now.

Feeling a bit burnt out, so I'll take a break. Feel free to issue PRs to
this branch to clean/weed out some mistakes or bugs, since I only made
sure that it built.
This commit is contained in:
lda 2024-05-15 20:15:49 +02:00
parent 3066a0e8a8
commit 15b884b04a
4 changed files with 311 additions and 82 deletions

View File

@ -174,7 +174,7 @@ RoomStateGet(Room * room)
database_state = DbJson(room->ref);
return StateDeserialise(database_state);
}
static HashMap *
HashMap *
RoomStateGetID(Room * room, char *event_id)
{
HashMap *event, *state;
@ -195,11 +195,10 @@ RoomStateGetID(Room * room, char *event_id)
return state;
}
#define PrepareState(room, e, type, key, n) \
#define PrepareState(room, S, type, key, n) \
do \
{ \
state_point = RoomStateGetID(room, e); \
id_##n = StateGet(state_point, type, key); \
id_##n = StateGet(S, type, key); \
if (id_##n) \
{ \
goto finish; \
@ -212,29 +211,24 @@ RoomStateGetID(Room * room, char *event_id)
} \
while (0)
#define FinishState(name) \
if (state_point) \
{ \
StateFree(state_point); \
} \
if (name) \
{ \
JsonFree(name); \
} \
return ret
static bool
RoomIsJoinRule(Room * room, char *e_id, char *jr)
RoomIsJoinRule(Room * room, HashMap *state, char *jr)
{
HashMap *state_point;
HashMap *joinrule = NULL;
char *id_joinrule = NULL;
bool ret = false;
if (!room || !e_id || !jr)
if (!room || !state || !jr)
{
return false;
}
PrepareState(room, e_id, "m.room.join_rules", "", joinrule);
PrepareState(room, state, "m.room.join_rules", "", joinrule);
ret = StrEquals(
JsonValueAsString(JsonGet(joinrule, 1, "join_rule")),
jr);
@ -243,19 +237,18 @@ finish:
}
/* Verifies if user has a specific membership before [e_id] in the room. */
static bool
RoomUserHasMembership(Room * room, char *e_id, char *user, char *mbr)
RoomUserHasMembership(Room * room, HashMap *state, char *user, char *mbr)
{
HashMap *state_point;
HashMap *membership = NULL;
char *id_membership = NULL;
char *membership_value;
bool ret = false;
if (!room || !e_id || !user || !mbr)
if (!room || !state || !user || !mbr)
{
return false;
}
PrepareState(room, e_id, "m.room.member", user, membership);
PrepareState(room, state, "m.room.member", user, membership);
membership_value =
JsonValueAsString(JsonGet(membership, 2, "content", "membership"));
@ -292,18 +285,18 @@ ParsePL(JsonValue *v, int64_t def)
}
/* Computes the smallest PL needed to do something somewhere */
static int64_t
RoomMinPL(Room * room, char *e_id, char *type, char *act)
RoomMinPL(Room * room, HashMap *state, char *type, char *act)
{
HashMap *state_point, *pl = NULL;
HashMap *pl = NULL;
JsonValue *val;
char *id_pl;
int64_t ret, def;
if (!room || !e_id || !act)
if (!room || !state || !act)
{
return 0;
}
PrepareState(room, e_id, "m.room.power_levels", "", pl);
PrepareState(room, state, "m.room.power_levels", "", pl);
/* Every other act has a minimum PL of 0 */
def = 0;
@ -331,20 +324,20 @@ finish:
}
/* Finds the power level of an user before [e_id] was sent. */
static int64_t
RoomUserPL(Room * room, char *e_id, char *user)
RoomUserPL(Room * room, HashMap *state, char *user)
{
HashMap *state_point, *pl = NULL;
HashMap *pl = NULL;
char *id_pl;
int64_t ret, def;
if (!room || !e_id || !user)
if (!room || !state || !user)
{
return 0;
}
PrepareState(room, e_id, "m.room.power_levels", "", pl);
PrepareState(room, state, "m.room.power_levels", "", pl);
def = RoomMinPL(room, e_id, NULL, "users_default");
def = RoomMinPL(room, state, NULL, "users_default");
ret = ParsePL(JsonGet(pl, 2, "users", user), def);
finish:
@ -598,7 +591,7 @@ AuthoriseAliasV1(PduV1 pdu)
return true;
}
static bool
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state)
{
int64_t invite_level;
int64_t pdu_level;
@ -608,10 +601,10 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
{
JsonValue *signed_val, *mxid, *token;
HashMap *third_pi_obj = JsonValueAsObject(third_pi), *signed_obj;
HashMap *state, *third_pi_event;
HashMap *third_pi_event;
char *third_pi_id, *thirdpi_event_sender;
/* Step 5.3.1.1: If target user is banned, reject. */
if (RoomUserHasMembership(room, pdu.event_id, pdu.state_key, "ban"))
if (RoomUserHasMembership(room, state, pdu.state_key, "ban"))
{
return false;
}
@ -642,16 +635,13 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
/* Step 5.3.1.5: If there is no m.room.third_party_invite event
* in the current room state with state_key matching token, reject. */
state = RoomStateGetID(room, pdu.event_id);
if (!(third_pi_id = StateGet(
state,
"m.room.third_party_invite", JsonValueAsString(token))))
{
StateFree(state);
return false;
}
third_pi_event = RoomEventFetch(room, third_pi_id);
StateFree(state);
/* Step 5.3.1.6: If sender does not match sender of the
* m.room.third_party_invite, reject. */
@ -675,21 +665,21 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
/* Step 5.3.2: If the sender's current membership state is not join,
* reject. */
if (!RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join"))
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
{
return false;
}
/* Step 5.3.3: If target users current membership state is join or ban, reject. */
if (RoomUserHasMembership(room, pdu.event_id, pdu.state_key, "join") ||
RoomUserHasMembership(room, pdu.event_id, pdu.state_key, "join"))
if (RoomUserHasMembership(room, state, pdu.state_key, "join") ||
RoomUserHasMembership(room, state, pdu.state_key, "join"))
{
return false;
}
/* Step 5.3.4: If the sender's power level is greater than or equal to the
* invite level, allow. */
invite_level = RoomMinPL(room, pdu.event_id, NULL, "invite");
pdu_level = RoomUserPL(room, pdu.event_id, pdu.sender);
invite_level = RoomMinPL(room, state, NULL, "invite");
pdu_level = RoomUserPL(room, state, pdu.sender);
if (pdu_level >= invite_level)
{
return true;
@ -698,31 +688,31 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
return false;
}
static bool
AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu)
AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, HashMap *state)
{
int64_t ban_level = RoomMinPL(room, pdu.event_id, NULL, "ban");
int64_t kick_level = RoomMinPL(room, pdu.event_id, NULL, "kick");
int64_t sender_level = RoomUserPL(room, pdu.event_id, pdu.sender);
int64_t target_level = RoomUserPL(room, pdu.state_key, pdu.sender);
int64_t ban_level = RoomMinPL(room, state, NULL, "ban");
int64_t kick_level = RoomMinPL(room, state, NULL, "kick");
int64_t sender_level = RoomUserPL(room, state, pdu.sender);
int64_t target_level = RoomUserPL(room, state, pdu.sender);
/* Step 5.4.1: If the sender matches state_key, allow if and only if
* that user's current membership state is invite or join. */
if (StrEquals(pdu.sender, pdu.state_key))
{
return
RoomUserHasMembership(room, pdu.event_id, pdu.sender, "invite") ||
RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join");
RoomUserHasMembership(room, state, pdu.sender, "invite") ||
RoomUserHasMembership(room, state, pdu.sender, "join");
}
/* Step 5.4.2: If the sender's current membership state is not join,
* reject. */
if (!RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join"))
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
{
return false;
}
/* Step 5.4.3: If the target user's current membership state is ban,
* and the sender's power level is less than the ban level, reject. */
if (RoomUserHasMembership(room, pdu.event_id, pdu.state_key, "ban") &&
if (RoomUserHasMembership(room, state, pdu.state_key, "ban") &&
sender_level < ban_level)
{
return false;
@ -740,12 +730,12 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu)
return false;
}
static bool
AuthorizeBanMembershipV1(Room * room, PduV1 pdu)
AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state)
{
int64_t ban_pl, pdu_pl, target_pl;
/* Step 5.5.1: If the sender's current membership state is not join, reject. */
if (!RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join"))
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
{
return false;
}
@ -753,9 +743,9 @@ AuthorizeBanMembershipV1(Room * room, PduV1 pdu)
/* Step 5.5.2: If the sender's power level is greater than or equal
* to the ban level, and the target user's power level is less than
* the sender's power level, allow. */
ban_pl = RoomMinPL(room, pdu.event_id, NULL, "ban");
pdu_pl = RoomUserPL(room, pdu.event_id, pdu.sender);
target_pl = RoomUserPL(room, pdu.state_key, pdu.sender);
ban_pl = RoomMinPL(room, state, NULL, "ban");
pdu_pl = RoomUserPL(room, state, pdu.sender);
target_pl = RoomUserPL(room, state, pdu.sender);
if ((pdu_pl >= ban_pl) && (target_pl < pdu_pl))
{
return true;
@ -765,7 +755,7 @@ AuthorizeBanMembershipV1(Room * room, PduV1 pdu)
return false;
}
static bool
AuthorizeJoinMembershipV1(Room * room, PduV1 pdu)
AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, HashMap *state)
{
/* Step 5.2.1: If the only previous event is an m.room.create and the
* state_key is the creator, allow. */
@ -797,21 +787,21 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu)
return false;
}
/* Step 5.2.3: If the sender is banned, reject. */
if (RoomUserHasMembership(room, pdu.event_id, pdu.sender, "ban"))
if (RoomUserHasMembership(room, state, pdu.sender, "ban"))
{
return false;
}
/* Step 5.2.4: If the join_rule is invite then allow if membership
* state is invite or join. */
if (RoomIsJoinRule(room, pdu.event_id, "invite") &&
(RoomUserHasMembership(room, pdu.event_id, pdu.sender, "invite") ||
RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join")))
if (RoomIsJoinRule(room, state, "invite") &&
(RoomUserHasMembership(room, state, pdu.sender, "invite") ||
RoomUserHasMembership(room, state, pdu.sender, "join")))
{
return true;
}
/* Step 5.2.5: If the join_rule is public, allow. */
if (RoomIsJoinRule(room, pdu.event_id, "public"))
if (RoomIsJoinRule(room, state, "public"))
{
return true;
}
@ -819,7 +809,7 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu)
return false;
}
static bool
AuthoriseMemberV1(Room * room, PduV1 pdu)
AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state)
{
JsonValue *membership;
char *membership_str;
@ -840,7 +830,7 @@ AuthoriseMemberV1(Room * room, PduV1 pdu)
#define JumpIfMembership(mem, func) do { \
if (StrEquals(membership_str, mem)) \
{ \
return func(room, pdu); \
return func(room, pdu, state); \
} \
} while (0)
@ -861,19 +851,19 @@ AuthoriseMemberV1(Room * room, PduV1 pdu)
#undef JumpIfMembership
}
static bool
AuthorisePowerLevelsV1(Room * room, PduV1 pdu)
AuthorisePowerLevelsV1(Room * room, PduV1 pdu, HashMap *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
* (or a string that is an integer), reject. */
JsonValue *users = JsonGet(pdu.content, 1, "users");
HashMap *users_o, *state, *prev_plevent;
HashMap *users_o, *prev_plevent;
char *user_id, *prev_pl_id, *ev_type;
JsonValue *power_level, *ev_obj;
bool flag = true;
int64_t userpl = RoomUserPL(room, pdu.event_id, pdu.sender);
int64_t userpl = RoomUserPL(room, state, pdu.sender);
HashMap *event_obj;
if (JsonValueType(users) != JSON_OBJECT)
{
@ -915,10 +905,8 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu)
/* Step 10.2: If there is no previous m.room.power_levels event
* in the room, allow. */
state = RoomStateGetID(room, pdu.event_id);
if (!(prev_pl_id = StateGet(state, "m.room.power_levels", "")))
{
StateFree(state);
return true;
}
@ -1033,17 +1021,16 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu)
#undef CheckPLNew
/* Step 10.8: Otherwise, allow. */
JsonFree(prev_plevent);
StateFree(state);
return true;
}
bool
AuthoriseEventV1(Room * room, PduV1 pdu)
RoomAuthoriseEventV1(Room * room, PduV1 pdu, HashMap *state)
{
HashMap *state, *create_event;
HashMap *create_event;
char *create_event_id;
JsonValue *federate;
int64_t pdu_pl = RoomUserPL(room, pdu.event_id, pdu.sender);
int64_t event_pl = RoomMinPL(room, pdu.event_id, "events", pdu.type);
int64_t pdu_pl = RoomUserPL(room, state, pdu.sender);
int64_t event_pl = RoomMinPL(room,state, "events", pdu.type);
/* Step 1: If m.room.create */
if (StrEquals(pdu.type, "m.room.create"))
{
@ -1061,13 +1048,10 @@ AuthoriseEventV1(Room * room, PduV1 pdu)
* the event does not match the sender domain of the create event,
* reject.
*/
state = RoomStateGet(room);
create_event_id = StateGet(state, "m.room.create", "");
if (!state || !create_event_id)
{
/* At this point, [create_event_id] has to exist */
StateFree(state); /* create_event_id is also freed. */
return false;
}
create_event = RoomEventFetch(room, create_event_id);
@ -1095,10 +1079,10 @@ AuthoriseEventV1(Room * room, PduV1 pdu)
/* Step 5: If type is m.room.member */
if (StrEquals(pdu.type, "m.room.member"))
{
return AuthoriseMemberV1(room, pdu);
return AuthoriseMemberV1(room, pdu, state);
}
/* Step 6: If the sender's current membership state is not join, reject. */
if (!RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join"))
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
{
return false;
}
@ -1107,7 +1091,7 @@ AuthoriseEventV1(Room * room, PduV1 pdu)
{
/* Allow if and only if sender's current power level is greater than
* or equal to the invite level */
int64_t min_pl = RoomMinPL(room, pdu.event_id, NULL, "invite");
int64_t min_pl = RoomMinPL(room, state, NULL, "invite");
return pdu_pl >= min_pl;
}
@ -1132,13 +1116,13 @@ AuthoriseEventV1(Room * room, PduV1 pdu)
/* Step 10: If type is m.room.power_levels */
if (StrEquals(pdu.type, "m.room.power_levels"))
{
return AuthorisePowerLevelsV1(room, pdu);
return AuthorisePowerLevelsV1(room, pdu, state);
}
/* Step 11: If type is m.room.redaction */
if (StrEquals(pdu.type, "m.room.redaction"))
{
int64_t min_pl = RoomMinPL(room, pdu.event_id, NULL, "redact");
int64_t min_pl = RoomMinPL(room, state, NULL, "redact");
/* Step 11.1: If the sender's power level is greater than or equal
* to the redact level, allow. */

View File

@ -29,15 +29,199 @@
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Array.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Sha.h>
#include <string.h>
#include <Event.h>
#include <Room.h>
static HashMap *
StateResolveV1(Array * states)
int
V1Cmp(void *a, void *b)
{
(void) states;
return NULL;
HashMap *e1 = a, *e2 = b;
int64_t depth1 =
JsonValueAsInteger(JsonGet(e1, 1, "depth"));
int64_t depth2 =
JsonValueAsInteger(JsonGet(e2, 1, "depth"));
if (depth1 > depth2)
{
return 1;
}
else if (depth1 < depth2)
{
return -1;
}
else
{
char *e1id =
JsonValueAsString(JsonGet(e1, 1, "event_id"));
char *e2id =
JsonValueAsString(JsonGet(e2, 1, "event_id"));
unsigned char *sha1 = Sha1(e1id);
unsigned char *sha2 = Sha1(e2id);
char *str1 = ShaToHex(sha1);
char *str2 = ShaToHex(sha2);
int ret = strcmp(str1, str2) * -1;
Free(str1);
Free(str2);
Free(sha1);
Free(sha2);
/* Descending */
return ret;
}
}
static HashMap *
StateResolveV1(Room * room, Array * states)
{
HashMap *R = HashMapCreate();
HashMap *conflicts = HashMapCreate();
Array *events, *types, *conflicting;
size_t i;
ssize_t j;
char *type, *key, *event_id;
for (i = 0; i < ArraySize(states); i++)
{
HashMap *state = ArrayGet(states, i);
char *tuple;
while (HashMapIterate(state, &tuple, (void **) &event_id))
{
if (HashMapGet(R, tuple))
{
Array *arr;
/* Conflicts! */
HashMapDelete(R, tuple);
arr = HashMapGet(conflicts, tuple);
if (!arr)
{
arr = ArrayCreate();
}
ArrayAdd(arr, RoomEventFetch(room, event_id));
HashMapSet(conflicts, tuple, arr);
}
else
{
/* Add to R */
HashMapSet(R, tuple, 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);
}
}
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")));
for (j = 0; j < (ssize_t) ArraySize(events); j++)
{
HashMap *event = ArrayGet(events, j);
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);
break;
}
(void) msg;
PduV1Free(&pdu);
}
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);
}
Free(state_keys);
}
ArrayFree(types);
while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
{
ArraySort(conflicting, V1Cmp);
for (j = ArraySize(conflicting) - 1; j >= 0; j--)
{
HashMap *event = ArrayGet(events, j);
PduV1 pdu;
char *msg;
PduV1FromJson(event, &pdu, &msg);
if (RoomAuthoriseEventV1(room, pdu, R))
{
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
PduV1Free(&pdu);
break;
}
(void) msg;
PduV1Free(&pdu);
}
Free(type);
Free(key);
}
while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
{
for (i = 0; i < ArraySize(conflicting); i++)
{
JsonFree(ArrayGet(conflicting, i));
}
ArrayFree(conflicting);
}
HashMapFree(conflicts);
return R;
}
static HashMap *
@ -83,11 +267,29 @@ StateResolve(Room * room, HashMap * event)
switch (RoomVersionGet(room))
{
case 1:
return StateResolveV1(states);
return StateResolveV1(room, states);
default:
return StateResolveV2(states);
}
}
bool StateIterate(HashMap *state, char **type, char **key, void **event)
{
char *tuple;
bool ret;
if (!state || !type || !key || !event)
{
return false;
}
ret = HashMapIterate(state, &tuple, event);
tuple = StrDuplicate(tuple);
*(strchr(tuple, ',')) = '\0';
*type = tuple;
*key = StrDuplicate(tuple + strlen(tuple) + 1);
return ret;
}
char *
StateGet(HashMap *state, char *type, char *key)
{
@ -104,6 +306,27 @@ StateGet(HashMap *state, char *type, char *key)
return ret;
}
void
StateSet(HashMap *state, char *type, char *key, char *event)
{
char *full_string, *old;
if (!state || !type || !key)
{
return;
}
full_string = StrConcat(3, type, ",", key);
old = HashMapDelete(state, full_string);
if (old)
{
Free(old);
}
if (event)
{
HashMapSet(state, full_string, StrDuplicate(event));
}
Free(full_string);
}
void
StateFree(HashMap *state)
{

View File

@ -41,6 +41,7 @@
#include <Cytoplasm/Db.h>
#include <Schema/RoomCreateRequest.h>
#include <Schema/PduV1.h>
#include <User.h>
@ -98,6 +99,12 @@ extern int RoomVersionGet(Room *);
* client events.
*/
extern HashMap * RoomStateGet(Room *);
/**
* Resolves the room's state before a specific point,
* like
* .Fn RoomStateGet .
*/
extern HashMap * RoomStateGetID(Room *, char *);
/**
* Get a list of the most recent events in the
@ -138,4 +145,10 @@ extern HashMap * RoomEventSend(Room *, HashMap *);
*/
extern HashMap * RoomEventFetch(Room *, char *);
/**
* Verifies whenever an event(as a PDUv1) is
* authorised by a room.
*/
extern bool RoomAuthoriseEventV1(Room *, PduV1, HashMap *);
#endif /* TELODENDRIA_ROOM_H */

View File

@ -47,7 +47,16 @@ extern char *StateGet(HashMap *, char *, char *);
/**
* Set a state tuple to a value.
*/
extern char *StateSet(HashMap *, char *, char *, char *);
extern void StateSet(HashMap *, char *, char *, char *);
/**
* Iterates through a statemap, with (type, key) -> event.
* The type and keys are stored on the heap, and will need
* to be freed.
* This function behaves like
* .Fn HashMapIterate .
*/
extern bool StateIterate(HashMap *, char **, char **, void **);
/**
* Compute the room state before the specified event was sent.