diff --git a/src/Room.c b/src/Room.c index 2d80358..7a5358d 100644 --- a/src/Room.c +++ b/src/Room.c @@ -126,6 +126,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s) event = RoomEventCreate(sender_str, "m.room.create", "", content); JsonFree(RoomEventSend(room, event)); JsonFree(event); + UserAddJoin(user, room->id); /* m.room.member */ content = HashMapCreate(); @@ -2024,6 +2025,8 @@ RoomSendInvite(User *sender, bool direct, char *user, Room *room) JsonFree(RoomEventSend(room, event)); JsonFree(event); + /* TODO: Send to "local" invite list if user is local. */ + ConfigUnlock(&conf); Free(senderStr); } @@ -2181,7 +2184,10 @@ RoomJoin(Room *room, User *user) pdu = RoomEventSend(room, event); ret = !!pdu; - /* TODO: Note down *somewhere* that the user joined. */ + if (ret) + { + UserAddJoin(user, room->id); + } end: UserIdFree(userId); JsonFree(event); diff --git a/src/Routes.c b/src/Routes.c index b6d4980..b90484b 100644 --- a/src/Routes.c +++ b/src/Routes.c @@ -82,6 +82,8 @@ RouterBuild(void) R("/_matrix/client/v3/join/(.*)", RouteJoinRoomAlias); + R("/_matrix/client/v3/joined_rooms", RouteJoinedRooms); + R("/_matrix/client/v3/createRoom", RouteCreateRoom); R("/_matrix/client/v3/directory/room/(.*)", RouteAliasDirectory); diff --git a/src/Routes/RouteJoinedRooms.c b/src/Routes/RouteJoinedRooms.c new file mode 100644 index 0000000..6393864 --- /dev/null +++ b/src/Routes/RouteJoinedRooms.c @@ -0,0 +1,93 @@ +/* + * 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 + + +ROUTE_IMPL(RouteJoinedRooms, path, argp) +{ + RouteArgs *args = argp; + Db *db = args->matrixArgs->db; + + HashMap *response = NULL; + + User *user = NULL; + char *token = NULL; + + Array *rawRoomList; + Array *roomIds; + + char *err; + + size_t i; + + 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; + } + rawRoomList = UserListJoins(user); + roomIds = ArrayCreate(); + for (i = 0; i < ArraySize(rawRoomList); i++) + { + ArrayAdd(roomIds, JsonValueString(ArrayGet(rawRoomList, i))); + } + UserFreeList(rawRoomList); + + response = HashMapCreate(); + HashMapSet(response, "joined_rooms", JsonValueArray(roomIds)); + + (void) path; +finish: + UserUnlock(user); + return response; +} diff --git a/src/User.c b/src/User.c index 27a6935..4bdab9f 100644 --- a/src/User.c +++ b/src/User.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -232,6 +233,9 @@ UserCreate(Db * db, char *name, char *password) HashMapSet(json, "createdOn", JsonValueInteger(ts)); HashMapSet(json, "deactivated", JsonValueBoolean(false)); + HashMapSet(json, "invites", JsonValueObject(HashMapCreate())); + HashMapSet(json, "joins", JsonValueObject(HashMapCreate())); + return user; } @@ -984,3 +988,129 @@ UserSetTransaction(User *user, char *transaction, char *path, HashMap *resp) 3, "transactions", path, transaction )); } +void +UserAddInvite(User *user, char *roomId) +{ + HashMap *data; + HashMap *invites; + if (!user || !roomId) + { + return; + } + + data = DbJson(user->ref); + invites = JsonValueAsObject(HashMapGet(data, "invites")); + + JsonFree(HashMapSet(invites, roomId, JsonValueNull())); +} + +void +UserRemoveInvite(User *user, char *roomId) +{ + HashMap *data; + HashMap *invites; + if (!user || !roomId) + { + return; + } + + data = DbJson(user->ref); + invites = JsonValueAsObject(HashMapGet(data, "invites")); + + JsonFree(HashMapDelete(invites, roomId)); +} +Array * +UserListInvites(User *user) +{ + Array *arr; + HashMap *data; + HashMap *invites; + + size_t i; + if (!user) + { + return NULL; + } + data = DbJson(user->ref); + invites = JsonValueAsObject(HashMapGet(data, "invites")); + arr = HashMapKeys(invites); + + for (i = 0; i < ArraySize(arr); i++) + { + ArraySet(arr, i, StrDuplicate(ArrayGet(arr, i))); + } + + return arr; +} + +void +UserAddJoin(User *user, char *roomId) +{ + HashMap *data; + HashMap *joins; + if (!user || !roomId) + { + return; + } + + data = DbJson(user->ref); + joins = JsonValueAsObject(HashMapGet(data, "joins")); + + JsonFree(HashMapSet(joins, roomId, JsonValueNull())); +} + +void +UserRemoveJoin(User *user, char *roomId) +{ + HashMap *data; + HashMap *joins; + if (!user || !roomId) + { + return; + } + + data = DbJson(user->ref); + joins = JsonValueAsObject(HashMapGet(data, "joins")); + + JsonFree(HashMapDelete(joins, roomId)); +} +Array * +UserListJoins(User *user) +{ + Array *arr; + HashMap *data; + HashMap *joins; + + size_t i; + if (!user) + { + return NULL; + } + data = DbJson(user->ref); + joins = JsonValueAsObject(HashMapGet(data, "joins")); + arr = HashMapKeys(joins); + + for (i = 0; i < ArraySize(arr); i++) + { + ArraySet(arr, i, StrDuplicate(ArrayGet(arr, i))); + } + + return arr; +} + +void +UserFreeList(Array *arr) +{ + size_t i; + if (!arr) + { + return; + } + + for (i = 0; i < ArraySize(arr); i++) + { + Free(ArrayGet(arr, i)); + } + + ArrayFree(arr); +} diff --git a/src/include/Routes.h b/src/include/Routes.h index f307add..7a7708e 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -103,6 +103,7 @@ ROUTE(RouteCreateRoom); ROUTE(RouteFetchEvent); ROUTE(RouteSendEvent); ROUTE(RouteJoinRoom); +ROUTE(RouteJoinedRooms); ROUTE(RouteJoinRoomAlias); ROUTE(RouteAliasDirectory); diff --git a/src/include/User.h b/src/include/User.h index c83f103..632f892 100644 --- a/src/include/User.h +++ b/src/include/User.h @@ -314,6 +314,48 @@ extern HashMap * UserGetTransaction(User *, char *, char *); */ extern void UserSetTransaction(User *, char *, char *, HashMap *); +/** + * Puts a room (indexed by room ID) in a user's invite list. + */ +extern void UserAddInvite(User *, char *); + +/** + * Removes a room from the user's invite list. + */ +extern void UserRemoveInvite(User *, char *); + +/** + * Lists all the rooms a user is invited to into an array of + * strings(room ID) to be freed by + * .Fn UserFreeInvites . + */ +extern Array * UserListInvites(User *); + +/** + * Puts a room (indexed by room ID) in a user's join list. + */ +extern void UserAddJoin(User *, char *); + +/** + * Removes a room from the user's join list. + */ +extern void UserRemoveJoin(User *, char *); + +/** + * Lists all the rooms a user has joined into an array of + * strings(room ID) to be freed by + * .Fn UserFreeJoins . + */ +extern Array * UserListJoins(User *); + +/** + * Frees a join/invite list as created by + * .Fn UserListJoins + * or + * .Fn UserListInvites . + */ +extern void UserFreeList(Array *); + /** * Frees the user's common ID and the memory allocated for it. */