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);
|
database_state = DbJson(room->ref);
|
||||||
return StateDeserialise(database_state);
|
return StateDeserialise(database_state);
|
||||||
}
|
}
|
||||||
static HashMap *
|
HashMap *
|
||||||
RoomStateGetID(Room * room, char *event_id)
|
RoomStateGetID(Room * room, char *event_id)
|
||||||
{
|
{
|
||||||
HashMap *event, *state;
|
HashMap *event, *state;
|
||||||
|
@ -195,11 +195,10 @@ RoomStateGetID(Room * room, char *event_id)
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PrepareState(room, e, type, key, n) \
|
#define PrepareState(room, S, type, key, n) \
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
state_point = RoomStateGetID(room, e); \
|
id_##n = StateGet(S, type, key); \
|
||||||
id_##n = StateGet(state_point, type, key); \
|
|
||||||
if (id_##n) \
|
if (id_##n) \
|
||||||
{ \
|
{ \
|
||||||
goto finish; \
|
goto finish; \
|
||||||
|
@ -212,29 +211,24 @@ RoomStateGetID(Room * room, char *event_id)
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
#define FinishState(name) \
|
#define FinishState(name) \
|
||||||
if (state_point) \
|
|
||||||
{ \
|
|
||||||
StateFree(state_point); \
|
|
||||||
} \
|
|
||||||
if (name) \
|
if (name) \
|
||||||
{ \
|
{ \
|
||||||
JsonFree(name); \
|
JsonFree(name); \
|
||||||
} \
|
} \
|
||||||
return ret
|
return ret
|
||||||
static bool
|
static bool
|
||||||
RoomIsJoinRule(Room * room, char *e_id, char *jr)
|
RoomIsJoinRule(Room * room, HashMap *state, char *jr)
|
||||||
{
|
{
|
||||||
HashMap *state_point;
|
|
||||||
HashMap *joinrule = NULL;
|
HashMap *joinrule = NULL;
|
||||||
char *id_joinrule = NULL;
|
char *id_joinrule = NULL;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
if (!room || !e_id || !jr)
|
if (!room || !state || !jr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrepareState(room, e_id, "m.room.join_rules", "", joinrule);
|
PrepareState(room, state, "m.room.join_rules", "", joinrule);
|
||||||
ret = StrEquals(
|
ret = StrEquals(
|
||||||
JsonValueAsString(JsonGet(joinrule, 1, "join_rule")),
|
JsonValueAsString(JsonGet(joinrule, 1, "join_rule")),
|
||||||
jr);
|
jr);
|
||||||
|
@ -243,19 +237,18 @@ finish:
|
||||||
}
|
}
|
||||||
/* Verifies if user has a specific membership before [e_id] in the room. */
|
/* Verifies if user has a specific membership before [e_id] in the room. */
|
||||||
static bool
|
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;
|
HashMap *membership = NULL;
|
||||||
char *id_membership = NULL;
|
char *id_membership = NULL;
|
||||||
char *membership_value;
|
char *membership_value;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (!room || !e_id || !user || !mbr)
|
if (!room || !state || !user || !mbr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrepareState(room, e_id, "m.room.member", user, membership);
|
PrepareState(room, state, "m.room.member", user, membership);
|
||||||
|
|
||||||
membership_value =
|
membership_value =
|
||||||
JsonValueAsString(JsonGet(membership, 2, "content", "membership"));
|
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 */
|
/* Computes the smallest PL needed to do something somewhere */
|
||||||
static int64_t
|
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;
|
JsonValue *val;
|
||||||
char *id_pl;
|
char *id_pl;
|
||||||
int64_t ret, def;
|
int64_t ret, def;
|
||||||
if (!room || !e_id || !act)
|
if (!room || !state || !act)
|
||||||
{
|
{
|
||||||
return 0;
|
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 */
|
/* Every other act has a minimum PL of 0 */
|
||||||
def = 0;
|
def = 0;
|
||||||
|
@ -331,20 +324,20 @@ finish:
|
||||||
}
|
}
|
||||||
/* Finds the power level of an user before [e_id] was sent. */
|
/* Finds the power level of an user before [e_id] was sent. */
|
||||||
static int64_t
|
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;
|
char *id_pl;
|
||||||
int64_t ret, def;
|
int64_t ret, def;
|
||||||
|
|
||||||
if (!room || !e_id || !user)
|
if (!room || !state || !user)
|
||||||
{
|
{
|
||||||
return 0;
|
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);
|
ret = ParsePL(JsonGet(pl, 2, "users", user), def);
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
|
@ -598,7 +591,7 @@ AuthoriseAliasV1(PduV1 pdu)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
|
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
{
|
{
|
||||||
int64_t invite_level;
|
int64_t invite_level;
|
||||||
int64_t pdu_level;
|
int64_t pdu_level;
|
||||||
|
@ -608,10 +601,10 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
|
||||||
{
|
{
|
||||||
JsonValue *signed_val, *mxid, *token;
|
JsonValue *signed_val, *mxid, *token;
|
||||||
HashMap *third_pi_obj = JsonValueAsObject(third_pi), *signed_obj;
|
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;
|
char *third_pi_id, *thirdpi_event_sender;
|
||||||
/* Step 5.3.1.1: If target user is banned, reject. */
|
/* 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;
|
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
|
/* 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. */
|
* in the current room state with state_key matching token, reject. */
|
||||||
state = RoomStateGetID(room, pdu.event_id);
|
|
||||||
if (!(third_pi_id = StateGet(
|
if (!(third_pi_id = StateGet(
|
||||||
state,
|
state,
|
||||||
"m.room.third_party_invite", JsonValueAsString(token))))
|
"m.room.third_party_invite", JsonValueAsString(token))))
|
||||||
{
|
{
|
||||||
StateFree(state);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
third_pi_event = RoomEventFetch(room, third_pi_id);
|
third_pi_event = RoomEventFetch(room, third_pi_id);
|
||||||
StateFree(state);
|
|
||||||
|
|
||||||
/* Step 5.3.1.6: If sender does not match sender of the
|
/* Step 5.3.1.6: If sender does not match sender of the
|
||||||
* m.room.third_party_invite, reject. */
|
* 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,
|
/* Step 5.3.2: If the sender's current membership state is not join,
|
||||||
* reject. */
|
* reject. */
|
||||||
if (!RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join"))
|
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* Step 5.3.3: If target user’s current membership state is join or ban, reject. */
|
/* 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") ||
|
if (RoomUserHasMembership(room, state, pdu.state_key, "join") ||
|
||||||
RoomUserHasMembership(room, pdu.event_id, pdu.state_key, "join"))
|
RoomUserHasMembership(room, state, pdu.state_key, "join"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.3.4: If the sender's power level is greater than or equal to the
|
/* Step 5.3.4: If the sender's power level is greater than or equal to the
|
||||||
* invite level, allow. */
|
* invite level, allow. */
|
||||||
invite_level = RoomMinPL(room, pdu.event_id, NULL, "invite");
|
invite_level = RoomMinPL(room, state, NULL, "invite");
|
||||||
pdu_level = RoomUserPL(room, pdu.event_id, pdu.sender);
|
pdu_level = RoomUserPL(room, state, pdu.sender);
|
||||||
if (pdu_level >= invite_level)
|
if (pdu_level >= invite_level)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -698,31 +688,31 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
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 ban_level = RoomMinPL(room, state, NULL, "ban");
|
||||||
int64_t kick_level = RoomMinPL(room, pdu.event_id, NULL, "kick");
|
int64_t kick_level = RoomMinPL(room, state, NULL, "kick");
|
||||||
int64_t sender_level = RoomUserPL(room, pdu.event_id, pdu.sender);
|
int64_t sender_level = RoomUserPL(room, state, pdu.sender);
|
||||||
int64_t target_level = RoomUserPL(room, pdu.state_key, 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
|
/* 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. */
|
* that user's current membership state is invite or join. */
|
||||||
if (StrEquals(pdu.sender, pdu.state_key))
|
if (StrEquals(pdu.sender, pdu.state_key))
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
RoomUserHasMembership(room, pdu.event_id, pdu.sender, "invite") ||
|
RoomUserHasMembership(room, state, pdu.sender, "invite") ||
|
||||||
RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join");
|
RoomUserHasMembership(room, state, pdu.sender, "join");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.4.2: If the sender's current membership state is not join,
|
/* Step 5.4.2: If the sender's current membership state is not join,
|
||||||
* reject. */
|
* reject. */
|
||||||
if (!RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join"))
|
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.4.3: If the target user's current membership state is ban,
|
/* 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. */
|
* 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)
|
sender_level < ban_level)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -740,12 +730,12 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeBanMembershipV1(Room * room, PduV1 pdu)
|
AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
{
|
{
|
||||||
int64_t ban_pl, pdu_pl, target_pl;
|
int64_t ban_pl, pdu_pl, target_pl;
|
||||||
|
|
||||||
/* Step 5.5.1: If the sender's current membership state is not join, reject. */
|
/* 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;
|
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
|
/* 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
|
* to the ban level, and the target user's power level is less than
|
||||||
* the sender's power level, allow. */
|
* the sender's power level, allow. */
|
||||||
ban_pl = RoomMinPL(room, pdu.event_id, NULL, "ban");
|
ban_pl = RoomMinPL(room, state, NULL, "ban");
|
||||||
pdu_pl = RoomUserPL(room, pdu.event_id, pdu.sender);
|
pdu_pl = RoomUserPL(room, state, pdu.sender);
|
||||||
target_pl = RoomUserPL(room, pdu.state_key, pdu.sender);
|
target_pl = RoomUserPL(room, state, pdu.sender);
|
||||||
if ((pdu_pl >= ban_pl) && (target_pl < pdu_pl))
|
if ((pdu_pl >= ban_pl) && (target_pl < pdu_pl))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -765,7 +755,7 @@ AuthorizeBanMembershipV1(Room * room, PduV1 pdu)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
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
|
/* Step 5.2.1: If the only previous event is an m.room.create and the
|
||||||
* state_key is the creator, allow. */
|
* state_key is the creator, allow. */
|
||||||
|
@ -797,21 +787,21 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* Step 5.2.3: If the sender is banned, reject. */
|
/* 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.2.4: If the join_rule is invite then allow if membership
|
/* Step 5.2.4: If the join_rule is invite then allow if membership
|
||||||
* state is invite or join. */
|
* state is invite or join. */
|
||||||
if (RoomIsJoinRule(room, pdu.event_id, "invite") &&
|
if (RoomIsJoinRule(room, state, "invite") &&
|
||||||
(RoomUserHasMembership(room, pdu.event_id, pdu.sender, "invite") ||
|
(RoomUserHasMembership(room, state, pdu.sender, "invite") ||
|
||||||
RoomUserHasMembership(room, pdu.event_id, pdu.sender, "join")))
|
RoomUserHasMembership(room, state, pdu.sender, "join")))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* Step 5.2.5: If the join_rule is public, allow. */
|
/* 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -819,7 +809,7 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthoriseMemberV1(Room * room, PduV1 pdu)
|
AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
{
|
{
|
||||||
JsonValue *membership;
|
JsonValue *membership;
|
||||||
char *membership_str;
|
char *membership_str;
|
||||||
|
@ -840,7 +830,7 @@ AuthoriseMemberV1(Room * room, PduV1 pdu)
|
||||||
#define JumpIfMembership(mem, func) do { \
|
#define JumpIfMembership(mem, func) do { \
|
||||||
if (StrEquals(membership_str, mem)) \
|
if (StrEquals(membership_str, mem)) \
|
||||||
{ \
|
{ \
|
||||||
return func(room, pdu); \
|
return func(room, pdu, state); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -861,19 +851,19 @@ AuthoriseMemberV1(Room * room, PduV1 pdu)
|
||||||
#undef JumpIfMembership
|
#undef JumpIfMembership
|
||||||
}
|
}
|
||||||
static bool
|
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
|
/* 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
|
* keys that are valid user IDs with values that are integers
|
||||||
* (or a string that is an integer), reject. */
|
* (or a string that is an integer), reject. */
|
||||||
JsonValue *users = JsonGet(pdu.content, 1, "users");
|
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;
|
char *user_id, *prev_pl_id, *ev_type;
|
||||||
JsonValue *power_level, *ev_obj;
|
JsonValue *power_level, *ev_obj;
|
||||||
|
|
||||||
bool flag = true;
|
bool flag = true;
|
||||||
int64_t userpl = RoomUserPL(room, pdu.event_id, pdu.sender);
|
int64_t userpl = RoomUserPL(room, state, pdu.sender);
|
||||||
HashMap *event_obj;
|
HashMap *event_obj;
|
||||||
if (JsonValueType(users) != JSON_OBJECT)
|
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
|
/* Step 10.2: If there is no previous m.room.power_levels event
|
||||||
* in the room, allow. */
|
* in the room, allow. */
|
||||||
state = RoomStateGetID(room, pdu.event_id);
|
|
||||||
if (!(prev_pl_id = StateGet(state, "m.room.power_levels", "")))
|
if (!(prev_pl_id = StateGet(state, "m.room.power_levels", "")))
|
||||||
{
|
{
|
||||||
StateFree(state);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,17 +1021,16 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu)
|
||||||
#undef CheckPLNew
|
#undef CheckPLNew
|
||||||
/* Step 10.8: Otherwise, allow. */
|
/* Step 10.8: Otherwise, allow. */
|
||||||
JsonFree(prev_plevent);
|
JsonFree(prev_plevent);
|
||||||
StateFree(state);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
AuthoriseEventV1(Room * room, PduV1 pdu)
|
RoomAuthoriseEventV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
{
|
{
|
||||||
HashMap *state, *create_event;
|
HashMap *create_event;
|
||||||
char *create_event_id;
|
char *create_event_id;
|
||||||
JsonValue *federate;
|
JsonValue *federate;
|
||||||
int64_t pdu_pl = RoomUserPL(room, pdu.event_id, pdu.sender);
|
int64_t pdu_pl = RoomUserPL(room, state, pdu.sender);
|
||||||
int64_t event_pl = RoomMinPL(room, pdu.event_id, "events", pdu.type);
|
int64_t event_pl = RoomMinPL(room,state, "events", pdu.type);
|
||||||
/* Step 1: If m.room.create */
|
/* Step 1: If m.room.create */
|
||||||
if (StrEquals(pdu.type, "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,
|
* the event does not match the sender domain of the create event,
|
||||||
* reject.
|
* reject.
|
||||||
*/
|
*/
|
||||||
state = RoomStateGet(room);
|
|
||||||
create_event_id = StateGet(state, "m.room.create", "");
|
create_event_id = StateGet(state, "m.room.create", "");
|
||||||
if (!state || !create_event_id)
|
if (!state || !create_event_id)
|
||||||
{
|
{
|
||||||
/* At this point, [create_event_id] has to exist */
|
/* At this point, [create_event_id] has to exist */
|
||||||
StateFree(state); /* create_event_id is also freed. */
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
create_event = RoomEventFetch(room, create_event_id);
|
create_event = RoomEventFetch(room, create_event_id);
|
||||||
|
@ -1095,10 +1079,10 @@ AuthoriseEventV1(Room * room, PduV1 pdu)
|
||||||
/* Step 5: If type is m.room.member */
|
/* Step 5: If type is m.room.member */
|
||||||
if (StrEquals(pdu.type, "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. */
|
/* 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1107,7 +1091,7 @@ AuthoriseEventV1(Room * room, PduV1 pdu)
|
||||||
{
|
{
|
||||||
/* Allow if and only if sender's current power level is greater than
|
/* Allow if and only if sender's current power level is greater than
|
||||||
* or equal to the invite level */
|
* 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;
|
return pdu_pl >= min_pl;
|
||||||
}
|
}
|
||||||
|
@ -1132,13 +1116,13 @@ AuthoriseEventV1(Room * room, PduV1 pdu)
|
||||||
/* Step 10: If type is m.room.power_levels */
|
/* Step 10: If type is m.room.power_levels */
|
||||||
if (StrEquals(pdu.type, "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 */
|
/* Step 11: If type is m.room.redaction */
|
||||||
if (StrEquals(pdu.type, "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
|
/* Step 11.1: If the sender's power level is greater than or equal
|
||||||
* to the redact level, allow. */
|
* to the redact level, allow. */
|
||||||
|
|
233
src/State.c
233
src/State.c
|
@ -29,15 +29,199 @@
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Array.h>
|
#include <Cytoplasm/Array.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <Event.h>
|
#include <Event.h>
|
||||||
#include <Room.h>
|
#include <Room.h>
|
||||||
|
|
||||||
static HashMap *
|
int
|
||||||
StateResolveV1(Array * states)
|
V1Cmp(void *a, void *b)
|
||||||
{
|
{
|
||||||
(void) states;
|
HashMap *e1 = a, *e2 = b;
|
||||||
return NULL;
|
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 *
|
static HashMap *
|
||||||
|
@ -83,11 +267,29 @@ StateResolve(Room * room, HashMap * event)
|
||||||
switch (RoomVersionGet(room))
|
switch (RoomVersionGet(room))
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
return StateResolveV1(states);
|
return StateResolveV1(room, states);
|
||||||
default:
|
default:
|
||||||
return StateResolveV2(states);
|
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 *
|
char *
|
||||||
StateGet(HashMap *state, char *type, char *key)
|
StateGet(HashMap *state, char *type, char *key)
|
||||||
{
|
{
|
||||||
|
@ -104,6 +306,27 @@ StateGet(HashMap *state, char *type, char *key)
|
||||||
|
|
||||||
return ret;
|
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
|
void
|
||||||
StateFree(HashMap *state)
|
StateFree(HashMap *state)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
|
|
||||||
#include <Schema/RoomCreateRequest.h>
|
#include <Schema/RoomCreateRequest.h>
|
||||||
|
#include <Schema/PduV1.h>
|
||||||
|
|
||||||
#include <User.h>
|
#include <User.h>
|
||||||
|
|
||||||
|
@ -98,6 +99,12 @@ extern int RoomVersionGet(Room *);
|
||||||
* client events.
|
* client events.
|
||||||
*/
|
*/
|
||||||
extern HashMap * RoomStateGet(Room *);
|
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
|
* Get a list of the most recent events in the
|
||||||
|
@ -138,4 +145,10 @@ extern HashMap * RoomEventSend(Room *, HashMap *);
|
||||||
*/
|
*/
|
||||||
extern HashMap * RoomEventFetch(Room *, char *);
|
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 */
|
#endif /* TELODENDRIA_ROOM_H */
|
||||||
|
|
|
@ -47,7 +47,16 @@ extern char *StateGet(HashMap *, char *, char *);
|
||||||
/**
|
/**
|
||||||
* Set a state tuple to a value.
|
* 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.
|
* Compute the room state before the specified event was sent.
|
||||||
|
|
Loading…
Reference in a new issue