forked from Telodendria/Telodendria
[MOD] Revamping the state a bit
Some checks are pending
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Waiting to run
Some checks are pending
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Waiting to run
This commit is contained in:
parent
15f4de2a25
commit
b719a16d9f
5 changed files with 261 additions and 178 deletions
39
src/Room.c
39
src/Room.c
|
@ -35,6 +35,7 @@
|
||||||
#include <Cytoplasm/Base64.h>
|
#include <Cytoplasm/Base64.h>
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
|
|
||||||
#include <Schema/RoomCreateRequest.h>
|
#include <Schema/RoomCreateRequest.h>
|
||||||
|
@ -430,7 +431,7 @@ RoomVersionGet(Room * room)
|
||||||
return room ? room->version : 0;
|
return room ? room->version : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap *
|
State *
|
||||||
RoomStateGet(Room * room)
|
RoomStateGet(Room * room)
|
||||||
{
|
{
|
||||||
HashMap *database_state;
|
HashMap *database_state;
|
||||||
|
@ -446,10 +447,11 @@ RoomStateGet(Room * room)
|
||||||
database_state = DbJson(room->state_ref);
|
database_state = DbJson(room->state_ref);
|
||||||
return StateDeserialise(database_state);
|
return StateDeserialise(database_state);
|
||||||
}
|
}
|
||||||
HashMap *
|
State *
|
||||||
RoomStateGetID(Room * room, char *event_id)
|
RoomStateGetID(Room * room, char *event_id)
|
||||||
{
|
{
|
||||||
HashMap *event, *state;
|
HashMap *event;
|
||||||
|
State *state;
|
||||||
if (!room || !event_id)
|
if (!room || !event_id)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -490,7 +492,7 @@ RoomStateGetID(Room * room, char *event_id)
|
||||||
} \
|
} \
|
||||||
return ret
|
return ret
|
||||||
static bool
|
static bool
|
||||||
RoomIsJoinRule(Room * room, HashMap *state, char *jr)
|
RoomIsJoinRule(Room * room, State *state, char *jr)
|
||||||
{
|
{
|
||||||
HashMap *joinrule = NULL;
|
HashMap *joinrule = NULL;
|
||||||
char *id_joinrule = NULL;
|
char *id_joinrule = NULL;
|
||||||
|
@ -510,7 +512,7 @@ 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, HashMap *state, char *user, char *mbr)
|
RoomUserHasMembership(Room * room, State *state, char *user, char *mbr)
|
||||||
{
|
{
|
||||||
HashMap *membership = NULL;
|
HashMap *membership = NULL;
|
||||||
char *id_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 */
|
/* Computes the smallest PL needed to do something somewhere */
|
||||||
static int64_t
|
static int64_t
|
||||||
RoomMinPL(Room * room, HashMap *state, char *type, char *act)
|
RoomMinPL(Room * room, State *state, char *type, char *act)
|
||||||
{
|
{
|
||||||
HashMap *pl = NULL;
|
HashMap *pl = NULL;
|
||||||
JsonValue *val;
|
JsonValue *val;
|
||||||
|
@ -599,7 +601,7 @@ 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. */
|
||||||
/* TODO: The creator should have PL100 by default. */
|
/* TODO: The creator should have PL100 by default. */
|
||||||
static int64_t
|
static int64_t
|
||||||
RoomUserPL(Room * room, HashMap *state, char *user)
|
RoomUserPL(Room * room, State *state, char *user)
|
||||||
{
|
{
|
||||||
HashMap *pl = NULL;
|
HashMap *pl = NULL;
|
||||||
char *id_pl;
|
char *id_pl;
|
||||||
|
@ -919,7 +921,7 @@ AuthoriseAliasV1(PduV1 pdu)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
{
|
{
|
||||||
int64_t invite_level;
|
int64_t invite_level;
|
||||||
int64_t pdu_level;
|
int64_t pdu_level;
|
||||||
|
@ -1018,7 +1020,7 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
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 ban_level = RoomMinPL(room, state, NULL, "ban");
|
||||||
int64_t kick_level = RoomMinPL(room, state, NULL, "kick");
|
int64_t kick_level = RoomMinPL(room, state, NULL, "kick");
|
||||||
|
@ -1061,7 +1063,7 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
AuthorizeBanMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
{
|
{
|
||||||
int64_t ban_pl, pdu_pl, target_pl;
|
int64_t ban_pl, pdu_pl, target_pl;
|
||||||
|
|
||||||
|
@ -1086,7 +1088,7 @@ AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
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
|
/* 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. */
|
||||||
|
@ -1144,7 +1146,7 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state)
|
AuthoriseMemberV1(Room * room, PduV1 pdu, State *state)
|
||||||
{
|
{
|
||||||
JsonValue *membership;
|
JsonValue *membership;
|
||||||
char *membership_str;
|
char *membership_str;
|
||||||
|
@ -1186,7 +1188,7 @@ AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
#undef JumpIfMembership
|
#undef JumpIfMembership
|
||||||
}
|
}
|
||||||
static bool
|
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
|
/* 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
|
||||||
|
@ -1362,7 +1364,7 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu, HashMap *state)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
RoomAuthoriseEventV1(Room * room, PduV1 pdu, HashMap *state)
|
RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state)
|
||||||
{
|
{
|
||||||
HashMap *create_event;
|
HashMap *create_event;
|
||||||
char *create_event_id;
|
char *create_event_id;
|
||||||
|
@ -1563,12 +1565,13 @@ RoomEventSendV1(Room * room, HashMap * event)
|
||||||
PduV1 pdu = { 0 };
|
PduV1 pdu = { 0 };
|
||||||
HashMap *pdu_object = NULL;
|
HashMap *pdu_object = NULL;
|
||||||
bool client_event, valid = false;
|
bool client_event, valid = false;
|
||||||
HashMap *state = NULL;
|
State *state = NULL;
|
||||||
|
|
||||||
client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room));
|
client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room));
|
||||||
pdu_object = PduV1ToJson(&pdu);
|
pdu_object = PduV1ToJson(&pdu);
|
||||||
|
|
||||||
state = StateResolve(room, pdu_object);
|
state = StateResolve(room, pdu_object);
|
||||||
|
|
||||||
if (client_event)
|
if (client_event)
|
||||||
{
|
{
|
||||||
char *ev_id;
|
char *ev_id;
|
||||||
|
@ -1851,7 +1854,7 @@ RoomAddEventV1(Room *room, PduV1 pdu)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
HashMap *state;
|
State *state;
|
||||||
char *type, *state_key, *event_id;
|
char *type, *state_key, *event_id;
|
||||||
|
|
||||||
pdu_json = PduV1ToJson(&pdu);
|
pdu_json = PduV1ToJson(&pdu);
|
||||||
|
@ -2133,7 +2136,7 @@ RoomGetDB(Room *room)
|
||||||
bool
|
bool
|
||||||
RoomContainsUser(Room *room, char *user)
|
RoomContainsUser(Room *room, char *user)
|
||||||
{
|
{
|
||||||
HashMap *state;
|
State *state;
|
||||||
bool ret;
|
bool ret;
|
||||||
if (!room || !user)
|
if (!room || !user)
|
||||||
{
|
{
|
||||||
|
@ -2151,7 +2154,7 @@ RoomContainsUser(Room *room, char *user)
|
||||||
bool
|
bool
|
||||||
RoomCanJoin(Room *room, char *user)
|
RoomCanJoin(Room *room, char *user)
|
||||||
{
|
{
|
||||||
HashMap *state;
|
State *state;
|
||||||
HashMap *joinRule = NULL;
|
HashMap *joinRule = NULL;
|
||||||
char *joinRuleV;
|
char *joinRuleV;
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
|
@ -184,7 +184,7 @@ ROUTE_IMPL(RouteSync, path, argp)
|
||||||
char *firstEvent = NULL;
|
char *firstEvent = NULL;
|
||||||
char *type, *key, *id;
|
char *type, *key, *id;
|
||||||
HashMap *joinedObj;
|
HashMap *joinedObj;
|
||||||
HashMap *state;
|
State *state;
|
||||||
Array *el;
|
Array *el;
|
||||||
size_t j;
|
size_t j;
|
||||||
Room *r;
|
Room *r;
|
||||||
|
|
367
src/State.c
367
src/State.c
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <Cytoplasm/HashMap.h>
|
#include <Cytoplasm/HashMap.h>
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Base64.h>
|
||||||
#include <Cytoplasm/Array.h>
|
#include <Cytoplasm/Array.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Sha.h>
|
#include <Cytoplasm/Sha.h>
|
||||||
|
@ -36,6 +37,21 @@
|
||||||
#include <Event.h>
|
#include <Event.h>
|
||||||
#include <Room.h>
|
#include <Room.h>
|
||||||
|
|
||||||
|
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
|
int
|
||||||
V1Cmp(void *a, void *b)
|
V1Cmp(void *a, void *b)
|
||||||
{
|
{
|
||||||
|
@ -76,30 +92,60 @@ V1Cmp(void *a, void *b)
|
||||||
return ret;
|
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;
|
size_t i;
|
||||||
ssize_t j;
|
char *type = NULL, *key = NULL;
|
||||||
|
|
||||||
char *type, *key, *event_id;
|
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(states); i++)
|
for (i = 0; i < ArraySize(states); i++)
|
||||||
{
|
{
|
||||||
HashMap *state = ArrayGet(states, i);
|
char *event_id;
|
||||||
char *tuple;
|
State *state = ArrayGet(states, i);
|
||||||
while (HashMapIterate(state, &tuple, (void **) &event_id))
|
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;
|
Array *arr;
|
||||||
HashMap *hm;
|
HashMap *hm;
|
||||||
|
|
||||||
/* Conflicts! */
|
/* Conflicts! */
|
||||||
HashMapDelete(R, tuple);
|
StateSet(R, type, key, NULL);
|
||||||
arr = HashMapGet(conflicts, tuple);
|
arr = HashMapGet(conflicts, tuple);
|
||||||
if (!arr)
|
if (!arr)
|
||||||
{
|
{
|
||||||
|
@ -112,98 +158,136 @@ StateResolveV1(Room * room, Array * states)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Add to R */
|
/* Add to R */
|
||||||
HashMapSet(R, tuple, StrDuplicate(event_id));
|
StateSet(R, type, key, 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(tuple);
|
||||||
Free(type);
|
Free(type);
|
||||||
Free(key);
|
Free(key);
|
||||||
}
|
}
|
||||||
ArraySort(events, V1Cmp);
|
}
|
||||||
/* Add first event. */
|
}
|
||||||
first = ArrayDelete(events, 0);
|
static void
|
||||||
StateSet(
|
FixoutConflictV1(char *t, Room *room, State *R, HashMap *conflicts)
|
||||||
R,
|
{
|
||||||
JsonValueAsString(JsonGet(first, 1, "type")),
|
HashMap *first;
|
||||||
JsonValueAsString(JsonGet(first, 1, "state_key")),
|
Array *state_keys, *conflicting, *events;
|
||||||
JsonValueAsString(JsonGet(first, 1, "event_id")));
|
char *tuple;
|
||||||
JsonFree(first);
|
size_t i;
|
||||||
|
|
||||||
for (j = 0; j < (ssize_t) ArraySize(events); j++)
|
events = ArrayCreate();
|
||||||
|
while (HashMapIterate(conflicts, &tuple, (void **) &conflicting))
|
||||||
|
{
|
||||||
|
char *type, *key;
|
||||||
|
DecodeStateTuple(tuple, &type, &key);
|
||||||
|
|
||||||
|
if (StrEquals(type, t))
|
||||||
{
|
{
|
||||||
HashMap *event = ArrayGet(events, j);
|
for (i = 0; i < ArraySize(conflicting); i++)
|
||||||
PduV1 pdu;
|
{
|
||||||
char *msg;
|
HashMap *event = ArrayGet(conflicting, i);
|
||||||
|
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"))
|
||||||
|
);
|
||||||
|
JsonFree(first);
|
||||||
|
|
||||||
PduV1FromJson(event, &pdu, &msg);
|
for (i = 0; i < ArraySize(events); i++)
|
||||||
if (RoomAuthoriseEventV1(room, pdu, R))
|
{
|
||||||
{
|
HashMap *event = ArrayGet(events, i);
|
||||||
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
PduV1 pdu;
|
||||||
}
|
char *msg;
|
||||||
else
|
|
||||||
{
|
PduV1FromJson(event, &pdu, &msg);
|
||||||
PduV1Free(&pdu);
|
if (RoomAuthoriseEventV1(room, pdu, R))
|
||||||
JsonFree(event);
|
{
|
||||||
break;
|
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
||||||
}
|
}
|
||||||
(void) msg;
|
else
|
||||||
|
{
|
||||||
PduV1Free(&pdu);
|
PduV1Free(&pdu);
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
ArrayFree(events);
|
(void) msg;
|
||||||
/* Delete all elements within a key. */
|
PduV1Free(&pdu);
|
||||||
state_keys = ArrayCreate();
|
JsonFree(event);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
ArrayFree(types);
|
ArrayFree(events);
|
||||||
|
|
||||||
while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
|
state_keys = ArrayCreate();
|
||||||
|
while (HashMapIterate(conflicts, &tuple, (void **) &conflicting))
|
||||||
{
|
{
|
||||||
ArraySort(conflicting, V1Cmp);
|
char *type, *key;
|
||||||
for (j = ArraySize(conflicting) - 1; j >= 0; j--)
|
DecodeStateTuple(tuple, &type, &key);
|
||||||
|
|
||||||
|
if (StrEquals(type, t))
|
||||||
{
|
{
|
||||||
HashMap *event = ArrayGet(events, j);
|
ArrayAdd(state_keys, key);
|
||||||
PduV1 pdu;
|
}
|
||||||
|
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;
|
char *msg;
|
||||||
|
|
||||||
PduV1FromJson(event, &pdu, &msg);
|
if (!PduV1FromJson(event, &pdu, &msg))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (RoomAuthoriseEventV1(room, pdu, R))
|
if (RoomAuthoriseEventV1(room, pdu, R))
|
||||||
{
|
{
|
||||||
|
@ -217,8 +301,10 @@ StateResolveV1(Room * room, Array * states)
|
||||||
Free(type);
|
Free(type);
|
||||||
Free(key);
|
Free(key);
|
||||||
}
|
}
|
||||||
while (HashMapIterate(conflicts, &type, (void **) &conflicting))
|
|
||||||
|
while (HashMapIterate(conflicts, &tuple, (void **) &conflicting))
|
||||||
{
|
{
|
||||||
|
size_t i;
|
||||||
for (i = 0; i < ArraySize(conflicting); i++)
|
for (i = 0; i < ArraySize(conflicting); i++)
|
||||||
{
|
{
|
||||||
JsonFree(ArrayGet(conflicting, i));
|
JsonFree(ArrayGet(conflicting, i));
|
||||||
|
@ -230,14 +316,14 @@ StateResolveV1(Room * room, Array * states)
|
||||||
return R;
|
return R;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HashMap *
|
static State *
|
||||||
StateResolveV2(Array * states)
|
StateResolveV2(Array * states)
|
||||||
{
|
{
|
||||||
(void) states;
|
(void) states;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HashMap *
|
static State *
|
||||||
StateFromPrevs(Room *room, Array *states)
|
StateFromPrevs(Room *room, Array *states)
|
||||||
{
|
{
|
||||||
switch (RoomVersionGet(room))
|
switch (RoomVersionGet(room))
|
||||||
|
@ -249,7 +335,7 @@ StateFromPrevs(Room *room, Array *states)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap *
|
State *
|
||||||
StateResolve(Room * room, HashMap * event)
|
StateResolve(Room * room, HashMap * event)
|
||||||
{
|
{
|
||||||
Array *states;
|
Array *states;
|
||||||
|
@ -257,7 +343,7 @@ StateResolve(Room * room, HashMap * event)
|
||||||
|
|
||||||
Array *prevEvents;
|
Array *prevEvents;
|
||||||
|
|
||||||
HashMap *ret_state;
|
State *ret_state;
|
||||||
|
|
||||||
char *room_id, *event_id;
|
char *room_id, *event_id;
|
||||||
|
|
||||||
|
@ -300,7 +386,7 @@ StateResolve(Room * room, HashMap * event)
|
||||||
{
|
{
|
||||||
HashMap *prevEvent =
|
HashMap *prevEvent =
|
||||||
RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i)));
|
RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i)));
|
||||||
HashMap *state = StateResolve(room, prevEvent);
|
State *state = StateResolve(room, prevEvent);
|
||||||
|
|
||||||
if (HashMapGet(prevEvent, "state_key"))
|
if (HashMapGet(prevEvent, "state_key"))
|
||||||
{
|
{
|
||||||
|
@ -308,7 +394,8 @@ StateResolve(Room * room, HashMap * event)
|
||||||
state,
|
state,
|
||||||
JsonValueAsString(HashMapGet(prevEvent, "type")),
|
JsonValueAsString(HashMapGet(prevEvent, "type")),
|
||||||
JsonValueAsString(HashMapGet(prevEvent, "state_key")),
|
JsonValueAsString(HashMapGet(prevEvent, "state_key")),
|
||||||
JsonValueAsString(HashMapGet(prevEvent, "event_id")));
|
JsonValueAsString(HashMapGet(prevEvent, "event_id"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayAdd(states, state);
|
ArrayAdd(states, state);
|
||||||
|
@ -319,7 +406,7 @@ StateResolve(Room * room, HashMap * event)
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(states); i++)
|
for (i = 0; i < ArraySize(states); i++)
|
||||||
{
|
{
|
||||||
HashMap *state = ArrayGet(states, i);
|
State *state = ArrayGet(states, i);
|
||||||
StateFree(state);
|
StateFree(state);
|
||||||
}
|
}
|
||||||
ArrayFree(states);
|
ArrayFree(states);
|
||||||
|
@ -335,13 +422,13 @@ StateResolve(Room * room, HashMap * event)
|
||||||
|
|
||||||
return ret_state;
|
return ret_state;
|
||||||
}
|
}
|
||||||
HashMap *
|
State *
|
||||||
StateCurrent(Room *room)
|
StateCurrent(Room *room)
|
||||||
{
|
{
|
||||||
Array *prevEvents;
|
Array *prevEvents;
|
||||||
Array *states;
|
Array *states;
|
||||||
size_t i;
|
size_t i;
|
||||||
HashMap *ret;
|
State *ret;
|
||||||
if (!room)
|
if (!room)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -353,8 +440,7 @@ StateCurrent(Room *room)
|
||||||
{
|
{
|
||||||
HashMap *event =
|
HashMap *event =
|
||||||
JsonValueAsObject(ArrayGet(prevEvents, i));
|
JsonValueAsObject(ArrayGet(prevEvents, i));
|
||||||
HashMap *state = StateResolve(room, event);
|
State *state = StateResolve(room, event);
|
||||||
|
|
||||||
|
|
||||||
if (HashMapGet(event, "state_key"))
|
if (HashMapGet(event, "state_key"))
|
||||||
{
|
{
|
||||||
|
@ -362,7 +448,8 @@ StateCurrent(Room *room)
|
||||||
state,
|
state,
|
||||||
JsonValueAsString(HashMapGet(event, "type")),
|
JsonValueAsString(HashMapGet(event, "type")),
|
||||||
JsonValueAsString(HashMapGet(event, "state_key")),
|
JsonValueAsString(HashMapGet(event, "state_key")),
|
||||||
JsonValueAsString(HashMapGet(event, "event_id")));
|
JsonValueAsString(HashMapGet(event, "event_id"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayAdd(states, state);
|
ArrayAdd(states, state);
|
||||||
|
@ -372,7 +459,7 @@ StateCurrent(Room *room)
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(states); i++)
|
for (i = 0; i < ArraySize(states); i++)
|
||||||
{
|
{
|
||||||
HashMap *state = ArrayGet(states, i);
|
State *state = ArrayGet(states, i);
|
||||||
StateFree(state);
|
StateFree(state);
|
||||||
}
|
}
|
||||||
ArrayFree(states);
|
ArrayFree(states);
|
||||||
|
@ -380,90 +467,79 @@ StateCurrent(Room *room)
|
||||||
return ret;
|
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
|
bool
|
||||||
StateIterate(HashMap *state, char **type, char **key, void **event)
|
StateIterate(State *state, char **type, char **key, void **event)
|
||||||
{
|
{
|
||||||
char *tuple;
|
|
||||||
bool ret;
|
bool ret;
|
||||||
|
char *tuple;
|
||||||
if (!state || !type || !key || !event)
|
if (!state || !type || !key || !event)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = HashMapIterate(state, &tuple, event);
|
ret = HashMapIterate(state->table, &tuple, event);
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
tuple = StrDuplicate(tuple);
|
DecodeStateTuple(tuple, type, key);
|
||||||
*(strchr(tuple, ',')) = '\0';
|
|
||||||
|
|
||||||
*type = tuple;
|
|
||||||
*key = StrDuplicate(tuple + strlen(tuple) + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
StateGet(HashMap *state, char *type, char *key)
|
StateGet(State *state, char *type, char *key)
|
||||||
{
|
{
|
||||||
char *full_string;
|
char *tableKey;
|
||||||
char *ret;
|
char *ret;
|
||||||
if (!state || !type || !key)
|
if (!state || !type || !key)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
full_string = StrConcat(3, type, ",", key);
|
tableKey = EncodeStateTuple(type, key);
|
||||||
ret = HashMapGet(state, full_string);
|
ret = HashMapGet(state->table, tableKey);
|
||||||
Free(full_string);
|
|
||||||
|
|
||||||
|
Free(tableKey);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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)
|
if (!state || !type || !key)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
tableKey = EncodeStateTuple(type, key);
|
||||||
full_string = StrConcat(3, type, ",", key);
|
Free(HashMapDelete(state->table, tableKey));
|
||||||
old = HashMapDelete(state, full_string);
|
|
||||||
if (old)
|
|
||||||
{
|
|
||||||
Free(old);
|
|
||||||
}
|
|
||||||
if (event)
|
if (event)
|
||||||
{
|
{
|
||||||
HashMapSet(state, full_string, StrDuplicate(event));
|
HashMapSet(state->table, tableKey, StrDuplicate(event));
|
||||||
}
|
}
|
||||||
Free(full_string);
|
Free(tableKey);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
StateFree(HashMap *state)
|
StateFree(State *state)
|
||||||
{
|
{
|
||||||
char *full;
|
char *tuple;
|
||||||
char *event_id;
|
char *eventID;
|
||||||
|
|
||||||
if (!state)
|
if (!state)
|
||||||
{
|
{
|
||||||
return;
|
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)
|
StateDeserialise(HashMap *json_state)
|
||||||
{
|
{
|
||||||
HashMap *raw_state;
|
State *raw_state;
|
||||||
|
|
||||||
char *state_type;
|
char *state_type;
|
||||||
JsonValue *state_keys;
|
JsonValue *state_keys;
|
||||||
|
@ -473,7 +549,7 @@ StateDeserialise(HashMap *json_state)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
raw_state = HashMapCreate();
|
raw_state = InitialiseState();
|
||||||
|
|
||||||
while (HashMapIterate(json_state, &state_type, (void **) &state_keys))
|
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))
|
while (HashMapIterate(state_keys_obj, &state_key, (void **) &event_id))
|
||||||
{
|
{
|
||||||
char *eid_string = JsonValueAsString(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));
|
StateSet(raw_state, state_type, state_key, eid_string);
|
||||||
|
|
||||||
Free(key_name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return raw_state;
|
return raw_state;
|
||||||
}
|
}
|
||||||
HashMap *
|
HashMap *
|
||||||
StateSerialise(HashMap *rawState)
|
StateSerialise(State *rawState)
|
||||||
{
|
{
|
||||||
HashMap *returned;
|
HashMap *returned;
|
||||||
char *type, *key, *event;
|
char *type, *key, *event;
|
||||||
|
|
|
@ -51,6 +51,8 @@
|
||||||
*/
|
*/
|
||||||
typedef struct Room Room;
|
typedef struct Room Room;
|
||||||
|
|
||||||
|
#include <State.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new room in the given database using the given
|
* Create a new room in the given database using the given
|
||||||
* RoomCreateRequest.
|
* RoomCreateRequest.
|
||||||
|
@ -104,14 +106,14 @@ extern int RoomVersionGet(Room *);
|
||||||
* which is used to select auth events on incoming
|
* which is used to select auth events on incoming
|
||||||
* client events.
|
* client events.
|
||||||
*/
|
*/
|
||||||
extern HashMap * RoomStateGet(Room *);
|
extern State * RoomStateGet(Room *);
|
||||||
/**
|
/**
|
||||||
* Resolves the room's state before a specific point,
|
* Resolves the room's state before a specific point,
|
||||||
* (with the event hashmap taking priority),
|
* (with the event hashmap taking priority),
|
||||||
* like
|
* like
|
||||||
* .Fn RoomStateGet .
|
* .Fn RoomStateGet .
|
||||||
*/
|
*/
|
||||||
extern HashMap * RoomStateGetID(Room *, char *);
|
extern State * RoomStateGetID(Room *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of the most recent events in the
|
* 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
|
* Verifies whenever an event(as a PDUv1) is
|
||||||
* authorised by a room.
|
* 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
|
* Gets the room's creator as a ServerPart. This value should
|
||||||
|
|
|
@ -37,17 +37,22 @@
|
||||||
|
|
||||||
#include <Cytoplasm/HashMap.h>
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An opaque structure to hold information about the state.
|
||||||
|
*/
|
||||||
|
typedef struct State State;
|
||||||
|
|
||||||
#include <Room.h>
|
#include <Room.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the value of a state tuple.
|
* 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.
|
* 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.
|
* Iterates through a statemap, with (type, key) -> event.
|
||||||
|
@ -56,22 +61,22 @@ extern void StateSet(HashMap *, char *, char *, char *);
|
||||||
* This function behaves like
|
* This function behaves like
|
||||||
* .Fn HashMapIterate .
|
* .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.
|
* 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.
|
* 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.
|
* 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
|
* Deserialises a state map from JSON to the internal format
|
||||||
|
@ -81,11 +86,11 @@ extern void StateFree(HashMap *);
|
||||||
* and should be freed with
|
* and should be freed with
|
||||||
* .Fn StateFree .
|
* .Fn StateFree .
|
||||||
*/
|
*/
|
||||||
extern HashMap * StateDeserialise(HashMap *);
|
extern State * StateDeserialise(HashMap *);
|
||||||
/**
|
/**
|
||||||
* Serialises a state map from the internal format to JSON
|
* Serialises a state map from the internal format to JSON
|
||||||
* used for the database, for example
|
* used for the database, for example
|
||||||
*/
|
*/
|
||||||
extern HashMap * StateSerialise(HashMap *);
|
extern HashMap * StateSerialise(State *);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_STATE_H */
|
#endif /* TELODENDRIA_STATE_H */
|
||||||
|
|
Loading…
Reference in a new issue