[ADD/WIP] Float checks, /members, GETting state pieces

This commit is contained in:
LDA 2024-10-30 01:55:59 +01:00
parent f279ead4d7
commit ebbca383cb
9 changed files with 306 additions and 73 deletions

View file

@ -509,3 +509,32 @@ MatrixSetJSON(HashMap *json, char *string, JsonValue *new)
}
return retVal;
}
bool
MatrixCheckFloats(HashMap *obj)
{
size_t i;
char *key;
JsonValue *value;
if (!obj)
{
return false;
}
i = 0;
while (HashMapIterateReentrant(obj, &key, (void **) &value, &i))
{
if (JsonValueType(value) == JSON_FLOAT)
{
return false;
}
else if (JsonValueType(value) == JSON_OBJECT)
{
if (!MatrixCheckFloats(JsonValueAsObject(value)))
{
return false;
}
}
}
return true;
}

View file

@ -44,6 +44,7 @@
#include <CanonicalJson.h>
#include <Config.h>
#include <Parser.h>
#include <Matrix.h>
#include <State.h>
#include <Event.h>
@ -239,6 +240,14 @@ RoomEventSend(Room * room, HashMap * event, char **errp)
{
return NULL;
}
if (!MatrixCheckFloats(event))
{
if (errp)
{
*errp = "Event contains forbidden float value";
}
return NULL;
}
if (room->version < 3)
{
/* Manage with PDUv1 */

View file

@ -5,22 +5,6 @@
#include <Config.h>
#include <State.h>
State *
RoomStateGet(Room * room)
{
HashMap *database_state;
if (!room)
{
return NULL;
}
/* TODO: Consider caching the deserialised result, as doing that on a
* large state would probably eat up a lot of time! */
/* TODO: Update the cached state */
database_state = DbJson(room->state_ref);
return StateDeserialise(database_state);
}
State *
RoomStateGetID(Room * room, char *event_id)
{

View file

@ -85,6 +85,7 @@ RouterBuild(void)
R("/_matrix/client/v3/rooms/(.*)/(join|leave)", RouteJoinRoom);
R("/_matrix/client/v3/rooms/(.*)/(kick|ban|unban)", RouteKickRoom);
R("/_matrix/client/v3/rooms/(.*)/messages", RouteMessages);
R("/_matrix/client/v3/rooms/(.*)/members", RouteMembers);
R("/_matrix/client/v3/join/(.*)", RouteJoinRoomAlias);

175
src/Routes/RouteMembers.c Normal file
View file

@ -0,0 +1,175 @@
/*
* 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 <Config.h>
#include <User.h>
#include <Room.h>
#include <string.h>
#include <stdlib.h>
#include <Schema/Filter.h>
static bool
IsAllowed(HashMap *e, HashMap *params)
{
char *not_membership = HashMapGet(params, "not_membership");
char *membership = HashMapGet(params, "membership");
char *e_membership = JsonValueAsString(JsonGet(e, 2, "content", "membership"));
return StrEquals(e_membership, membership) || !StrEquals(e_membership, not_membership);
}
ROUTE_IMPL(RouteMembers, path, argp)
{
RouteArgs *args = argp;
Db *db = args->matrixArgs->db;
HashMap *params = HttpRequestParams(args->context);
HashMap *response = NULL;
User *user = NULL;
char *token = NULL;
char *entryType, *entryKey, *entry;
char *serverName = NULL, *sender = NULL;
char *roomId = ArrayGet(path, 0);
char *at = HashMapGet(params, "at");
char *atId = NULL;
CommonID *id = NULL;
Array *messages = NULL, *batch;
Room *room = NULL;
State *state = NULL;
char *err;
if (!roomId)
{
/* Should be impossible */
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
return MatrixErrorCreate(M_UNKNOWN, NULL);
}
if (HttpRequestMethodGet(args->context) != HTTP_GET)
{
err = "Unknown request method.";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_UNRECOGNIZED, err);
goto finish;
}
serverName = ConfigGetServerName(db);
if (!serverName)
{
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
response = MatrixErrorCreate(M_UNKNOWN, NULL);
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;
}
id = UserIdParse(UserGetName(user), serverName);
id->sigil = '@';
sender = ParserRecomposeCommonID(*id);
messages = UserGetEvents(user, at, roomId);
atId = ArrayGet(messages, 0);
room = RoomLock(db, roomId);
if (!RoomContainsUser(room, sender))
{
err = "User is not in the room.";
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
response = MatrixErrorCreate(M_FORBIDDEN, err);
goto finish;
}
if (!atId)
{
state = StateCurrent(room);
}
else
{
state = RoomStateGetID(room, atId);
}
response = HashMapCreate();
batch = ArrayCreate();
HashMapSet(response, "batch", JsonValueArray(batch));
while (StateIterate(state, &entryType, &entryKey, (void **) &entry))
{
HashMap *event;
if (!StrEquals(entryType, "m.room.member"))
{
Free(entryType);
Free(entryKey);
continue;
}
if (IsAllowed((event = RoomEventFetch(room, entry)), params))
{
ArrayAdd(batch, JsonValueObject(event));
event = NULL;
}
Free(entryType);
Free(entryKey);
JsonFree(event);
}
StateFree(state);
RoomUnlock(room);
/* TODO: Filters, to, and friends */
finish:
UserUnlock(user);
UserIdFree(id);
UserFreeList(messages);
if (sender)
{
Free(sender);
}
if (serverName)
{
Free(serverName);
}
return response;
}

