diff --git a/Cytoplasm b/Cytoplasm index 346b912..9108fef 160000 --- a/Cytoplasm +++ b/Cytoplasm @@ -1 +1 @@ -Subproject commit 346b912a0633cceac10780b8a103f6c89b5ba89f +Subproject commit 9108fef7018010e6b49a111856ca0553333804f8 diff --git a/src/Room.c b/src/Room.c index e1e79d7..20d262c 100644 --- a/src/Room.c +++ b/src/Room.c @@ -2054,3 +2054,43 @@ RoomContainsUser(Room *room, char *user) return ret; } +HashMap * +RoomEventClientify(HashMap *pdu) +{ + HashMap *event; + if (!pdu) + { + return NULL; + } + + event = HashMapCreate(); +#define CopyField(field, req) do { \ + JsonValue *f = HashMapGet(pdu, field);\ + if (!f && req) \ + { \ + goto failure; \ + } \ + HashMapSet( \ + event, \ + field, JsonValueDuplicate(f) \ + ); \ + } \ + while(0) + + CopyField("content", true); + CopyField("event_id", true); + CopyField("origin_server_ts", true); + CopyField("room_id", true); + CopyField("sender", true); + CopyField("type", true); + + CopyField("state_key",false); + CopyField("unsigned", false); + + return event; + +failure: + JsonFree(event); + return NULL; +#undef CopyField +} diff --git a/src/Routes.c b/src/Routes.c index bfea0c8..a55c19c 100644 --- a/src/Routes.c +++ b/src/Routes.c @@ -142,8 +142,9 @@ RouterBuild(void) R("/_matrix/client/v3/user/(.*)/filter", RouteFilter); R("/_matrix/client/v3/user/(.*)/filter/(.*)", RouteFilter); - R("/_matrix/client/v3/rooms/(.*)/send/(.*)/(.*)", RouteSendEvent); + R("/_matrix/client/v3/rooms/(.*)/send/(.*)/(.*)", RouteSendEvent); + R("/_matrix/client/v3/rooms/(.*)/event/(.*)", RouteFetchEvent); R("/_matrix/client/v3/createRoom", RouteCreateRoom); R("/_matrix/client/v3/directory/room/(.*)", RouteAliasDirectory); diff --git a/src/Routes/RouteFetchEvent.c b/src/Routes/RouteFetchEvent.c new file mode 100644 index 0000000..62f015b --- /dev/null +++ b/src/Routes/RouteFetchEvent.c @@ -0,0 +1,144 @@ +/* + * 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 + + +#include +#include +#include +#include + +#include +#include +#include + +#include + +static char * +GetServerName(Db * db) +{ + char *name; + + Config config; + + ConfigLock(db, &config); + if (!config.ok) + { + return NULL; + } + + name = StrDuplicate(config.serverName); + + ConfigUnlock(&config); + + return name; +} + +ROUTE_IMPL(RouteFetchEvent, path, argp) +{ + RouteArgs *args = argp; + Db *db = args->matrixArgs->db; + + HashMap *event = NULL; + HashMap *response = NULL; + + User *user = NULL; + char *token = NULL; + CommonID *id = NULL; + + char *roomId = ArrayGet(path, 0); + char *eventId= ArrayGet(path, 1); + char *sender = NULL, *serverName = NULL; + + Room *room = NULL; + + char *err; + + if (!roomId || !eventId) + { + /* 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 = GetServerName(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); + + /* TODO: Better auth(as in check m.room.history_visibility) */ + 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; + } + event = RoomEventFetch(room, eventId); + + response = RoomEventClientify(event); + JsonFree(event); +finish: + UserIdFree(id); + if (sender) + { + Free(sender); + } + if (serverName) + { + Free(serverName); + } + RoomUnlock(room); + UserUnlock(user); + return response; +} diff --git a/src/include/Room.h b/src/include/Room.h index 1136d86..95e8f81 100644 --- a/src/include/Room.h +++ b/src/include/Room.h @@ -158,6 +158,12 @@ extern void RoomSendInvite(User *, bool, char *, Room *); */ extern HashMap * RoomEventFetch(Room *, char *); +/** + * Strips all the fields not required in a + * Matrix ClientEvent from a PDU object. + */ +extern HashMap * RoomEventClientify(HashMap *); + /** * Verifies whenever an event(as a PDUv1) is * authorised by a room. diff --git a/src/include/Routes.h b/src/include/Routes.h index 99332c2..e0c328e 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -101,6 +101,7 @@ ROUTE(RoutePrivileges); ROUTE(RouteCreateRoom); ROUTE(RouteSendEvent); +ROUTE(RouteFetchEvent); ROUTE(RouteAliasDirectory); ROUTE(RouteRoomAliases);