forked from Telodendria/Telodendria
Compare commits
2 commits
6b39b3eb74
...
276b7fecd7
Author | SHA1 | Date | |
---|---|---|---|
|
276b7fecd7 | ||
|
68c47e1ae6 |
27 changed files with 792 additions and 98 deletions
|
@ -48,6 +48,9 @@
|
||||||
"room_alias_name": {
|
"room_alias_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"room_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"preset": {
|
"preset": {
|
||||||
"type": "RoomCreatePreset"
|
"type": "RoomCreatePreset"
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,13 +77,13 @@ SignalHandler(int signal)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserNotifyAll(matrixArgs.db);
|
|
||||||
for (i = 0; i < ArraySize(httpServers); i++)
|
for (i = 0; i < ArraySize(httpServers); i++)
|
||||||
{
|
{
|
||||||
HttpServer *server = ArrayGet(httpServers, i);
|
HttpServer *server = ArrayGet(httpServers, i);
|
||||||
/* TODO: Notify all users, so that the server doesn't have to
|
/* TODO: Notify all users, so that the server doesn't have to
|
||||||
* await for a sync reply. */
|
* await for a sync reply. */
|
||||||
|
|
||||||
|
UserNotifyAll(matrixArgs.db);
|
||||||
HttpServerStop(server);
|
HttpServerStop(server);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
85
src/Room.c
85
src/Room.c
|
@ -34,7 +34,6 @@
|
||||||
#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>
|
||||||
|
@ -54,13 +53,15 @@
|
||||||
#include "Room/internal.h"
|
#include "Room/internal.h"
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
GenerateRoomId(ServerPart s)
|
GenerateRoomId(RoomCreateRequest *req, ServerPart s)
|
||||||
{
|
{
|
||||||
CommonID cid;
|
CommonID cid;
|
||||||
char *string;
|
char *string;
|
||||||
|
|
||||||
cid.sigil = '!';
|
cid.sigil = '!';
|
||||||
cid.local = StrRandom(32);
|
cid.local = (req && req->room_id) ?
|
||||||
|
StrDuplicate(req->room_id) :
|
||||||
|
StrRandom(32);
|
||||||
cid.server = s;
|
cid.server = s;
|
||||||
string = ParserRecomposeCommonID(cid);
|
string = ParserRecomposeCommonID(cid);
|
||||||
Free(cid.local);
|
Free(cid.local);
|
||||||
|
@ -91,7 +92,7 @@ RoomCreate(Db * db, User *user, RoomCreateRequest * req, ServerPart s)
|
||||||
room->db = db;
|
room->db = db;
|
||||||
room->creator.hostname = s.hostname ? StrDuplicate(s.hostname) : NULL;
|
room->creator.hostname = s.hostname ? StrDuplicate(s.hostname) : NULL;
|
||||||
room->creator.port = s.port ? StrDuplicate(s.port) : NULL;
|
room->creator.port = s.port ? StrDuplicate(s.port) : NULL;
|
||||||
room->id = GenerateRoomId(s);
|
room->id = GenerateRoomId(req, s);
|
||||||
room->version = version_num;
|
room->version = version_num;
|
||||||
|
|
||||||
room->state_ref = DbCreate(db, 3, "rooms", room->id, "state");
|
room->state_ref = DbCreate(db, 3, "rooms", room->id, "state");
|
||||||
|
@ -278,7 +279,38 @@ CreateSafeID(char *unsafe_id)
|
||||||
return safe_id;
|
return safe_id;
|
||||||
}
|
}
|
||||||
HashMap *
|
HashMap *
|
||||||
RoomEventFetch(Room *room, char *id)
|
RoomEventFetchRaw(Room *room, char *id)
|
||||||
|
{
|
||||||
|
char *safe_id;
|
||||||
|
HashMap *ret;
|
||||||
|
DbRef *event_ref;
|
||||||
|
|
||||||
|
if (!room || !id)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Let's try to locally find that event in our junk. */
|
||||||
|
safe_id = CreateSafeID(id);
|
||||||
|
event_ref = DbLockIntent(
|
||||||
|
room->db, DB_HINT_READONLY,
|
||||||
|
4, "rooms", room->id, "events", safe_id
|
||||||
|
);
|
||||||
|
if (!event_ref)
|
||||||
|
{
|
||||||
|
/* TODO: Fetch from another homeserver if possible. */
|
||||||
|
ret = NULL;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
ret = JsonDuplicate(JsonValueAsObject(HashMapGet(DbJson(event_ref), "pdu")));
|
||||||
|
|
||||||
|
DbUnlock(room->db, event_ref);
|
||||||
|
finish:
|
||||||
|
Free(safe_id);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
HashMap *
|
||||||
|
RoomEventFetch(Room *room, char *id, bool prev)
|
||||||
{
|
{
|
||||||
char *safe_id, *redactor;
|
char *safe_id, *redactor;
|
||||||
HashMap *ret, *unsign;
|
HashMap *ret, *unsign;
|
||||||
|
@ -292,7 +324,10 @@ RoomEventFetch(Room *room, char *id)
|
||||||
|
|
||||||
/* Let's try to locally find that event in our junk. */
|
/* Let's try to locally find that event in our junk. */
|
||||||
safe_id = CreateSafeID(id);
|
safe_id = CreateSafeID(id);
|
||||||
event_ref = DbLock(room->db, 4, "rooms", room->id, "events", safe_id);
|
event_ref = DbLockIntent(
|
||||||
|
room->db, DB_HINT_READONLY,
|
||||||
|
4, "rooms", room->id, "events", safe_id
|
||||||
|
);
|
||||||
if (!event_ref)
|
if (!event_ref)
|
||||||
{
|
{
|
||||||
/* TODO: Fetch from another homeserver if possible. */
|
/* TODO: Fetch from another homeserver if possible. */
|
||||||
|
@ -302,7 +337,6 @@ RoomEventFetch(Room *room, char *id)
|
||||||
ret = JsonDuplicate(JsonValueAsObject(HashMapGet(DbJson(event_ref), "pdu")));
|
ret = JsonDuplicate(JsonValueAsObject(HashMapGet(DbJson(event_ref), "pdu")));
|
||||||
unsign = JsonValueAsObject(HashMapGet(ret, "unsigned"));
|
unsign = JsonValueAsObject(HashMapGet(ret, "unsigned"));
|
||||||
|
|
||||||
|
|
||||||
/* Overwrite a few unsigned properties on the fly. */
|
/* Overwrite a few unsigned properties on the fly. */
|
||||||
JsonValueFree(HashMapSet(
|
JsonValueFree(HashMapSet(
|
||||||
unsign,
|
unsign,
|
||||||
|
@ -321,12 +355,34 @@ RoomEventFetch(Room *room, char *id)
|
||||||
JsonValueInteger(UtilTsMillis() - ts)
|
JsonValueInteger(UtilTsMillis() - ts)
|
||||||
));
|
));
|
||||||
|
|
||||||
redactor = JsonValueAsString(HashMapGet(DbJson(event_ref), "redacted_by"));
|
if (prev)
|
||||||
JsonValueFree(HashMapSet(
|
{
|
||||||
unsign,
|
redactor =
|
||||||
"redacted_because",
|
JsonValueAsString(HashMapGet(DbJson(event_ref), "redacted_by"));
|
||||||
JsonValueObject(RoomEventFetch(room, redactor))
|
JsonValueFree(HashMapSet(
|
||||||
));
|
unsign,
|
||||||
|
"redacted_because",
|
||||||
|
JsonValueObject(RoomEventFetch(room, redactor, false))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev && HashMapGet(ret, "state_key"))
|
||||||
|
{
|
||||||
|
char *prev_ev;
|
||||||
|
HashMap *prev_obj;
|
||||||
|
JsonValue *prev_content;
|
||||||
|
prev_ev =
|
||||||
|
JsonValueAsString(HashMapGet(DbJson(event_ref), "prev_event"));
|
||||||
|
prev_obj = RoomEventFetch(room, prev_ev, false);
|
||||||
|
prev_content = HashMapGet(prev_obj, "content");
|
||||||
|
JsonValueFree(HashMapSet(
|
||||||
|
unsign,
|
||||||
|
"prev_content",
|
||||||
|
JsonValueDuplicate(prev_content)
|
||||||
|
));
|
||||||
|
|
||||||
|
JsonFree(prev_obj);
|
||||||
|
}
|
||||||
|
|
||||||
DbUnlock(room->db, event_ref);
|
DbUnlock(room->db, event_ref);
|
||||||
finish:
|
finish:
|
||||||
|
@ -562,7 +618,7 @@ RoomCanJoin(Room *room, char *user)
|
||||||
/* Check join_rules */
|
/* Check join_rules */
|
||||||
joinRule = RoomEventFetch(
|
joinRule = RoomEventFetch(
|
||||||
room,
|
room,
|
||||||
StateGet(state, "m.room.join_rules", "")
|
StateGet(state, "m.room.join_rules", ""), false
|
||||||
);
|
);
|
||||||
joinRuleV = JsonValueAsString(JsonGet(joinRule, 2, "content", "join_rule"));
|
joinRuleV = JsonValueAsString(JsonGet(joinRule, 2, "content", "join_rule"));
|
||||||
|
|
||||||
|
@ -807,6 +863,7 @@ RoomEventClientify(HashMap *pdu)
|
||||||
|
|
||||||
CopyField("state_key",false);
|
CopyField("state_key",false);
|
||||||
CopyField("redacts", false);
|
CopyField("redacts", false);
|
||||||
|
CopyField("unsigned", false);
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ RoomStateGetID(Room * room, char *event_id)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
event = RoomEventFetch(room, event_id);
|
event = RoomEventFetch(room, event_id, false);
|
||||||
if (!event)
|
if (!event)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -53,7 +53,7 @@ GetCurrentMembership(Room *room, State *s, char *mxid)
|
||||||
}
|
}
|
||||||
|
|
||||||
event_id = StateGet(state, "m.room.member", mxid);
|
event_id = StateGet(state, "m.room.member", mxid);
|
||||||
event = RoomEventFetch(room, event_id);
|
event = RoomEventFetchRaw(room, event_id);
|
||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
{
|
{
|
||||||
|
@ -94,7 +94,7 @@ RoomIsEventVisible(Room *room, User *user, char *eventId)
|
||||||
|
|
||||||
state = RoomStateGetID(room, eventId);
|
state = RoomStateGetID(room, eventId);
|
||||||
|
|
||||||
entryV = RoomEventFetch(room,
|
entryV = RoomEventFetchRaw(room,
|
||||||
StateGet(state, "m.room.history_visibility", "")
|
StateGet(state, "m.room.history_visibility", "")
|
||||||
);
|
);
|
||||||
if (!(visibility = JsonValueAsString(
|
if (!(visibility = JsonValueAsString(
|
||||||
|
@ -148,7 +148,7 @@ RoomIsEventVisible(Room *room, User *user, char *eventId)
|
||||||
{ \
|
{ \
|
||||||
goto finish; \
|
goto finish; \
|
||||||
} \
|
} \
|
||||||
n = RoomEventFetch(room, id_##n); \
|
n = RoomEventFetchRaw(room, id_##n); \
|
||||||
if (!n) \
|
if (!n) \
|
||||||
{ \
|
{ \
|
||||||
goto finish; \
|
goto finish; \
|
||||||
|
|
|
@ -48,12 +48,14 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
int64_t pdu_pl = RoomUserPL(room, state, pdu.sender);
|
int64_t pdu_pl = RoomUserPL(room, state, pdu.sender);
|
||||||
int64_t event_pl = RoomMinPL(room,state, "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"))
|
||||||
{
|
{
|
||||||
return AuthoriseCreateV1(pdu, errp);
|
return AuthoriseCreateV1(pdu, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 2: Considering the event's auth_events. */
|
/* Step 2: Considering the event's auth_events.
|
||||||
|
* TODO: This is slow. */
|
||||||
if (!ConsiderAuthEventsV1(room, pdu, errp))
|
if (!ConsiderAuthEventsV1(room, pdu, errp))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -63,6 +65,7 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
* has the property m.federate set to false, and the sender domain of
|
* has the property m.federate set to false, and the sender domain of
|
||||||
* 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.
|
||||||
|
* TODO: This is slow.
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
|
@ -75,7 +78,7 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
create_event = RoomEventFetch(room, create_event_id);
|
create_event = RoomEventFetchRaw(room, create_event_id);
|
||||||
federate = JsonGet(create_event, 2, "content", "m.federate");
|
federate = JsonGet(create_event, 2, "content", "m.federate");
|
||||||
if (JsonValueType(federate) == JSON_BOOLEAN)
|
if (JsonValueType(federate) == JSON_BOOLEAN)
|
||||||
{
|
{
|
||||||
|
@ -157,6 +160,20 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StrEquals(create_event_id, pdu.redacts))
|
||||||
|
{
|
||||||
|
/* This is _not_ spec behaviour. As such, I _need_ to document
|
||||||
|
* this oddity. Apparently, up until v11 came along, a user could
|
||||||
|
* redact a creation event. Freely. Without complaining. Of course,
|
||||||
|
* this breaks the room.
|
||||||
|
*
|
||||||
|
* As such, I have made the room to NOT redact creation events.
|
||||||
|
* Diverges that could be caused by this is, in my opinion low,
|
||||||
|
* and all other servers that completely listen to the spec will
|
||||||
|
* be broken. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 11.2: If the domain of the event_id of the event being
|
/* Step 11.2: If the domain of the event_id of the event being
|
||||||
* redacted is the same as the domain of the event_id of the
|
* redacted is the same as the domain of the event_id of the
|
||||||
* m.room.redaction, allow. */
|
* m.room.redaction, allow. */
|
||||||
|
|
|
@ -94,7 +94,7 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu, char **errp)
|
||||||
for (i = 0; i < ArraySize(pdu.auth_events); i++)
|
for (i = 0; i < ArraySize(pdu.auth_events); i++)
|
||||||
{
|
{
|
||||||
char *event_id = JsonValueAsString(ArrayGet(pdu.auth_events, i));
|
char *event_id = JsonValueAsString(ArrayGet(pdu.auth_events, i));
|
||||||
HashMap *event = RoomEventFetch(room, event_id);
|
HashMap *event = RoomEventFetchRaw(room, event_id);
|
||||||
PduV1 auth_pdu = { 0 };
|
PduV1 auth_pdu = { 0 };
|
||||||
|
|
||||||
char *key_type_id;
|
char *key_type_id;
|
||||||
|
|
|
@ -79,7 +79,7 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
third_pi_event = RoomEventFetch(room, third_pi_id);
|
third_pi_event = RoomEventFetchRaw(room, third_pi_id);
|
||||||
|
|
||||||
/* 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. */
|
||||||
|
@ -256,7 +256,7 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
{
|
{
|
||||||
/* Interperet prev properly, as a list of JsonObjects. */
|
/* Interperet prev properly, as a list of JsonObjects. */
|
||||||
char *prev_id = JsonValueAsString(ArrayGet(prev, 0));
|
char *prev_id = JsonValueAsString(ArrayGet(prev, 0));
|
||||||
HashMap *prev_event = RoomEventFetch(room, prev_id);
|
HashMap *prev_event = RoomEventFetchRaw(room, prev_id);
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
|
|
||||||
if (prev_event)
|
if (prev_event)
|
||||||
|
|
|
@ -71,7 +71,7 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu, State *state)
|
||||||
/* Step 10.3: For the properties users_default, events_default,
|
/* Step 10.3: For the properties users_default, events_default,
|
||||||
* state_default, ban, redact, kick, invite, check if they were
|
* state_default, ban, redact, kick, invite, check if they were
|
||||||
* added, changed or removed. For each found alteration: */
|
* added, changed or removed. For each found alteration: */
|
||||||
prev_plevent = RoomEventFetch(room, prev_pl_id);
|
prev_plevent = RoomEventFetchRaw(room, prev_pl_id);
|
||||||
#define CheckChange(prop) do \
|
#define CheckChange(prop) do \
|
||||||
{ \
|
{ \
|
||||||
JsonValue *old = \
|
JsonValue *old = \
|
||||||
|
|
|
@ -122,7 +122,7 @@ RedactPDU1(HashMap *obj)
|
||||||
if (!StrEquals(key, "event_id") && !StrEquals(key, "type") &&
|
if (!StrEquals(key, "event_id") && !StrEquals(key, "type") &&
|
||||||
!StrEquals(key, "room_id") && !StrEquals(key, "sender") &&
|
!StrEquals(key, "room_id") && !StrEquals(key, "sender") &&
|
||||||
!StrEquals(key, "state_key")&& !StrEquals(key, "content") &&
|
!StrEquals(key, "state_key")&& !StrEquals(key, "content") &&
|
||||||
!StrEquals(key, "hashes") && !StrEquals(key, "signature") &&
|
!StrEquals(key, "hashes") && !StrEquals(key, "signatures") &&
|
||||||
!StrEquals(key, "depth") && !StrEquals(key, "prev_events") &&
|
!StrEquals(key, "depth") && !StrEquals(key, "prev_events") &&
|
||||||
!StrEquals(key, "auth_events")&& !StrEquals(key, "origin") &&
|
!StrEquals(key, "auth_events")&& !StrEquals(key, "origin") &&
|
||||||
!StrEquals(key, "unsigned") &&
|
!StrEquals(key, "unsigned") &&
|
||||||
|
@ -204,8 +204,9 @@ RoomAddEventV1(Room *room, PduV1 pdu, PduV1Status status)
|
||||||
DbRef *event_ref;
|
DbRef *event_ref;
|
||||||
Array *prev_events = NULL, *leaves = NULL;
|
Array *prev_events = NULL, *leaves = NULL;
|
||||||
HashMap *leaves_json = NULL, *pdu_json = NULL;
|
HashMap *leaves_json = NULL, *pdu_json = NULL;
|
||||||
|
State *prev_state = NULL;
|
||||||
JsonValue *leaves_val;
|
JsonValue *leaves_val;
|
||||||
char *safe_id;
|
char *safe_id, *prev_id;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (!room || room->version >= 3 ||
|
if (!room || room->version >= 3 ||
|
||||||
|
@ -218,6 +219,9 @@ RoomAddEventV1(Room *room, PduV1 pdu, PduV1Status status)
|
||||||
safe_id = CreateSafeID(pdu.event_id);
|
safe_id = CreateSafeID(pdu.event_id);
|
||||||
event_ref = DbCreate(room->db, 4, "rooms", room->id, "events", safe_id);
|
event_ref = DbCreate(room->db, 4, "rooms", room->id, "events", safe_id);
|
||||||
pdu_json = PduV1ToJson(&pdu);
|
pdu_json = PduV1ToJson(&pdu);
|
||||||
|
prev_state = StateResolve(room, pdu_json);
|
||||||
|
prev_id = StrDuplicate(StateGet(prev_state, pdu.type, pdu.state_key));
|
||||||
|
StateFree(prev_state);
|
||||||
|
|
||||||
/* TODO: Use those values concretely. */
|
/* TODO: Use those values concretely. */
|
||||||
pdu._unsigned.pdu_status = status;
|
pdu._unsigned.pdu_status = status;
|
||||||
|
@ -228,6 +232,11 @@ RoomAddEventV1(Room *room, PduV1 pdu, PduV1Status status)
|
||||||
JsonValueString(PduV1StatusToStr(status)),
|
JsonValueString(PduV1StatusToStr(status)),
|
||||||
1, "status"
|
1, "status"
|
||||||
);
|
);
|
||||||
|
JsonSet(
|
||||||
|
DbJson(event_ref),
|
||||||
|
JsonValueString(prev_id),
|
||||||
|
1, "prev_event"
|
||||||
|
);
|
||||||
JsonSet(
|
JsonSet(
|
||||||
DbJson(event_ref),
|
DbJson(event_ref),
|
||||||
JsonValueArray(ArrayCreate()),
|
JsonValueArray(ArrayCreate()),
|
||||||
|
@ -236,6 +245,7 @@ RoomAddEventV1(Room *room, PduV1 pdu, PduV1Status status)
|
||||||
|
|
||||||
DbUnlock(room->db, event_ref);
|
DbUnlock(room->db, event_ref);
|
||||||
Free(safe_id);
|
Free(safe_id);
|
||||||
|
Free(prev_id);
|
||||||
|
|
||||||
/* Only accepted PDUs get to do the news */
|
/* Only accepted PDUs get to do the news */
|
||||||
if (status == PDUV1_STATUS_ACCEPTED)
|
if (status == PDUV1_STATUS_ACCEPTED)
|
||||||
|
@ -317,13 +327,20 @@ RoomAddEventV1(Room *room, PduV1 pdu, PduV1Status status)
|
||||||
JsonValueAsObject(HashMapGet(pdu.content, "m.relates_to"));
|
JsonValueAsObject(HashMapGet(pdu.content, "m.relates_to"));
|
||||||
Relation rel = { 0 };
|
Relation rel = { 0 };
|
||||||
State *state;
|
State *state;
|
||||||
char *type, *state_key, *event_id;
|
char *type, *state_key, *event_id, *errp = NULL;
|
||||||
|
|
||||||
pdu_json = PduV1ToJson(&pdu);
|
pdu_json = PduV1ToJson(&pdu);
|
||||||
|
|
||||||
/* If we have a membership change, then add it to the
|
/* If we have a membership change, then add it to the
|
||||||
* proper table. */
|
* proper table. */
|
||||||
if (relates_to && RelationFromJson(relates_to, &rel, NULL))
|
{
|
||||||
|
CommonID *id = UserIdParse(pdu.sender, NULL);
|
||||||
|
User *user = UserLockID(room->db, id);
|
||||||
|
UserPushEvent(user, pdu_json);
|
||||||
|
UserUnlock(user);
|
||||||
|
UserIdFree(id);
|
||||||
|
}
|
||||||
|
if (relates_to && RelationFromJson(relates_to, &rel, &errp))
|
||||||
{
|
{
|
||||||
DbRef *relate = DbLock(
|
DbRef *relate = DbLock(
|
||||||
room->db,
|
room->db,
|
||||||
|
@ -392,7 +409,7 @@ RoomAddEventV1(Room *room, PduV1 pdu, PduV1Status status)
|
||||||
);
|
);
|
||||||
JsonValueFree(HashMapSet(
|
JsonValueFree(HashMapSet(
|
||||||
DbJson(eventRef),
|
DbJson(eventRef),
|
||||||
"redacted_by", JsonValueString(pdu.redacts)
|
"redacted_by", JsonValueString(pdu.event_id)
|
||||||
));
|
));
|
||||||
DbUnlock(room->db, eventRef);
|
DbUnlock(room->db, eventRef);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ RouterBuild(void)
|
||||||
R("/_matrix/client/v3/auth/(.*)/fallback/web", RouteUiaFallback);
|
R("/_matrix/client/v3/auth/(.*)/fallback/web", RouteUiaFallback);
|
||||||
|
|
||||||
R("/_matrix/client/v3/capabilities", RouteCapabilities);
|
R("/_matrix/client/v3/capabilities", RouteCapabilities);
|
||||||
|
R("/_matrix/client/v3/devices", RouteDevices);
|
||||||
R("/_matrix/client/v3/login", RouteLogin);
|
R("/_matrix/client/v3/login", RouteLogin);
|
||||||
R("/_matrix/client/v3/logout", RouteLogout);
|
R("/_matrix/client/v3/logout", RouteLogout);
|
||||||
R("/_matrix/client/v3/logout/(all)", RouteLogout);
|
R("/_matrix/client/v3/logout/(all)", RouteLogout);
|
||||||
|
@ -76,6 +77,7 @@ RouterBuild(void)
|
||||||
|
|
||||||
R("/_matrix/client/v3/user/(.*)/filter", RouteFilter);
|
R("/_matrix/client/v3/user/(.*)/filter", RouteFilter);
|
||||||
R("/_matrix/client/v3/user/(.*)/filter/(.*)", RouteFilter);
|
R("/_matrix/client/v3/user/(.*)/filter/(.*)", RouteFilter);
|
||||||
|
R("/_matrix/client/v3/user/(.*)/account_data/(.*)", RouteLocalData);
|
||||||
|
|
||||||
R("/_matrix/client/v3/rooms/(.*)/send/(.*)/(.*)", RouteSendEvent);
|
R("/_matrix/client/v3/rooms/(.*)/send/(.*)/(.*)", RouteSendEvent);
|
||||||
R("/_matrix/client/v3/rooms/(.*)/redact/(.*)/(.*)", RouteRedact);
|
R("/_matrix/client/v3/rooms/(.*)/redact/(.*)/(.*)", RouteRedact);
|
||||||
|
@ -101,6 +103,7 @@ RouterBuild(void)
|
||||||
/* Spoofed endpoints, to be TODO'd */
|
/* Spoofed endpoints, to be TODO'd */
|
||||||
R("/_matrix/client/v3/keys/(query|upload)", RouteKeyQuery);
|
R("/_matrix/client/v3/keys/(query|upload)", RouteKeyQuery);
|
||||||
R("/_matrix/client/v3/pushrules", RoutePushrules);
|
R("/_matrix/client/v3/pushrules", RoutePushrules);
|
||||||
|
R("/_matrix/federation/v1/hierarchy/(.*)", RouteHierarchy);
|
||||||
|
|
||||||
|
|
||||||
/* Telodendria Admin API Routes */
|
/* Telodendria Admin API Routes */
|
||||||
|
|
|
@ -84,12 +84,12 @@ ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
||||||
switch (HttpRequestMethodGet(args->context))
|
switch (HttpRequestMethodGet(args->context))
|
||||||
{
|
{
|
||||||
case HTTP_GET:
|
case HTTP_GET:
|
||||||
val = JsonGet(aliases, 2, "alias", alias);
|
val = JsonGet(aliases, 2, "aliases", alias);
|
||||||
if (val)
|
if (val)
|
||||||
{
|
{
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
HashMapSet(response, "room_id", JsonValueDuplicate(HashMapGet(JsonValueAsObject(val), "id")));
|
HashMapSet(response, "room_id", JsonValueDuplicate(HashMapGet(JsonValueAsObject(val), "id")));
|
||||||
HashMapSet(response, "servers", JsonValueDuplicate(JsonGet(aliases, 3, "alias", alias, "servers")));
|
HashMapSet(response, "servers", JsonValueDuplicate(JsonGet(aliases, 3, "aliases", alias, "servers")));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -99,6 +99,13 @@ ROUTE_IMPL(RouteCreateRoom, path, argp)
|
||||||
response = MatrixErrorCreate(M_BAD_JSON, err);
|
response = MatrixErrorCreate(M_BAD_JSON, err);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
if (parsed.room_id && !(UserGetPrivileges(user) & USER_ALIAS))
|
||||||
|
{
|
||||||
|
err = "Custom room ID used without ALIAS privileges.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
response = MatrixErrorCreate(M_UNAUTHORIZED, err);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
/* No longer need this now that it is parsed */
|
/* No longer need this now that it is parsed */
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
|
|
105
src/Routes/RouteDevices.c
Normal file
105
src/Routes/RouteDevices.c
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> with
|
||||||
|
* other valuable contributors. See CONTRIBUTORS.txt for the full list.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation files
|
||||||
|
* (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <Routes.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <User.h>
|
||||||
|
|
||||||
|
ROUTE_IMPL(RouteDevices, path, argp)
|
||||||
|
{
|
||||||
|
RouteArgs *args = argp;
|
||||||
|
HashMap *response = NULL;
|
||||||
|
HashMap *devices;
|
||||||
|
|
||||||
|
char *tokenstr, *deviceID;
|
||||||
|
JsonValue *deviceVal;
|
||||||
|
|
||||||
|
char *msg;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
Array *responseDevices;
|
||||||
|
|
||||||
|
Db *db = args->matrixArgs->db;
|
||||||
|
|
||||||
|
User *user;
|
||||||
|
|
||||||
|
if (HttpRequestMethodGet(args->context) != HTTP_GET)
|
||||||
|
{
|
||||||
|
msg = "This route only accepts GET.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
return MatrixErrorCreate(M_UNRECOGNIZED, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
response = MatrixGetAccessToken(args->context, &tokenstr);
|
||||||
|
if (response)
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = UserAuthenticate(db, tokenstr);
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
return MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
|
||||||
|
}
|
||||||
|
if (!(devices = UserGetDevices(user)))
|
||||||
|
{
|
||||||
|
msg = "Couldn't get user's devices. This is not right.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
response = MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = HashMapCreate();
|
||||||
|
HashMapSet(response,
|
||||||
|
"devices", JsonValueArray((responseDevices = ArrayCreate()))
|
||||||
|
);
|
||||||
|
i = 0;
|
||||||
|
while (HashMapIterateReentrant(devices, &deviceID, (void **) &deviceVal, &i))
|
||||||
|
{
|
||||||
|
HashMap *deviceInfo = HashMapCreate();
|
||||||
|
HashMap *deviceObj = JsonValueAsObject(deviceVal);
|
||||||
|
|
||||||
|
JsonValue *potentialDisplay = HashMapGet(deviceObj, "displayName");
|
||||||
|
|
||||||
|
HashMapSet(deviceInfo, "device_id", JsonValueString(deviceID));
|
||||||
|
HashMapSet(deviceInfo, "display_name", JsonValueDuplicate(potentialDisplay));
|
||||||
|
/* TODO: As of now, Telodendria does not store enough information to figure out
|
||||||
|
* last timestamp and IP address. */
|
||||||
|
|
||||||
|
ArrayAdd(responseDevices, JsonValueObject(deviceInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We do not need to free the device object as it lives with the user */
|
||||||
|
finish:
|
||||||
|
UserUnlock(user);
|
||||||
|
(void) path;
|
||||||
|
return response;
|
||||||
|
}
|
|
@ -106,7 +106,7 @@ ROUTE_IMPL(RouteFetchEvent, path, argp)
|
||||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
event = RoomEventFetch(room, eventId);
|
event = RoomEventFetch(room, eventId, true);
|
||||||
|
|
||||||
response = RoomEventClientify(event);
|
response = RoomEventClientify(event);
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
84
src/Routes/RouteHierarchy.c
Normal file
84
src/Routes/RouteHierarchy.c
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> with
|
||||||
|
* other valuable contributors. See CONTRIBUTORS.txt for the full list.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation files
|
||||||
|
* (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <Cytoplasm/HttpServer.h>
|
||||||
|
#include <Routes.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <User.h>
|
||||||
|
#include <Room.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Schema/Filter.h>
|
||||||
|
|
||||||
|
|
||||||
|
ROUTE_IMPL(RouteHierarchy, path, argp)
|
||||||
|
{
|
||||||
|
RouteArgs *args = argp;
|
||||||
|
Db *db = args->matrixArgs->db;
|
||||||
|
|
||||||
|
HashMap *request = NULL;
|
||||||
|
HashMap *response = NULL;
|
||||||
|
|
||||||
|
User *user = NULL;
|
||||||
|
char *token = NULL;
|
||||||
|
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if (HttpRequestMethodGet(args->context) != HTTP_GET)
|
||||||
|
{
|
||||||
|
err = "Unknown request method.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_UNRECOGNIZED, err);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = MatrixGetAccessToken(args->context, &token);
|
||||||
|
if (response)
|
||||||
|
{
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = UserAuthenticate(db, token);
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = HashMapCreate();
|
||||||
|
HashMapSet(response, "children", JsonValueArray(ArrayCreate()));
|
||||||
|
HashMapSet(response, "inaccessible_children", JsonValueArray(ArrayCreate()));
|
||||||
|
HashMapSet(response, "room", JsonValueObject(HashMapCreate()));
|
||||||
|
(void) path;
|
||||||
|
finish:
|
||||||
|
JsonFree(request);
|
||||||
|
UserUnlock(user);
|
||||||
|
return response;
|
||||||
|
}
|
88
src/Routes/RouteKeyManagement.c
Normal file
88
src/Routes/RouteKeyManagement.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> with
|
||||||
|
* other valuable contributors. See CONTRIBUTORS.txt for the full list.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation files
|
||||||
|
* (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <Cytoplasm/HttpServer.h>
|
||||||
|
#include <Routes.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <User.h>
|
||||||
|
#include <Room.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Schema/Filter.h>
|
||||||
|
|
||||||
|
|
||||||
|
ROUTE_IMPL(RouteKeyQuery, path, argp)
|
||||||
|
{
|
||||||
|
RouteArgs *args = argp;
|
||||||
|
Db *db = args->matrixArgs->db;
|
||||||
|
|
||||||
|
HashMap *request = NULL;
|
||||||
|
HashMap *response = NULL;
|
||||||
|
|
||||||
|
User *user = NULL;
|
||||||
|
char *token = NULL;
|
||||||
|
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
||||||
|
{
|
||||||
|
err = "Unknown request method.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_UNRECOGNIZED, err);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = MatrixGetAccessToken(args->context, &token);
|
||||||
|
if (response)
|
||||||
|
{
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = UserAuthenticate(db, token);
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
request = JsonDecode(HttpServerStream(args->context));
|
||||||
|
if (!request)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = HashMapCreate();
|
||||||
|
(void) path;
|
||||||
|
finish:
|
||||||
|
JsonFree(request);
|
||||||
|
UserUnlock(user);
|
||||||
|
return response;
|
||||||
|
}
|
158
src/Routes/RouteLocalData.c
Normal file
158
src/Routes/RouteLocalData.c
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> with
|
||||||
|
* other valuable contributors. See CONTRIBUTORS.txt for the full list.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation files
|
||||||
|
* (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <Routes.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Config.h>
|
||||||
|
#include <User.h>
|
||||||
|
|
||||||
|
#include <Schema/Filter.h>
|
||||||
|
|
||||||
|
ROUTE_IMPL(RouteLocalData, path, argp)
|
||||||
|
{
|
||||||
|
RouteArgs *args = argp;
|
||||||
|
Db *db = args->matrixArgs->db;
|
||||||
|
|
||||||
|
HashMap *request = NULL;
|
||||||
|
HashMap *response = NULL;
|
||||||
|
|
||||||
|
User *user = NULL;
|
||||||
|
CommonID *id = NULL;
|
||||||
|
char *token = NULL;
|
||||||
|
|
||||||
|
char *serverName = NULL;
|
||||||
|
|
||||||
|
char *userParam = ArrayGet(path, 0);
|
||||||
|
char *dataKey = ArrayGet(path, 1);
|
||||||
|
|
||||||
|
char *msg;
|
||||||
|
|
||||||
|
if (!userParam || !dataKey)
|
||||||
|
{
|
||||||
|
/* Should be impossible */
|
||||||
|
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
return MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
serverName = ConfigGetServerName(db);
|
||||||
|
if (!serverName)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
response = MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = UserIdParse(userParam, serverName);
|
||||||
|
if (!id)
|
||||||
|
{
|
||||||
|
msg = "Invalid user ID.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ParserServerNameEquals(id->server, serverName))
|
||||||
|
{
|
||||||
|
msg = "Cannot use /filter for non-local users.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
response = MatrixErrorCreate(M_UNAUTHORIZED, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = MatrixGetAccessToken(args->context, &token);
|
||||||
|
if (response)
|
||||||
|
{
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = UserAuthenticate(db, token);
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StrEquals(id->local, UserGetName(user)))
|
||||||
|
{
|
||||||
|
msg = "Unauthorized to use /account_data.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (HttpRequestMethodGet(args->context))
|
||||||
|
{
|
||||||
|
case HTTP_GET:
|
||||||
|
{
|
||||||
|
HashMap *accountData = UserGetAccountData(user, dataKey);
|
||||||
|
if (!accountData)
|
||||||
|
{
|
||||||
|
msg = "Couldn't find account data.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
|
||||||
|
response = MatrixErrorCreate(M_NOT_FOUND, msg);
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
}
|
||||||
|
response = accountData;
|
||||||
|
}; break;
|
||||||
|
case HTTP_PUT:
|
||||||
|
{
|
||||||
|
if (StrEquals(dataKey, "m.fully_read"))
|
||||||
|
{
|
||||||
|
msg = "Cannot use protected data key.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_BAD_JSON, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
request = JsonDecode(HttpServerStream(args->context));
|
||||||
|
if (!request)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
UserSetAccountData(user, dataKey, request);
|
||||||
|
response = HashMapCreate();
|
||||||
|
}; break;
|
||||||
|
default:
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_UNRECOGNIZED, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
Free(serverName);
|
||||||
|
UserIdFree(id);
|
||||||
|
UserUnlock(user);
|
||||||
|
JsonFree(request);
|
||||||
|
return response;
|
||||||
|
}
|
|
@ -146,7 +146,7 @@ ROUTE_IMPL(RouteMembers, path, argp)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAllowed((event = RoomEventFetch(room, entry)), params))
|
if (IsAllowed((event = RoomEventFetch(room, entry, true)), params))
|
||||||
{
|
{
|
||||||
ArrayAdd(batch, JsonValueObject(event));
|
ArrayAdd(batch, JsonValueObject(event));
|
||||||
event = NULL;
|
event = NULL;
|
||||||
|
|
|
@ -77,53 +77,3 @@ finish:
|
||||||
UserUnlock(user);
|
UserUnlock(user);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
ROUTE_IMPL(RouteKeyQuery, path, argp)
|
|
||||||
{
|
|
||||||
RouteArgs *args = argp;
|
|
||||||
Db *db = args->matrixArgs->db;
|
|
||||||
|
|
||||||
HashMap *request = NULL;
|
|
||||||
HashMap *response = NULL;
|
|
||||||
|
|
||||||
User *user = NULL;
|
|
||||||
char *token = NULL;
|
|
||||||
|
|
||||||
char *err;
|
|
||||||
|
|
||||||
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
|
||||||
{
|
|
||||||
err = "Unknown request method.";
|
|
||||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
|
||||||
response = MatrixErrorCreate(M_UNRECOGNIZED, err);
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
response = MatrixGetAccessToken(args->context, &token);
|
|
||||||
if (response)
|
|
||||||
{
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = UserAuthenticate(db, token);
|
|
||||||
if (!user)
|
|
||||||
{
|
|
||||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
|
||||||
response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
request = JsonDecode(HttpServerStream(args->context));
|
|
||||||
if (!request)
|
|
||||||
{
|
|
||||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
|
||||||
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
response = HashMapCreate();
|
|
||||||
(void) path;
|
|
||||||
finish:
|
|
||||||
JsonFree(request);
|
|
||||||
UserUnlock(user);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
|
@ -225,7 +225,9 @@ ROUTE_IMPL(RouteSendState, path, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
state = StateCurrent(room);
|
state = StateCurrent(room);
|
||||||
event = RoomEventFetch(room, StateGet(state, eventType, stateKey));
|
event = RoomEventFetch(
|
||||||
|
room, StateGet(state, eventType, stateKey), true
|
||||||
|
);
|
||||||
if (!event)
|
if (!event)
|
||||||
{
|
{
|
||||||
err = "Event could not be found.";
|
err = "Event could not be found.";
|
||||||
|
@ -271,6 +273,7 @@ ROUTE_IMPL(RouteSendState, path, argp)
|
||||||
|
|
||||||
if (!filled)
|
if (!filled)
|
||||||
{
|
{
|
||||||
|
Log(LOG_ERR, "%s", err);
|
||||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
|
@ -69,7 +69,7 @@ ROUTE_IMPL(RouteStaticResources, path, argp)
|
||||||
"function jsonRequest(meth, url, json, cb) {"
|
"function jsonRequest(meth, url, json, cb) {"
|
||||||
" var xhr = new XMLHttpRequest();"
|
" var xhr = new XMLHttpRequest();"
|
||||||
" xhr.open(meth, url);"
|
" xhr.open(meth, url);"
|
||||||
" xhr.setRequestHeader('Content-Type', 'application/json');"
|
" xhr.setRequestHeader('Content-Type', 'application/json');"
|
||||||
" xhr.onreadystatechange = () => {"
|
" xhr.onreadystatechange = () => {"
|
||||||
" if (xhr.readyState == 4) {"
|
" if (xhr.readyState == 4) {"
|
||||||
" cb(xhr);"
|
" cb(xhr);"
|
||||||
|
|
|
@ -70,6 +70,7 @@ ROUTE_IMPL(RouteSync, path, argp)
|
||||||
SyncResponse sync = { 0 };
|
SyncResponse sync = { 0 };
|
||||||
Filter *filterData = NULL;
|
Filter *filterData = NULL;
|
||||||
|
|
||||||
|
Array *accountData;
|
||||||
Array *invites;
|
Array *invites;
|
||||||
Array *joins;
|
Array *joins;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -149,6 +150,20 @@ ROUTE_IMPL(RouteSync, path, argp)
|
||||||
* a hashmap of unknown keys pointing to a known type. */
|
* a hashmap of unknown keys pointing to a known type. */
|
||||||
sync.rooms.invite = HashMapCreate();
|
sync.rooms.invite = HashMapCreate();
|
||||||
sync.rooms.join = HashMapCreate();
|
sync.rooms.join = HashMapCreate();
|
||||||
|
sync.account_data.events = ArrayCreate();
|
||||||
|
|
||||||
|
/* account data */
|
||||||
|
accountData = UserGetAccountDataSync(user, currBatch);
|
||||||
|
for (i = 0; i < ArraySize(accountData); i++)
|
||||||
|
{
|
||||||
|
char *key = ArrayGet(accountData, i);
|
||||||
|
Event *event = Malloc(sizeof(*event));
|
||||||
|
event->type = StrDuplicate(key);
|
||||||
|
event->content = UserGetAccountData(user, key);
|
||||||
|
|
||||||
|
ArrayAdd(sync.account_data.events, event);
|
||||||
|
}
|
||||||
|
UserFreeList(accountData);
|
||||||
|
|
||||||
/* invites */
|
/* invites */
|
||||||
invites = UserGetInvites(user, currBatch);
|
invites = UserGetInvites(user, currBatch);
|
||||||
|
@ -201,7 +216,7 @@ ROUTE_IMPL(RouteSync, path, argp)
|
||||||
for (j = 0; j < ArraySize(el); j++)
|
for (j = 0; j < ArraySize(el); j++)
|
||||||
{
|
{
|
||||||
char *event = ArrayGet(el, j);
|
char *event = ArrayGet(el, j);
|
||||||
HashMap *eventObj = RoomEventFetch(r, event);
|
HashMap *eventObj = RoomEventFetch(r, event, true);
|
||||||
HashMap *filteredObj = FilterApply(filterData, eventObj);
|
HashMap *filteredObj = FilterApply(filterData, eventObj);
|
||||||
|
|
||||||
if (filteredObj)
|
if (filteredObj)
|
||||||
|
@ -229,7 +244,7 @@ ROUTE_IMPL(RouteSync, path, argp)
|
||||||
joined->state.events = ArrayCreate();
|
joined->state.events = ArrayCreate();
|
||||||
while (StateIterate(state, &type, &key, (void **) &id))
|
while (StateIterate(state, &type, &key, (void **) &id))
|
||||||
{
|
{
|
||||||
HashMap *e = RoomEventFetch(r, id);
|
HashMap *e = RoomEventFetch(r, id, true);
|
||||||
StrippedStateEvent rs = StripStateEventSync(e);
|
StrippedStateEvent rs = StripStateEventSync(e);
|
||||||
StrippedStateEvent *s = Malloc(sizeof(*s));
|
StrippedStateEvent *s = Malloc(sizeof(*s));
|
||||||
memcpy(s, &rs, sizeof(*s));
|
memcpy(s, &rs, sizeof(*s));
|
||||||
|
@ -251,9 +266,9 @@ ROUTE_IMPL(RouteSync, path, argp)
|
||||||
|
|
||||||
if (prevBatch)
|
if (prevBatch)
|
||||||
{
|
{
|
||||||
|
/* TODO: Should we be dropping syncs? */
|
||||||
UserDropSync(user, prevBatch);
|
UserDropSync(user, prevBatch);
|
||||||
nextBatch = UserInitSyncDiff(user);
|
nextBatch = UserInitSyncDiff(user);
|
||||||
UserFillSyncDiff(user, nextBatch);
|
|
||||||
}
|
}
|
||||||
sync.next_batch = nextBatch;
|
sync.next_batch = nextBatch;
|
||||||
response = SyncResponseToJson(&sync);
|
response = SyncResponseToJson(&sync);
|
||||||
|
|
12
src/State.c
12
src/State.c
|
@ -153,7 +153,7 @@ BuildBaseAndConflictV1(Room *room, Array *states, State *R, HashMap *conflicts)
|
||||||
{
|
{
|
||||||
arr = ArrayCreate();
|
arr = ArrayCreate();
|
||||||
}
|
}
|
||||||
hm = RoomEventFetch(room, event_id);
|
hm = RoomEventFetch(room, event_id, false);
|
||||||
ArrayAdd(arr, hm);
|
ArrayAdd(arr, hm);
|
||||||
HashMapSet(conflicts, tuple, arr);
|
HashMapSet(conflicts, tuple, arr);
|
||||||
}
|
}
|
||||||
|
@ -367,6 +367,10 @@ StateResolve(Room * room, HashMap * event)
|
||||||
db = RoomGetDB(room);
|
db = RoomGetDB(room);
|
||||||
room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
||||||
event_id = JsonValueAsString(HashMapGet(event, "event_id"));
|
event_id = JsonValueAsString(HashMapGet(event, "event_id"));
|
||||||
|
if (!room_id || !event_id)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (DbExists(db, 4, "rooms", room_id, "state", event_id))
|
if (DbExists(db, 4, "rooms", room_id, "state", event_id))
|
||||||
{
|
{
|
||||||
DbRef *ref = DbLock(db, 4,
|
DbRef *ref = DbLock(db, 4,
|
||||||
|
@ -393,8 +397,10 @@ StateResolve(Room * room, HashMap * event)
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(prevEvents); i++)
|
for (i = 0; i < ArraySize(prevEvents); i++)
|
||||||
{
|
{
|
||||||
HashMap *prevEvent =
|
HashMap *prevEvent = RoomEventFetch(
|
||||||
RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i)));
|
room, JsonValueAsString(ArrayGet(prevEvents, i)),
|
||||||
|
false
|
||||||
|
);
|
||||||
State *state = StateResolve(room, prevEvent);
|
State *state = StateResolve(room, prevEvent);
|
||||||
|
|
||||||
if (HashMapGet(prevEvent, "state_key") && !IsRejected(prevEvent))
|
if (HashMapGet(prevEvent, "state_key") && !IsRejected(prevEvent))
|
||||||
|
|
150
src/User.c
150
src/User.c
|
@ -1249,6 +1249,7 @@ UserInitSyncDiff(User *user)
|
||||||
|
|
||||||
HashMapSet(data, "nextBatch", JsonValueString(nextBatch));
|
HashMapSet(data, "nextBatch", JsonValueString(nextBatch));
|
||||||
HashMapSet(data, "invites", JsonValueArray(ArrayCreate()));
|
HashMapSet(data, "invites", JsonValueArray(ArrayCreate()));
|
||||||
|
HashMapSet(data, "account_data", JsonValueObject(HashMapCreate()));
|
||||||
HashMapSet(data, "leaves", JsonValueArray(ArrayCreate()));
|
HashMapSet(data, "leaves", JsonValueArray(ArrayCreate()));
|
||||||
HashMapSet(data, "joins", JsonValueObject(HashMapCreate()));
|
HashMapSet(data, "joins", JsonValueObject(HashMapCreate()));
|
||||||
|
|
||||||
|
@ -1288,6 +1289,45 @@ UserPushInviteSync(User *user, char *roomId)
|
||||||
UserNotifyUser(user->name);
|
UserNotifyUser(user->name);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
|
UserPushAccountData(User *user, char *key)
|
||||||
|
{
|
||||||
|
DbRef *syncRef;
|
||||||
|
HashMap *data;
|
||||||
|
Array *entries;
|
||||||
|
HashMap *join;
|
||||||
|
size_t i;
|
||||||
|
if (!user || !key)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = DbList(user->db, 3, "users", user->name, "sync");
|
||||||
|
for (i = 0; i < ArraySize(entries); i++)
|
||||||
|
{
|
||||||
|
char *entry = ArrayGet(entries, i);
|
||||||
|
HashMap *accountEntry;
|
||||||
|
syncRef = DbLock(user->db, 4, "users", user->name, "sync", entry);
|
||||||
|
data = DbJson(syncRef);
|
||||||
|
join = JsonValueAsObject(HashMapGet(data, "account_data"));
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
|
||||||
|
if (!HashMapGet(join, key))
|
||||||
|
{
|
||||||
|
accountEntry = HashMapCreate();
|
||||||
|
JsonValueFree(HashMapSet(join,
|
||||||
|
key, JsonValueObject(accountEntry)
|
||||||
|
));
|
||||||
|
Log(LOG_INFO, "user=%s's batch=%s", user->name, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
DbUnlock(user->db, syncRef);
|
||||||
|
}
|
||||||
|
DbListFree(entries);
|
||||||
|
Log(LOG_INFO, "You have notified the '%s' about key=%s", user->name, key);
|
||||||
|
UserNotifyUser(user->name);
|
||||||
|
}
|
||||||
|
void
|
||||||
UserPushJoinSync(User *user, char *roomId)
|
UserPushJoinSync(User *user, char *roomId)
|
||||||
{
|
{
|
||||||
DbRef *syncRef;
|
DbRef *syncRef;
|
||||||
|
@ -1413,7 +1453,7 @@ UserGetInvites(User *user, char *batch)
|
||||||
void
|
void
|
||||||
UserFillSyncDiff(User *user, char *batch)
|
UserFillSyncDiff(User *user, char *batch)
|
||||||
{
|
{
|
||||||
Array *joins;
|
Array *joins, *data;
|
||||||
size_t i;
|
size_t i;
|
||||||
DbRef *syncRef;
|
DbRef *syncRef;
|
||||||
if (!user || !batch)
|
if (!user || !batch)
|
||||||
|
@ -1421,6 +1461,8 @@ UserFillSyncDiff(User *user, char *batch)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log(LOG_WARNING, "This is an initial sync.");
|
||||||
|
|
||||||
joins = UserListJoins(user);
|
joins = UserListJoins(user);
|
||||||
syncRef = DbLock(
|
syncRef = DbLock(
|
||||||
user->db, 4, "users", user->name, "sync", batch
|
user->db, 4, "users", user->name, "sync", batch
|
||||||
|
@ -1459,9 +1501,58 @@ UserFillSyncDiff(User *user, char *batch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UserFreeList(joins);
|
UserFreeList(joins);
|
||||||
|
|
||||||
|
data = DbList(user->db, 3, "users", user->name, "data");
|
||||||
|
for (i = 0; i < ArraySize(data); i++)
|
||||||
|
{
|
||||||
|
char *id = ArrayGet(data, i);
|
||||||
|
DbRef *ref = DbLock(user->db, 4, "users", user->name, "data", id);
|
||||||
|
char *key = JsonValueAsString(HashMapGet(DbJson(ref), "key"));
|
||||||
|
HashMap *obj = DbJson(syncRef);
|
||||||
|
HashMap *aData = JsonValueAsObject(HashMapGet(obj, "account_data"));
|
||||||
|
|
||||||
|
JsonValueFree(HashMapSet(aData,
|
||||||
|
key, JsonValueObject(HashMapCreate())
|
||||||
|
));
|
||||||
|
|
||||||
|
DbUnlock(user->db, ref);
|
||||||
|
}
|
||||||
|
DbListFree(data);
|
||||||
DbUnlock(user->db, syncRef);
|
DbUnlock(user->db, syncRef);
|
||||||
}
|
}
|
||||||
Array *
|
Array *
|
||||||
|
UserGetAccountDataSync(User *user, char *batch)
|
||||||
|
{
|
||||||
|
Db *db;
|
||||||
|
DbRef *syncRef;
|
||||||
|
HashMap *data;
|
||||||
|
Array *keys;
|
||||||
|
size_t i;
|
||||||
|
if (!user || !batch)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
db = user->db;
|
||||||
|
syncRef = DbLock(db, 4, "users", user->name, "sync", batch);
|
||||||
|
if (!syncRef)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = DbJson(syncRef);
|
||||||
|
|
||||||
|
keys = HashMapKeys(JsonValueAsObject(HashMapGet(data, "account_data")));
|
||||||
|
for (i = 0; i < ArraySize(keys); i++)
|
||||||
|
{
|
||||||
|
char *str = ArrayGet(keys, i);
|
||||||
|
ArraySet(keys, i, StrDuplicate(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
DbUnlock(db, syncRef);
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
Array *
|
||||||
UserGetJoins(User *user, char *batch)
|
UserGetJoins(User *user, char *batch)
|
||||||
{
|
{
|
||||||
Db *db;
|
Db *db;
|
||||||
|
@ -1647,7 +1738,7 @@ UserFetchMessages(User *user, int n, char *token, char **next)
|
||||||
for (i = 0; i < (size_t) n && ArraySize(nexts); i++)
|
for (i = 0; i < (size_t) n && ArraySize(nexts); i++)
|
||||||
{
|
{
|
||||||
char *curr = ArrayDelete(nexts, ArraySize(nexts) - 1);
|
char *curr = ArrayDelete(nexts, ArraySize(nexts) - 1);
|
||||||
HashMap *event = RoomEventFetch(room, curr);
|
HashMap *event = RoomEventFetch(room, curr, true);
|
||||||
Array *prevEvents;
|
Array *prevEvents;
|
||||||
size_t j;
|
size_t j;
|
||||||
bool toFree = true;
|
bool toFree = true;
|
||||||
|
@ -1772,7 +1863,7 @@ UserIsSyncOld(User *user, char *token)
|
||||||
dt = UtilTsMillis() - JsonValueAsInteger(HashMapGet(map, "creation"));
|
dt = UtilTsMillis() - JsonValueAsInteger(HashMapGet(map, "creation"));
|
||||||
|
|
||||||
DbUnlock(user->db, ref);
|
DbUnlock(user->db, ref);
|
||||||
return dt > (5 * 60 * 1000); /* 5-minutes of timeout. */
|
return dt > (3 * 24 * 60 * 60 * 1000); /* Three days of timeout. */
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
UserSyncExists(User *user, char *sync)
|
UserSyncExists(User *user, char *sync)
|
||||||
|
@ -2014,3 +2105,56 @@ UserNotifyAll(Db *db)
|
||||||
}
|
}
|
||||||
DbListFree(list);
|
DbListFree(list);
|
||||||
}
|
}
|
||||||
|
HashMap *
|
||||||
|
UserGetAccountData(User *user, char *key)
|
||||||
|
{
|
||||||
|
DbRef *ref;
|
||||||
|
HashMap *ret;
|
||||||
|
if (!user || !key)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbLockIntent(user->db, DB_HINT_READONLY,
|
||||||
|
4, "users", user->name, "data", key
|
||||||
|
);
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = JsonDuplicate(JsonValueAsObject(HashMapGet(DbJson(ref), "content")));
|
||||||
|
DbUnlock(user->db, ref);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UserSetAccountData(User *user, char *key, HashMap *obj)
|
||||||
|
{
|
||||||
|
DbRef *ref;
|
||||||
|
if (!user || !key || !obj)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbLock(user->db,
|
||||||
|
4, "users", user->name, "data", key
|
||||||
|
);
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
ref = DbCreate(user->db,
|
||||||
|
4, "users", user->name, "data", key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonValueFree(HashMapSet(DbJson(ref), "content", JsonValueObject(JsonDuplicate(obj))));
|
||||||
|
JsonValueFree(HashMapSet(DbJson(ref), "key", JsonValueString(key)));
|
||||||
|
DbUnlock(user->db, ref);
|
||||||
|
|
||||||
|
UserPushAccountData(user, key);
|
||||||
|
}
|
||||||
|
|
|
@ -154,9 +154,18 @@ extern bool RoomIsEventVisible(Room *, User *, char *);
|
||||||
* Fetch a single event's PDU in a room into an
|
* Fetch a single event's PDU in a room into an
|
||||||
* hashmap, given an event ID, from the database
|
* hashmap, given an event ID, from the database
|
||||||
* if possible, or otherwise fetched from a remote
|
* if possible, or otherwise fetched from a remote
|
||||||
* homeserver participating in the room.
|
* homeserver participating in the room. If the boolean
|
||||||
|
* value is set, then the prev_content is set.
|
||||||
*/
|
*/
|
||||||
extern HashMap * RoomEventFetch(Room *, char *);
|
extern HashMap * RoomEventFetch(Room *, char *, bool);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behaves like
|
||||||
|
* .Fn RoomEventFetch ,
|
||||||
|
* but avoids doing any postprocessing.
|
||||||
|
*/
|
||||||
|
extern HashMap * RoomEventFetchRaw(Room *, char *);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strips all the fields not required in a
|
* Strips all the fields not required in a
|
||||||
|
|
|
@ -77,6 +77,7 @@ ROUTE(RouteVersions);
|
||||||
ROUTE(RouteWellKnown);
|
ROUTE(RouteWellKnown);
|
||||||
|
|
||||||
ROUTE(RouteCapabilities);
|
ROUTE(RouteCapabilities);
|
||||||
|
ROUTE(RouteDevices);
|
||||||
ROUTE(RouteLogin);
|
ROUTE(RouteLogin);
|
||||||
ROUTE(RouteLogout);
|
ROUTE(RouteLogout);
|
||||||
ROUTE(RouteRegister);
|
ROUTE(RouteRegister);
|
||||||
|
@ -95,6 +96,7 @@ ROUTE(RouteStaticLogin);
|
||||||
ROUTE(RouteStaticResources);
|
ROUTE(RouteStaticResources);
|
||||||
|
|
||||||
ROUTE(RouteFilter);
|
ROUTE(RouteFilter);
|
||||||
|
ROUTE(RouteLocalData);
|
||||||
|
|
||||||
ROUTE(RouteProcControl);
|
ROUTE(RouteProcControl);
|
||||||
ROUTE(RouteConfig);
|
ROUTE(RouteConfig);
|
||||||
|
@ -122,6 +124,7 @@ ROUTE(RouteAdminTokens);
|
||||||
|
|
||||||
ROUTE(RouteKeyQuery);
|
ROUTE(RouteKeyQuery);
|
||||||
ROUTE(RoutePushrules);
|
ROUTE(RoutePushrules);
|
||||||
|
ROUTE(RouteHierarchy);
|
||||||
#undef ROUTE
|
#undef ROUTE
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -67,7 +67,7 @@ typedef enum UserPrivileges
|
||||||
USER_PROC_CONTROL = (1 << 4),
|
USER_PROC_CONTROL = (1 << 4),
|
||||||
USER_ALIAS = (1 << 5),
|
USER_ALIAS = (1 << 5),
|
||||||
USER_APPSERVICE = (1 << 6),
|
USER_APPSERVICE = (1 << 6),
|
||||||
USER_ALL = ((1 << 7) - 1)
|
USER_ALL = ((1 << 7) - 1),
|
||||||
} UserPrivileges;
|
} UserPrivileges;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,6 +397,12 @@ extern void UserPushJoinSync(User *, char *);
|
||||||
*/
|
*/
|
||||||
extern void UserPushEvent(User *, HashMap *);
|
extern void UserPushEvent(User *, HashMap *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a global account data key into every diff table
|
||||||
|
* of a user.
|
||||||
|
*/
|
||||||
|
extern void UserPushAccountData(User *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the invite list(as a room ID table) for an user
|
* Shows the invite list(as a room ID table) for an user
|
||||||
* and a specified diff table, to be freed by
|
* and a specified diff table, to be freed by
|
||||||
|
@ -404,6 +410,13 @@ extern void UserPushEvent(User *, HashMap *);
|
||||||
*/
|
*/
|
||||||
extern Array * UserGetInvites(User *, char *);
|
extern Array * UserGetInvites(User *, char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the global account data list for an user
|
||||||
|
* and a specified diff table, to be freed by
|
||||||
|
* .Fn UserFreeList .
|
||||||
|
*/
|
||||||
|
extern Array * UserGetAccountDataSync(User *, char *);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a list of rooms for an user and a specified diff
|
* Shows a list of rooms for an user and a specified diff
|
||||||
|
@ -412,6 +425,7 @@ extern Array * UserGetInvites(User *, char *);
|
||||||
*/
|
*/
|
||||||
extern Array * UserGetJoins(User *, char *);
|
extern Array * UserGetJoins(User *, char *);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of event IDs for a diff table(and room ID),
|
* Get a list of event IDs for a diff table(and room ID),
|
||||||
* to be freed by
|
* to be freed by
|
||||||
|
@ -497,4 +511,15 @@ extern bool UserAwaitNotification(char *, int);
|
||||||
* .Fn UserInitialisePushTable .
|
* .Fn UserInitialisePushTable .
|
||||||
*/
|
*/
|
||||||
extern void UserDestroyPushTable(void);
|
extern void UserDestroyPushTable(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a user's account data by key into a JSON object living
|
||||||
|
* on the heap.
|
||||||
|
*/
|
||||||
|
extern HashMap * UserGetAccountData(User *, char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces an account data entry.
|
||||||
|
*/
|
||||||
|
extern void UserSetAccountData(User *, char *, HashMap *);
|
||||||
#endif /* TELODENDRIA_USER_H */
|
#endif /* TELODENDRIA_USER_H */
|
||||||
|
|
Loading…
Reference in a new issue