diff --git a/src/Routes.c b/src/Routes.c index b90484b..aac7343 100644 --- a/src/Routes.c +++ b/src/Routes.c @@ -86,6 +86,8 @@ RouterBuild(void) R("/_matrix/client/v3/createRoom", RouteCreateRoom); + R("/_matrix/client/v3/sync", RouteSync); + R("/_matrix/client/v3/directory/room/(.*)", RouteAliasDirectory); R("/_matrix/client/v3/rooms/(.*)/aliases", RouteRoomAliases); diff --git a/src/Routes/RouteSync.c b/src/Routes/RouteSync.c new file mode 100644 index 0000000..24791d0 --- /dev/null +++ b/src/Routes/RouteSync.c @@ -0,0 +1,148 @@ +/* + * 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 +#include + +#include + + +ROUTE_IMPL(RouteSync, path, argp) +{ + RouteArgs *args = argp; + Db *db = args->matrixArgs->db; + + HashMap *params = NULL; + HashMap *response = NULL; + + + HashMap *rooms = NULL; + + User *user = NULL; + char *token = NULL; + char *prevBatch = NULL; + char *nextBatch = NULL; + char *currBatch = 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; + } + params = HttpRequestParams(args->context); + prevBatch = HashMapGet(params, "since"); + if (!prevBatch) + { + nextBatch = UserInitSyncDiff(user); + UserFillSyncDiff(user, nextBatch); + } + currBatch = prevBatch ? prevBatch : nextBatch; + + /* TODO: I only am manually parsing this because j2s does not support + * a hashmap of unknown keys pointing to a known type. */ + response = HashMapCreate(); + rooms = HashMapCreate(); + + /* invites */ + { + Array *invites; + size_t i; + + invites = UserGetInvites(user, currBatch); + for (i = 0; i < ArraySize(invites); i++) + { + char *roomId = ArrayGet(invites, i); + JsonSet( + rooms, + JsonValueObject(HashMapCreate()), + 2, "invites", roomId + ); + } + UserFreeList(invites); + } + + /* Joins */ + { + Array *joins; + size_t i; + + joins = UserGetJoins(user, currBatch); + for (i = 0; i < ArraySize(joins); i++) + { + char *roomId = ArrayGet(joins, i); + HashMap *room = HashMapCreate(); + + /* TODO: Add history. */ + + JsonSet( + rooms, + JsonValueObject(room), + 2, "join", roomId + ); + } + UserFreeList(joins); + } + + (void) path; + if (prevBatch) + { + UserDropSync(user, prevBatch); + nextBatch = UserInitSyncDiff(user); + } + HashMapSet(response, "next_batch", JsonValueString(nextBatch)); + Free(nextBatch); + HashMapSet(response, "rooms", JsonValueObject(rooms)); +finish: + UserUnlock(user); + return response; +} diff --git a/src/User.c b/src/User.c index 8d38ea3..8a37c6c 100644 --- a/src/User.c +++ b/src/User.c @@ -1261,3 +1261,102 @@ UserDropSync(User *user, char *batch) } DbDelete(user->db, 4, "users", user->name, "sync", batch); } +Array * +UserGetInvites(User *user, char *batch) +{ + DbRef *syncRef; + HashMap *data; + Array *keys; + size_t i; + if (!user || !batch) + { + return NULL; + } + + syncRef = DbLock(user->db, 4, "users", user->name, "sync", batch); + if (!syncRef) + { + return NULL; + } + + data = DbJson(syncRef); + + keys = ArrayDuplicate(JsonValueAsArray(HashMapGet(data, "invites"))); + for (i = 0; i < ArraySize(keys); i++) + { + char *str = JsonValueAsString(ArrayGet(keys, i)); + ArraySet(keys, i, StrDuplicate(str)); + } + + + DbUnlock(user->db, syncRef); + return keys; +} +void +UserFillSyncDiff(User *user, char *batch) +{ + Array *joins; + size_t i; + DbRef *syncRef; + if (!user || !batch) + { + return; + } + + joins = UserListJoins(user); + syncRef = DbLock( + user->db, 4, "users", user->name, "sync", batch + ); + for (i = 0; i < ArraySize(joins); i++) + { + char *roomId = ArrayGet(joins, i); + HashMap *joinEntry; + HashMap *data = DbJson(syncRef); + HashMap *join = JsonValueAsObject(HashMapGet(data, "joins")); + + joinEntry = HashMapCreate(); + HashMapSet(joinEntry, "timeline", JsonValueArray(ArrayCreate())); + + if (!HashMapGet(join, roomId)) + { + JsonFree(HashMapSet(join, roomId, JsonValueObject(joinEntry))); + } + else + { + JsonFree(joinEntry); + } + } + UserFreeList(joins); + DbUnlock(user->db, syncRef); +} +Array * +UserGetJoins(User *user, char *batch) +{ + DbRef *syncRef; + HashMap *data; + Array *keys; + size_t i; + if (!user || !batch) + { + return NULL; + } + + syncRef = DbLock(user->db, 4, "users", user->name, "sync", batch); + if (!syncRef) + { + return NULL; + } + + data = DbJson(syncRef); + + keys = HashMapKeys(JsonValueAsObject(HashMapGet(data, "joins"))); + for (i = 0; i < ArraySize(keys); i++) + { + char *str = ArrayGet(keys, i); + ArraySet(keys, i, StrDuplicate(str)); + } + + + DbUnlock(user->db, syncRef); + return keys; +} diff --git a/src/include/Routes.h b/src/include/Routes.h index 7a7708e..5f4472b 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -105,6 +105,7 @@ ROUTE(RouteSendEvent); ROUTE(RouteJoinRoom); ROUTE(RouteJoinedRooms); ROUTE(RouteJoinRoomAlias); +ROUTE(RouteSync); ROUTE(RouteAliasDirectory); ROUTE(RouteRoomAliases); diff --git a/src/include/User.h b/src/include/User.h index f67794a..e835205 100644 --- a/src/include/User.h +++ b/src/include/User.h @@ -368,6 +368,11 @@ extern void UserIdFree(CommonID *); */ extern char * UserInitSyncDiff(User *); +/** + * Fills a sync table with initial sync information + */ +extern void UserFillSyncDiff(User *, char *); + /** * Pushes an invite onto all sync diff tables. */ @@ -383,6 +388,21 @@ extern void UserPushJoinSync(User *, char *); */ extern void UserPushEvent(User *, HashMap *); +/** + * Shows the invite list(as a room ID table) for an user + * and a specified diff table, to be freed by + * .Fn UserFreeList . + */ +extern Array * UserGetInvites(User *, char *); + + +/** + * Shows a list of rooms for an user and a specified diff + * table, to be freed by + * .Fn UserFreeList . + */ +extern Array * UserGetJoins(User *, char *); + /** * Drops a sync diff, denoted by it's previous batch ID, if it * exists.