forked from Telodendria/Telodendria
[ADD/UNTESTED] Barebones State Resolution V1
Some checks failed
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Has been cancelled
Some checks failed
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Has been cancelled
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Has been cancelled
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:
parent
3066a0e8a8
commit
15b884b04a
4 changed files with 311 additions and 82 deletions
136
src/Room.c
136
src/Room.c
|
@ -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 user’s 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. */
|
||||
|
|
233
src/State.c
233
src/State.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue