diff --git a/src/Room.c b/src/Room.c index 335eefe..47fc903 100644 --- a/src/Room.c +++ b/src/Room.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -586,11 +585,11 @@ RoomMinPL(Room * room, HashMap *state, char *type, char *act) if (!type) { - val = JsonGet(pl, 1, act); + val = JsonGet(pl, 2, "content", act); } else { - val = JsonGet(pl, 2, type, act); + val = JsonGet(pl, 3, "content", type, act); } ret = ParsePL(val, def); @@ -614,7 +613,7 @@ RoomUserPL(Room * room, HashMap *state, char *user) PrepareState(room, state, "m.room.power_levels", "", pl); def = RoomMinPL(room, state, NULL, "users_default"); - ret = ParsePL(JsonGet(pl, 2, "users", user), def); + ret = ParsePL(JsonGet(pl, 3, "content", "users", user), def); finish: FinishState(pl); @@ -751,7 +750,8 @@ ValidAuthEventV1(PduV1 *auth_pdu, PduV1 *pdu) JsonValueAsString(JsonGet(pdu->content, 1, "membership")); JsonValue *third_pid = JsonGet(pdu->content, 1, "third_party_invite"); - if (IsState(auth_pdu, "m.room.member", pdu->sender)) + + if (IsState(auth_pdu, "m.room.member", auth_pdu->sender)) { /* TODO: Check if it's the latest in terms of [pdu] */ return true; @@ -1023,7 +1023,8 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, HashMap *state) int64_t ban_level = RoomMinPL(room, state, NULL, "ban"); int64_t kick_level = RoomMinPL(room, state, NULL, "kick"); int64_t sender_level = RoomUserPL(room, state, pdu.sender); - int64_t target_level = RoomUserPL(room, state, pdu.sender); + int64_t target_level = RoomUserPL(room, state, pdu.state_key); + /* Step 5.4.1: If the sender matches state_key, allow if and only if * that user's current membership state is invite or join. */ if (StrEquals(pdu.sender, pdu.state_key)) diff --git a/src/Routes.c b/src/Routes.c index bbae407..ad61ad0 100644 --- a/src/Routes.c +++ b/src/Routes.c @@ -81,6 +81,7 @@ RouterBuild(void) R("/_matrix/client/v3/rooms/(.*)/state/(.*)", RouteSendState); R("/_matrix/client/v3/rooms/(.*)/event/(.*)", RouteFetchEvent); R("/_matrix/client/v3/rooms/(.*)/join", RouteJoinRoom); + R("/_matrix/client/v3/rooms/(.*)/kick", RouteKickRoom); R("/_matrix/client/v3/rooms/(.*)/messages", RouteMessages); R("/_matrix/client/v3/join/(.*)", RouteJoinRoomAlias); diff --git a/src/Routes/RouteJoinRoom.c b/src/Routes/RouteActRoom.c similarity index 55% rename from src/Routes/RouteJoinRoom.c rename to src/Routes/RouteActRoom.c index 20c9d50..dce551f 100644 --- a/src/Routes/RouteJoinRoom.c +++ b/src/Routes/RouteActRoom.c @@ -154,3 +154,140 @@ finish: UserUnlock(user); return response; } + +ROUTE_IMPL(RouteKickRoom, path, argp) +{ + RouteArgs *args = argp; + Db *db = args->matrixArgs->db; + + HashMap *request = NULL; + HashMap *response = NULL; + + HashMap *content, *membership; + HashMap *pduResponse; + + User *user = NULL; + char *token = NULL; + CommonID *id = NULL; + + char *roomId = ArrayGet(path, 0); + char *kicked = NULL, *reason = NULL; + char *sender = NULL, *serverName = NULL; + + Room *room = 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_POST) + { + err = "Unknown request method."; + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + response = MatrixErrorCreate(M_UNRECOGNIZED, err); + goto finish; + } + + request = JsonDecode(HttpServerStream(args->context)); + if (!request) + { + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + response = MatrixErrorCreate(M_NOT_JSON, NULL); + goto finish; + } + + kicked = JsonValueAsString(HashMapGet(request, "reason")); + if (!(kicked = JsonValueAsString(HashMapGet(request, "user_id")))) + { + err = "'user_id' field required(string), but not given"; + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + response = MatrixErrorCreate(M_BAD_JSON, 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); + + room = RoomLock(db, roomId); + if (!RoomContainsUser(room, sender)) + { + err = "Sender is not already in the room"; + HttpResponseStatus(args->context, HTTP_UNAUTHORIZED); + response = MatrixErrorCreate(M_FORBIDDEN, err); + goto finish; + } + if (!RoomContainsUser(room, kicked)) + { + err = "Kicked user is not present in the room"; + HttpResponseStatus(args->context, HTTP_BAD_REQUEST); + response = MatrixErrorCreate(M_BAD_STATE, err); + goto finish; + } + + content = HashMapCreate(); + membership = RoomEventCreate( + sender, + "m.room.member", kicked, + content + ); + + HashMapSet(content, "membership", JsonValueString("leave")); + if (reason) + { + HashMapSet(content, "reason", JsonValueString(reason)); + } + + pduResponse = RoomEventSend(room, membership); + if (!pduResponse) + { + err = "Couldn't accept kick event"; + HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR); + response = MatrixErrorCreate(M_UNKNOWN, err); + goto finish; + } + JsonFree(pduResponse); + JsonFree(membership); + + response = HashMapCreate(); +finish: + UserIdFree(id); + JsonFree(request); + if (sender) + { + Free(sender); + } + if (serverName) + { + Free(serverName); + } + RoomUnlock(room); + UserUnlock(user); + + return response; +} diff --git a/src/Routes/RouteKeyQuery.c b/src/Routes/RouteKeyQuery.c deleted file mode 100644 index 7352d4a..0000000 --- a/src/Routes/RouteKeyQuery.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 - -ROUTE_IMPL(RouteKeyQuery, path, argp) -{ - HashMap *response = HashMapCreate(); - char *action = ArrayGet(path, 0); - - (void) argp; - - if (StrEquals(action, "upload")) - { - HashMapSet(response, - "one_time_key_counts", JsonValueObject(HashMapCreate()) - ); - } - /* TODO: Spoofed endpoint */ - - return response; -} diff --git a/src/Routes/RoutePushrules.c b/src/Routes/RoutePushrules.c deleted file mode 100644 index 94e6159..0000000 --- a/src/Routes/RoutePushrules.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 - -ROUTE_IMPL(RouteKeyQuery, path, argp) -{ - HashMap *response = HashMapCreate(); - - (void) path; - (void) argp; - - /* TODO: Spoofed endpoint */ - HashMapSet(response, "global", JsonValueObject(HashMapCreate())); - - return response; -} - diff --git a/src/User.c b/src/User.c index 3209ba8..025a1fe 100644 --- a/src/User.c +++ b/src/User.c @@ -1054,7 +1054,7 @@ UserRemoveInvite(User *user, char *roomId) data = DbJson(user->inviteRef); - JsonFree(HashMapDelete(data, roomId)); + JsonValueFree(HashMapDelete(data, roomId)); } Array * UserListInvites(User *user) @@ -1107,7 +1107,7 @@ UserRemoveJoin(User *user, char *roomId) data = DbJson(user->joinRef); - JsonFree(HashMapDelete(data, roomId)); + JsonValueFree(HashMapDelete(data, roomId)); UserNotifyUser(user); } Array * diff --git a/src/include/Routes.h b/src/include/Routes.h index 107ff3f..261f271 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -103,6 +103,7 @@ ROUTE(RouteCreateRoom); ROUTE(RouteSendEvent); ROUTE(RouteSendState); ROUTE(RouteJoinRoom); +ROUTE(RouteKickRoom); ROUTE(RouteJoinRoomAlias); ROUTE(RouteFetchEvent); ROUTE(RouteJoinedRooms);