View file

@ -173,6 +173,7 @@ ROUTE_IMPL(RouteSendState, path, argp)
char *sender = NULL;
Room *room = NULL;
State *state = NULL;
char *err;
@ -182,20 +183,10 @@ ROUTE_IMPL(RouteSendState, path, argp)
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
return MatrixErrorCreate(M_UNKNOWN, NULL);
}
if (HttpRequestMethodGet(args->context) != HTTP_PUT)
{
err = "Unknown request method.";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_UNRECOGNIZED, err);
goto finish;
}
serverName = ConfigGetServerName(db);
if (!serverName)
if (!stateKey)
{
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
response = MatrixErrorCreate(M_UNKNOWN, NULL);
goto finish;
stateKey = "";
}
response = MatrixGetAccessToken(args->context, &token);
@ -203,6 +194,55 @@ ROUTE_IMPL(RouteSendState, path, argp)
{
goto finish;
}
user = UserAuthenticate(db, token);
if (!user)
{
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
goto finish;
}
serverName = ConfigGetServerName(db);
if (!serverName)
{
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
response = MatrixErrorCreate(M_UNKNOWN, NULL);
goto finish;
}
id = UserIdParse(UserGetName(user), serverName);
id->sigil = '@';
sender = ParserRecomposeCommonID(*id);
switch (HttpRequestMethodGet(args->context))
{
case HTTP_GET:
room = RoomLock(db, roomId);
if (!RoomContainsUser(room, sender))
{
err = "User is not in the room.";
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
response = MatrixErrorCreate(M_FORBIDDEN, err);
goto finish;
}
state = StateCurrent(room);
event = RoomEventFetch(room, StateGet(state, eventType, stateKey));
if (!event)
{
err = "Event could not be found.";
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_UNKNOWN, err);
StateFree(state);
goto finish;
}
response = JsonDuplicate(JsonValueAsObject(
HashMapGet(event, "content")
));
StateFree(state);
JsonFree(event);
break;
case HTTP_PUT:
request = JsonDecode(HttpServerStream(args->context));
if (!request)
@ -212,17 +252,6 @@ ROUTE_IMPL(RouteSendState, path, argp)
goto finish;
}
user = UserAuthenticate(db, token);
if (!user)
{
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
goto finish;
}
id = UserIdParse(UserGetName(user), serverName);
id->sigil = '@';
sender = ParserRecomposeCommonID(*id);
room = RoomLock(db, roomId);
if (!RoomContainsUser(room, sender))
{
@ -253,6 +282,13 @@ ROUTE_IMPL(RouteSendState, path, argp)
JsonValueDuplicate(HashMapGet(filled, "event_id"))
);
JsonFree(filled);
break;
default:
err = "Unknown request method.";
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_UNRECOGNIZED, err);
goto finish;
}
finish:
RoomUnlock(room);
Free(serverName);

View file

@ -170,4 +170,10 @@ extern JsonValue * MatrixGetJSON(HashMap *, char *);
*/
extern JsonValue * MatrixSetJSON(HashMap *, char *, JsonValue *);
/**
* Traverses the entire JSON object and verifies if no floating-point
* values are present within it.
*/
extern bool MatrixCheckFloats(HashMap *);
#endif

View file

@ -99,14 +99,6 @@ extern char * RoomIdGet(Room *);
*/
extern int RoomVersionGet(Room *);
/**
* Resolve the state for the latest events in the
* room. This function uses the appropriate state
* resolution algorithm to compute the latest state,
* which is used to select auth events on incoming
* client events.
*/
extern State * RoomStateGet(Room *);
/**
* Resolves the room's state before a specific point,
* (with the event hashmap taking priority),

View file

@ -111,6 +111,7 @@ ROUTE(RouteFetchEvent);
ROUTE(RouteJoinedRooms);
ROUTE(RouteSync);
ROUTE(RouteMessages);
ROUTE(RouteMembers);
ROUTE(RouteAliasDirectory);
ROUTE(RouteRoomAliases);