forked from Telodendria/Telodendria
Compare commits
42 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
e2d26c7f33 | |
|
97b1e4d723 | |
|
046a04e495 | |
|
00311f58dc | |
|
a491a740d4 | |
![]() |
bb3d15f2c9 | |
![]() |
88610a3238 | |
![]() |
9203ec1268 | |
![]() |
9600d2ffb5 | |
|
250d28b958 | |
![]() |
6ce63b01ce | |
![]() |
e268471dba | |
![]() |
39c4af8d4d | |
|
b0c3d6ce31 | |
![]() |
3a2fec8a47 | |
![]() |
604d6cf017 | |
![]() |
21b015da2c | |
|
c1630247b1 | |
|
879e51c169 | |
|
c22d628c86 | |
|
9dcaab0819 | |
|
cacc72bf84 | |
|
a99798a312 | |
|
e3c57d8f05 | |
|
7ee35fcc28 | |
|
327730c2c6 | |
|
5ca36396a2 | |
|
e1d632b135 | |
|
a8924b8437 | |
|
ecb18dc7b2 | |
|
e36f4357ab | |
|
1753c2188b | |
|
9ffa37d7a7 | |
|
15b884b04a | |
|
3066a0e8a8 | |
|
878b294030 | |
|
6a69218479 | |
|
aec71d8d32 | |
|
50759a3298 | |
|
9bc36f324a | |
|
001b4821fe | |
|
d0a447a409 |
|
@ -0,0 +1,14 @@
|
|||
import os
|
||||
import ycm_core
|
||||
|
||||
def Settings( **kwargs ):
|
||||
return {
|
||||
'flags': [
|
||||
"-xc",
|
||||
"-I", "src/include",
|
||||
"-I", "Cytoplasm/include",
|
||||
"-I", "build",
|
||||
|
||||
"-std=c99"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit 5d87da31cda74e6808eebca72e9475aabde86532
|
||||
Subproject commit 346b912a0633cceac10780b8a103f6c89b5ba89f
|
|
@ -16,6 +16,10 @@
|
|||
"fields": {
|
||||
"age": {
|
||||
"type": "integer"
|
||||
},
|
||||
"next_events": {
|
||||
"type": "[string]",
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -51,7 +55,8 @@
|
|||
"required": true
|
||||
},
|
||||
"redacts": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"required": false
|
||||
},
|
||||
"room_id": {
|
||||
"type": "string",
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"guard": "TELODENDRIA_SCHEMA_SYNCRESPONSE_H",
|
||||
"header": "Schema\/SyncResponse.h",
|
||||
"types": {
|
||||
"Event": {
|
||||
"fields": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"AccountData": {
|
||||
"fields": {
|
||||
"events": { "type": "[Event]" }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"InviteState": {
|
||||
"fields": {
|
||||
"events": { "type": "[StrippedStateEvent]" }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"States": {
|
||||
"fields": {
|
||||
"events": { "type": "[StrippedStateEvent]" }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"StrippedStateEvent": {
|
||||
"fields": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"required": true
|
||||
},
|
||||
"sender": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"state_key": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"InvitedRooms": {
|
||||
"fields": {
|
||||
"invite_state": { "type": "InviteState" }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"ClientEventWithoutRoomID": {
|
||||
"fields": {
|
||||
"content": { "type": "object", "required": true },
|
||||
"event_id": { "type": "string", "required": true },
|
||||
"origin_server_ts": { "type": "integer", "required": true },
|
||||
"sender": { "type": "string", "required": true },
|
||||
"state_key": { "type": "string" },
|
||||
"type": { "type": "string", "required": true }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"Timeline": {
|
||||
"fields": {
|
||||
"events": { "type": "[ClientEventWithoutRoomID]", "required": true },
|
||||
"limited": { "type": "boolean" },
|
||||
"prev_batch": { "type": "string" }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"JoinedRooms": {
|
||||
"fields": {
|
||||
"timeline": { "type": "Timeline" },
|
||||
"state": { "type": "States" }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"Rooms": {
|
||||
"fields": {
|
||||
"invite": { "type": "object" },
|
||||
"join": { "type": "object" }
|
||||
},
|
||||
"type": "struct"
|
||||
},
|
||||
"SyncResponse": {
|
||||
"fields": {
|
||||
"next_batch": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"account_data": {
|
||||
"type": "AccountData"
|
||||
},
|
||||
"rooms": {
|
||||
"type": "Rooms"
|
||||
}
|
||||
},
|
||||
"type": "struct"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,11 +22,16 @@
|
|||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "Cytoplasm/Io.h"
|
||||
#include "Cytoplasm/Stream.h"
|
||||
#include <CanonicalJson.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Array.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -159,3 +164,55 @@ CanonicalJsonEncode(HashMap * object, Stream * out)
|
|||
ArrayFree(keys);
|
||||
return length;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
StringIoRead(void *cookie, void *buf, size_t count)
|
||||
{
|
||||
(void) cookie;
|
||||
(void) buf;
|
||||
(void) count;
|
||||
return -1; /* TODO: Consider reading properly */
|
||||
}
|
||||
static ssize_t
|
||||
StringIoWrite(void *c, void *buf, size_t count)
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
char **str = c;
|
||||
char *stringised = Malloc(count + 1);
|
||||
char *new;
|
||||
memcpy(stringised, buf, count);
|
||||
new = StrConcat(2, *str, stringised);
|
||||
|
||||
Free(stringised);
|
||||
if (*str)
|
||||
{
|
||||
Free(*str);
|
||||
}
|
||||
*str = new;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
static const IoFunctions Functions = {
|
||||
.read = StringIoRead,
|
||||
.write = StringIoWrite,
|
||||
.close = NULL,
|
||||
.seek = NULL
|
||||
};
|
||||
unsigned char *
|
||||
CanonicalJsonHash(HashMap *json)
|
||||
{
|
||||
char *string = NULL;
|
||||
unsigned char *sha;
|
||||
Io *string_writer = IoCreate(&string, Functions);
|
||||
Stream *io_stream = StreamIo(string_writer);
|
||||
CanonicalJsonEncode(json, io_stream);
|
||||
StreamFlush(io_stream);
|
||||
|
||||
sha = Sha256(string);
|
||||
|
||||
Free(string);
|
||||
StreamClose(io_stream);
|
||||
|
||||
return sha;
|
||||
}
|
||||
|
|
27
src/Main.c
27
src/Main.c
|
@ -86,6 +86,26 @@ SignalHandler(int signal)
|
|||
}
|
||||
}
|
||||
|
||||
/* TODO: Move this! */
|
||||
static void
|
||||
UsersClean(MatrixHttpHandlerArgs *args)
|
||||
{
|
||||
Db *db = args->db;
|
||||
Array *arr = DbList(db, 1, "users");
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ArraySize(arr); i++)
|
||||
{
|
||||
char *id = ArrayGet(arr, i);
|
||||
User *user = UserLock(db, id);
|
||||
|
||||
UserCleanTemporaryData(user);
|
||||
UserUnlock(user);
|
||||
}
|
||||
|
||||
DbListFree(arr);
|
||||
}
|
||||
|
||||
typedef enum ArgFlag
|
||||
{
|
||||
ARG_VERSION = (1 << 0),
|
||||
|
@ -147,6 +167,7 @@ start:
|
|||
token = NULL;
|
||||
|
||||
memset(&matrixArgs, 0, sizeof(matrixArgs));
|
||||
UserInitialisePushTable();
|
||||
|
||||
if (!LogConfigGlobal())
|
||||
{
|
||||
|
@ -513,10 +534,15 @@ start:
|
|||
Log(LOG_DEBUG, "Registering jobs...");
|
||||
|
||||
CronEvery(cron, 30 * 60 * 1000, (JobFunc *) UiaCleanup, &matrixArgs);
|
||||
CronEvery(cron, 5 * 60 * 1000, (JobFunc *) UsersClean, &matrixArgs);
|
||||
|
||||
Log(LOG_NOTICE, "Starting job scheduler...");
|
||||
CronStart(cron);
|
||||
|
||||
/* We still call it anyways to be sure. */
|
||||
Log(LOG_NOTICE, "Cleaning up user data...");
|
||||
UsersClean(&matrixArgs);
|
||||
|
||||
Log(LOG_NOTICE, "Building routing tree...");
|
||||
matrixArgs.router = RouterBuild();
|
||||
if (!matrixArgs.router)
|
||||
|
@ -583,6 +609,7 @@ start:
|
|||
|
||||
finish:
|
||||
Log(LOG_NOTICE, "Shutting down...");
|
||||
UserDestroyPushTable();
|
||||
if (httpServers)
|
||||
{
|
||||
for (i = 0; i < ArraySize(httpServers); i++)
|
||||
|
|
|
@ -487,7 +487,7 @@ ParserRecomposeCommonID(CommonID id)
|
|||
if (id.server.hostname)
|
||||
{
|
||||
char *server = ParserRecomposeServerPart(id.server);
|
||||
char *tmp = StrConcat(4, "@", ret, ":", server);
|
||||
char *tmp = StrConcat(3, ret, ":", server);
|
||||
Free(ret);
|
||||
Free(server);
|
||||
|
||||
|
|
2232
src/Room.c
2232
src/Room.c
File diff suppressed because it is too large
Load Diff
13
src/Routes.c
13
src/Routes.c
|
@ -76,11 +76,24 @@ 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/(.*)/state/(.*)/(.*)", RouteSendState);
|
||||
R("/_matrix/client/v3/rooms/(.*)/event/(.*)", RouteFetchEvent);
|
||||
R("/_matrix/client/v3/rooms/(.*)/join", RouteJoinRoom);
|
||||
R("/_matrix/client/v3/rooms/(.*)/messages", RouteMessages);
|
||||
|
||||
R("/_matrix/client/v3/join/(.*)", RouteJoinRoomAlias);
|
||||
|
||||
R("/_matrix/client/v3/joined_rooms", RouteJoinedRooms);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
/* Telodendria Admin API Routes */
|
||||
|
||||
R("/_telodendria/admin/v1/(restart|shutdown|stats)", RouteProcControl);
|
||||
|
|
|
@ -23,11 +23,14 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "Cytoplasm/Http.h"
|
||||
#include <Routes.h>
|
||||
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Schema/RoomCreateRequest.h>
|
||||
#include <Room.h>
|
||||
#include <User.h>
|
||||
|
||||
ROUTE_IMPL(RouteCreateRoom, path, argp)
|
||||
{
|
||||
|
@ -35,11 +38,21 @@ ROUTE_IMPL(RouteCreateRoom, path, argp)
|
|||
|
||||
HashMap *request = NULL;
|
||||
HashMap *response;
|
||||
RoomCreateRequest parsed;
|
||||
Room *room = NULL;
|
||||
Db *db = args->matrixArgs->db;
|
||||
RoomCreateRequest parsed = { 0 };
|
||||
User *user = NULL;
|
||||
Config cfg;
|
||||
ServerPart server;
|
||||
|
||||
char *token;
|
||||
char *err;
|
||||
|
||||
(void) path;
|
||||
|
||||
ConfigLock(db, &cfg);
|
||||
ParseServerPart(cfg.serverName, &server);
|
||||
|
||||
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
||||
{
|
||||
err = "Unknown request method.";
|
||||
|
@ -48,6 +61,18 @@ ROUTE_IMPL(RouteCreateRoom, path, argp)
|
|||
goto finish;
|
||||
}
|
||||
|
||||
response = MatrixGetAccessToken(args->context, &token);
|
||||
if (response)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
user = UserAuthenticate(db, token);
|
||||
if (!user)
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
return MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
|
||||
}
|
||||
|
||||
request = JsonDecode(HttpServerStream(args->context));
|
||||
if (!request)
|
||||
{
|
||||
|
@ -55,7 +80,19 @@ ROUTE_IMPL(RouteCreateRoom, path, argp)
|
|||
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
||||
/* j2s hack: Setting to a default value like that.
|
||||
* It's definitely NOT good(you can't rely on enums to
|
||||
* store values outside their limits in the C standard,
|
||||
* and this line will need to be changed everytime the
|
||||
* preset list is changed).
|
||||
*
|
||||
* I do believe a decent solution to this would be to
|
||||
* add a 'default' type(maybe initialised to 0 so that
|
||||
* memsets would work as intended), that *wouldn't* be
|
||||
* returned by j2s itself.
|
||||
* TODO. */
|
||||
parsed.preset = ROOM_CREATE_PRIVATE + 1;
|
||||
if (!RoomCreateRequestFromJson(request, &parsed, &err))
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
|
@ -67,9 +104,24 @@ ROUTE_IMPL(RouteCreateRoom, path, argp)
|
|||
JsonFree(request);
|
||||
request = NULL;
|
||||
|
||||
if (!(room = RoomCreate(db, user, &parsed, server)))
|
||||
{
|
||||
err = "Couldn't create room.";
|
||||
/* Consider another error status. */
|
||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||
response = MatrixErrorCreate(M_UNRECOGNIZED, err);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
response = HashMapCreate();
|
||||
JsonSet(response, JsonValueString(RoomIdGet(room)), 1, "room_id");
|
||||
|
||||
finish:
|
||||
JsonFree(request);
|
||||
RoomUnlock(room);
|
||||
UserUnlock(user);
|
||||
ConfigUnlock(&cfg);
|
||||
ServerPartFree(server);
|
||||
RoomCreateRequestFree(&parsed);
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -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 <Routes.h>
|
||||
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <User.h>
|
||||
#include <Room.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Schema/Filter.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -189,7 +189,7 @@ ROUTE_IMPL(RouteFilter, path, argp)
|
|||
DbJsonSet(ref, filterJson);
|
||||
DbUnlock(db, ref);
|
||||
|
||||
Free(filterJson);
|
||||
JsonFree(filterJson);
|
||||
|
||||
response = HashMapCreate();
|
||||
HashMapSet(response, "filter_id", JsonValueString(filterId));
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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 <User.h>
|
||||
#include <Room.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Schema/Filter.h>
|
||||
|
||||
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(RouteJoinRoom, path, argp)
|
||||
{
|
||||
RouteArgs *args = argp;
|
||||
Db *db = args->matrixArgs->db;
|
||||
|
||||
HashMap *request = NULL;
|
||||
HashMap *response = NULL;
|
||||
|
||||
User *user = NULL;
|
||||
char *token = NULL;
|
||||
CommonID *id = NULL;
|
||||
|
||||
char *roomId = ArrayGet(path, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
room = RoomLock(db, roomId);
|
||||
if (!room)
|
||||
{
|
||||
|
||||
err = "Room ID does not exist.";
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
||||
goto finish;
|
||||
}
|
||||
if (RoomContainsUser(room, sender))
|
||||
{
|
||||
err = "User is already in the room.";
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||
goto finish;
|
||||
}
|
||||
if (!RoomCanJoin(room, sender))
|
||||
{
|
||||
err = "User cannot be in the room.";
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||
goto finish;
|
||||
}
|
||||
/* TODO: Custom reason parameter. */
|
||||
if (!RoomJoin(room, user))
|
||||
{
|
||||
err = "User could not be the room due to unknown reasons.";
|
||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
response = HashMapCreate();
|
||||
JsonSet(response, JsonValueString(roomId), 1, "room_id");
|
||||
|
||||
finish:
|
||||
UserIdFree(id);
|
||||
JsonFree(request);
|
||||
if (sender)
|
||||
{
|
||||
Free(sender);
|
||||
}
|
||||
if (serverName)
|
||||
{
|
||||
Free(serverName);
|
||||
}
|
||||
RoomUnlock(room);
|
||||
UserUnlock(user);
|
||||
return response;
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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 <User.h>
|
||||
#include <Room.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Schema/Filter.h>
|
||||
|
||||
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(RouteJoinRoomAlias, path, argp)
|
||||
{
|
||||
RouteArgs *args = argp;
|
||||
Db *db = args->matrixArgs->db;
|
||||
|
||||
HashMap *request = NULL;
|
||||
HashMap *response = NULL;
|
||||
|
||||
User *user = NULL;
|
||||
char *token = NULL;
|
||||
CommonID *id = NULL;
|
||||
|
||||
char *roomId = ArrayGet(path, 0);
|
||||
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 (*roomId != '!')
|
||||
{
|
||||
roomId = RoomResolveAlias(db, roomId);
|
||||
Log(LOG_NOTICE, "Here's my guess: %s", roomId);
|
||||
}
|
||||
else
|
||||
{
|
||||
roomId = StrDuplicate(roomId);
|
||||
}
|
||||
if (!roomId)
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||
response = MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Log(LOG_INFO, "Trying with token %s", token);
|
||||
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);
|
||||
Log(LOG_INFO, "Now as %s", sender);
|
||||
|
||||
room = RoomLock(db, roomId);
|
||||
if (!room)
|
||||
{
|
||||
err = "Room ID does not exist.";
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
||||
goto finish;
|
||||
}
|
||||
if (RoomContainsUser(room, sender))
|
||||
{
|
||||
err = "User is already in the room.";
|
||||
Log(LOG_INFO, "qhar for %s", sender);
|
||||
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||
goto finish;
|
||||
}
|
||||
if (!RoomCanJoin(room, sender))
|
||||
{
|
||||
err = "User cannot be in the room.";
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||
goto finish;
|
||||
}
|
||||
/* TODO: Custom reason parameter. */
|
||||
if (!RoomJoin(room, user))
|
||||
{
|
||||
err = "User could not be the room due to unknown reasons.";
|
||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
response = HashMapCreate();
|
||||
JsonSet(response, JsonValueString(roomId), 1, "room_id");
|
||||
|
||||
finish:
|
||||
Free(roomId);
|
||||
UserIdFree(id);
|
||||
JsonFree(request);
|
||||
if (sender)
|
||||
{
|
||||
Free(sender);
|
||||
}
|
||||
if (serverName)
|
||||
{
|
||||
Free(serverName);
|
||||
}
|
||||
RoomUnlock(room);
|
||||
UserUnlock(user);
|
||||
return response;
|
||||
}
|
|
@ -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 <Routes.h>
|
||||
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <User.h>
|
||||
#include <Room.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Schema/Filter.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* 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 <User.h>
|
||||
#include <Room.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Schema/Filter.h>
|
||||
|
||||
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(RouteMessages, 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 *roomId = ArrayGet(path, 0);
|
||||
char *from = HashMapGet(params, "from");
|
||||
char *limitStr = HashMapGet(params, "limit");
|
||||
char *end = NULL;
|
||||
char *sender = NULL;
|
||||
char *serverName = NULL;
|
||||
CommonID *id = NULL;
|
||||
int limit = strtol(limitStr, NULL, 10);
|
||||
|
||||
Array *messages = NULL;
|
||||
|
||||
Room *room = NULL;
|
||||
|
||||
char *err;
|
||||
|
||||
if (!limit || limit < 0)
|
||||
{
|
||||
limit = 10;
|
||||
}
|
||||
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 = 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);
|
||||
|
||||
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;
|
||||
}
|
||||
RoomUnlock(room);
|
||||
|
||||
/* TODO: Filters, to, and friends */
|
||||
messages = UserFetchMessages(user, limit, from, &end);
|
||||
if (!messages)
|
||||
{
|
||||
Room *r = RoomLock(db, roomId);
|
||||
Array *leaf = RoomPrevEventsGet(r);
|
||||
HashMap *start = JsonValueAsObject(ArrayGet(leaf, 0));
|
||||
char *startId = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(start, "event_id"))
|
||||
);
|
||||
char *token = UserNewMessageToken(user, roomId, startId);
|
||||
RoomUnlock(r);
|
||||
|
||||
messages = UserFetchMessages(user, limit, token, &end);
|
||||
Free(token);
|
||||
Free(startId);
|
||||
}
|
||||
response = HashMapCreate();
|
||||
JsonSet(response, JsonValueArray(messages), 1, "chunk");
|
||||
if (end)
|
||||
{
|
||||
JsonSet(response, JsonValueString(end), 1, "end");
|
||||
Free(end);
|
||||
}
|
||||
|
||||
UserFreeMessageToken(user, from);
|
||||
finish:
|
||||
UserUnlock(user);
|
||||
UserIdFree(id);
|
||||
if (sender)
|
||||
{
|
||||
Free(sender);
|
||||
}
|
||||
if (serverName)
|
||||
{
|
||||
Free(serverName);
|
||||
}
|
||||
return response;
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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 <Cytoplasm/HttpServer.h>
|
||||
#include <Routes.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <User.h>
|
||||
#include <Room.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Schema/Filter.h>
|
||||
|
||||
|
||||
ROUTE_IMPL(RoutePushRules, path, argp)
|
||||
{
|
||||
RouteArgs *args = argp;
|
||||
Db *db = args->matrixArgs->db;
|
||||
|
||||
HashMap *response = NULL;
|
||||
|
||||
User *user = NULL;
|
||||
char *token = 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;
|
||||
}
|
||||
response = HashMapCreate();
|
||||
JsonSet(response, JsonValueObject(HashMapCreate()), 1, "global");
|
||||
(void) path;
|
||||
finish:
|
||||
UserUnlock(user);
|
||||
return response;
|
||||
}
|
||||
|
||||
ROUTE_IMPL(RouteKeyQuery, path, argp)
|
||||
{
|
||||
RouteArgs *args = argp;
|
||||
Db *db = args->matrixArgs->db;
|
||||
|
||||
HashMap *request = NULL;
|
||||
HashMap *response = NULL;
|
||||
|
||||
User *user = NULL;
|
||||
char *token = NULL;
|
||||
|
||||
char *err;
|
||||
|
||||
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
||||
{
|
||||
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;
|
||||
}
|
||||
request = JsonDecode(HttpServerStream(args->context));
|
||||
if (!request)
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
response = HashMapCreate();
|
||||
(void) path;
|
||||
finish:
|
||||
JsonFree(request);
|
||||
UserUnlock(user);
|
||||
return response;
|
||||
}
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include <Matrix.h>
|
||||
#include <User.h>
|
||||
#include <Room.h>
|
||||
|
||||
ROUTE_IMPL(RouteRoomAliases, path, argp)
|
||||
{
|
||||
|
@ -40,16 +41,15 @@ ROUTE_IMPL(RouteRoomAliases, path, argp)
|
|||
char *msg;
|
||||
|
||||
HashMap *response = NULL;
|
||||
HashMap *aliases = NULL;
|
||||
HashMap *reversealias = NULL;
|
||||
|
||||
JsonValue *val;
|
||||
Array *alias = NULL, *arr;
|
||||
|
||||
Db *db = args->matrixArgs->db;
|
||||
DbRef *ref = NULL;
|
||||
|
||||
User *user = NULL;
|
||||
|
||||
size_t i;
|
||||
|
||||
if (HttpRequestMethodGet(args->context) != HTTP_GET)
|
||||
{
|
||||
msg = "Route only accepts GET.";
|
||||
|
@ -81,11 +81,10 @@ ROUTE_IMPL(RouteRoomAliases, path, argp)
|
|||
response = MatrixErrorCreate(M_FORBIDDEN, msg);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
||||
ref = DbLock(db, 1, "aliases");
|
||||
aliases = DbJson(ref);
|
||||
reversealias = JsonValueAsObject(JsonGet(aliases, 2, "id", roomId));
|
||||
if (!reversealias)
|
||||
alias = RoomReverseAlias(db, roomId);
|
||||
if (!alias)
|
||||
{
|
||||
/* We do not know about the room ID. */
|
||||
msg = "Unknown room ID.";
|
||||
|
@ -93,12 +92,17 @@ ROUTE_IMPL(RouteRoomAliases, path, argp)
|
|||
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||
goto finish;
|
||||
}
|
||||
arr = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(alias); i++)
|
||||
{
|
||||
char *str = ArrayGet(alias, i);
|
||||
ArrayAdd(arr, JsonValueString(str));
|
||||
}
|
||||
|
||||
response = HashMapCreate();
|
||||
val = JsonGet(reversealias, 1, "aliases");
|
||||
HashMapSet(response, "aliases", JsonValueDuplicate(val));
|
||||
HashMapSet(response, "aliases", JsonValueArray(arr));
|
||||
finish:
|
||||
DbUnlock(db, ref);
|
||||
RoomFreeReverse(alias);
|
||||
UserUnlock(user);
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* 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 <User.h>
|
||||
#include <Room.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Schema/Filter.h>
|
||||
|
||||
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(RouteSendEvent, path, argp)
|
||||
{
|
||||
RouteArgs *args = argp;
|
||||
Db *db = args->matrixArgs->db;
|
||||
|
||||
HashMap *request = NULL, *event = NULL;
|
||||
HashMap *response = NULL, *filled = NULL;
|
||||
|
||||
User *user = NULL;
|
||||
CommonID *id = NULL;
|
||||
char *token = NULL;
|
||||
|
||||
char *serverName = NULL;
|
||||
|
||||
char *roomId = ArrayGet(path, 0);
|
||||
char *eventType = ArrayGet(path, 1);
|
||||
char *transId = ArrayGet(path, 2);
|
||||
char *sender = NULL;
|
||||
|
||||
Room *room = NULL;
|
||||
|
||||
char *err;
|
||||
|
||||
if (!roomId || !eventType || !transId)
|
||||
{
|
||||
/* Should be impossible */
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
request = JsonDecode(HttpServerStream(args->context));
|
||||
if (!request)
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||
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);
|
||||
|
||||
if ((response = UserGetTransaction(user, transId, "send")))
|
||||
{
|
||||
goto finish;
|
||||
}
|
||||
|
||||
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 = RoomEventCreate(sender, eventType, NULL, JsonDuplicate(request));
|
||||
filled = RoomEventSend(room, event);
|
||||
JsonFree(event);
|
||||
|
||||
if (!filled)
|
||||
{
|
||||
err = "User is not allowed to send event.";
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
response = HashMapCreate();
|
||||
HashMapSet(
|
||||
response, "event_id",
|
||||
JsonValueDuplicate(HashMapGet(filled, "event_id"))
|
||||
);
|
||||
JsonFree(filled);
|
||||
UserSetTransaction(user, transId, "send", response);
|
||||
finish:
|
||||
RoomUnlock(room);
|
||||
Free(serverName);
|
||||
if (sender)
|
||||
{
|
||||
Free(sender);
|
||||
}
|
||||
UserIdFree(id);
|
||||
UserUnlock(user);
|
||||
JsonFree(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
ROUTE_IMPL(RouteSendState, path, argp)
|
||||
{
|
||||
RouteArgs *args = argp;
|
||||
Db *db = args->matrixArgs->db;
|
||||
|
||||
HashMap *request = NULL, *event = NULL;
|
||||
HashMap *response = NULL, *filled = NULL;
|
||||
|
||||
User *user = NULL;
|
||||
CommonID *id = NULL;
|
||||
char *token = NULL;
|
||||
|
||||
char *serverName = NULL;
|
||||
|
||||
char *roomId = ArrayGet(path, 0);
|
||||
char *eventType = ArrayGet(path, 1);
|
||||
char *stateKey = ArrayGet(path, 2);
|
||||
char *sender = NULL;
|
||||
|
||||
Room *room = NULL;
|
||||
|
||||
char *err;
|
||||
|
||||
if (!roomId || !eventType)
|
||||
{
|
||||
/* Should be impossible */
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
request = JsonDecode(HttpServerStream(args->context));
|
||||
if (!request)
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||
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 = "User is not in the room.";
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
Log(LOG_INFO, "State event (%s,%s) coming this way", eventType, stateKey);
|
||||
event = RoomEventCreate(
|
||||
sender,
|
||||
eventType, stateKey ? stateKey : "",
|
||||
JsonDuplicate(request)
|
||||
);
|
||||
filled = RoomEventSend(room, event);
|
||||
Log(LOG_INFO, "State event (%s,%s) coming this way", eventType, stateKey);
|
||||
JsonFree(event);
|
||||
Log(LOG_INFO, "And thats freed!");
|
||||
|
||||
if (!filled)
|
||||
{
|
||||
err = "User is not allowed to send state.";
|
||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
response = HashMapCreate();
|
||||
HashMapSet(
|
||||
response, "event_id",
|
||||
JsonValueDuplicate(HashMapGet(filled, "event_id"))
|
||||
);
|
||||
JsonFree(filled);
|
||||
finish:
|
||||
RoomUnlock(room);
|
||||
Free(serverName);
|
||||
if (sender)
|
||||
{
|
||||
Free(sender);
|
||||
}
|
||||
UserIdFree(id);
|
||||
UserUnlock(user);
|
||||
JsonFree(request);
|
||||
return response;
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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 <Cytoplasm/HttpServer.h>
|
||||
#include <Routes.h>
|
||||
|
||||
#include <Schema/SyncResponse.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <State.h>
|
||||
#include <User.h>
|
||||
#include <Room.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static ClientEventWithoutRoomID
|
||||
ClientfyEventSync(HashMap *pdu)
|
||||
{
|
||||
ClientEventWithoutRoomID ret = { 0 };
|
||||
char *ignored;
|
||||
ClientEventWithoutRoomIDFromJson(pdu, &ret, &ignored);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static StrippedStateEvent
|
||||
StripStateEventSync(HashMap *pdu)
|
||||
{
|
||||
StrippedStateEvent ret = { 0 };
|
||||
char *ignored;
|
||||
StrippedStateEventFromJson(pdu, &ret, &ignored);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ROUTE_IMPL(RouteSync, path, argp)
|
||||
{
|
||||
RouteArgs *args = argp;
|
||||
Db *db = args->matrixArgs->db;
|
||||
|
||||
HashMap *params = NULL;
|
||||
HashMap *response = NULL;
|
||||
SyncResponse sync = { 0 };
|
||||
|
||||
Array *invites;
|
||||
Array *joins;
|
||||
size_t i;
|
||||
|
||||
User *user = NULL;
|
||||
char *token = NULL;
|
||||
char *prevBatch = NULL;
|
||||
char *nextBatch = NULL;
|
||||
char *currBatch = NULL;
|
||||
char *timeout = NULL;
|
||||
|
||||
char *err;
|
||||
|
||||
int timeoutDuration;
|
||||
|
||||
/* TODO: Respect `timeout', (and stop when something is
|
||||
* pushed, maybe by 'polling' the database? sounds like
|
||||
* a bad idea) */
|
||||
|
||||
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");
|
||||
timeout = HashMapGet(params, "timeout");
|
||||
timeoutDuration = atoi(timeout);
|
||||
|
||||
|
||||
if (!prevBatch)
|
||||
{
|
||||
prevBatch = NULL;
|
||||
nextBatch = UserInitSyncDiff(user);
|
||||
UserFillSyncDiff(user, nextBatch);
|
||||
}
|
||||
else if (timeout && timeoutDuration)
|
||||
{
|
||||
char *name = StrDuplicate(UserGetName(user));
|
||||
int passed = 0;
|
||||
|
||||
UserUnlock(user);
|
||||
|
||||
/* TODO: Unlocking the user for other threads to notify us is not
|
||||
* wise.
|
||||
* Also, Telodendria will NOT reply to ^C while someone is executing
|
||||
* a sync. */
|
||||
while (passed < timeoutDuration)
|
||||
{
|
||||
if (UserGetNotification(name))
|
||||
{
|
||||
break;
|
||||
}
|
||||
UtilSleepMillis(100);
|
||||
passed += 100;
|
||||
}
|
||||
Free(name);
|
||||
user = UserAuthenticate(db, token);
|
||||
}
|
||||
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. */
|
||||
sync.rooms.invite = HashMapCreate();
|
||||
sync.rooms.join = HashMapCreate();
|
||||
|
||||
/* invites */
|
||||
invites = UserGetInvites(user, currBatch);
|
||||
for (i = 0; i < ArraySize(invites); i++)
|
||||
{
|
||||
char *roomId = ArrayGet(invites, i);
|
||||
InvitedRooms invited = { 0 };
|
||||
HashMap *invitedObject;
|
||||
|
||||
invited.invite_state.events = ArrayCreate();
|
||||
invitedObject = InvitedRoomsToJson(&invited);
|
||||
JsonSet(
|
||||
sync.rooms.invite,
|
||||
JsonValueObject(invitedObject),
|
||||
1, roomId
|
||||
);
|
||||
InvitedRoomsFree(&invited);
|
||||
}
|
||||
UserFreeList(invites);
|
||||
|
||||
/* Joins */
|
||||
joins = UserGetJoins(user, currBatch);
|
||||
for (i = 0; i < ArraySize(joins); i++)
|
||||
{
|
||||
char *roomId = ArrayGet(joins, i);
|
||||
Array *el = UserGetEvents(user, currBatch, roomId);
|
||||
size_t j;
|
||||
Room *r = RoomLock(db, roomId);
|
||||
HashMap *state = StateCurrent(r);
|
||||
char *firstEvent = NULL;
|
||||
JoinedRooms joined = { 0 };
|
||||
HashMap *joinedObj;
|
||||
char *type, *key, *id;
|
||||
|
||||
joined.timeline.events = ArrayCreate();
|
||||
|
||||
for (j = 0; j < ArraySize(el); j++)
|
||||
{
|
||||
char *event = ArrayGet(el, j);
|
||||
HashMap *e = RoomEventFetch(r, event);
|
||||
ClientEventWithoutRoomID rc = ClientfyEventSync(e);
|
||||
ClientEventWithoutRoomID *c = Malloc(sizeof(*c));
|
||||
memcpy(c, &rc, sizeof(*c));
|
||||
|
||||
JsonFree(e);
|
||||
|
||||
if (j == 0)
|
||||
{
|
||||
firstEvent = c->event_id;
|
||||
}
|
||||
|
||||
ArrayAdd(joined.timeline.events, c);
|
||||
}
|
||||
joined.timeline.prev_batch = UserNewMessageToken(
|
||||
user, roomId, firstEvent
|
||||
);
|
||||
/* TODO: Don't shove the entire state.
|
||||
* That's a recipe for disaster, especially on large rooms. */
|
||||
joined.state.events = ArrayCreate();
|
||||
while (StateIterate(state, &type, &key, (void **) &id))
|
||||
{
|
||||
HashMap *e = RoomEventFetch(r, id);
|
||||
StrippedStateEvent rs = StripStateEventSync(e);
|
||||
StrippedStateEvent *s = Malloc(sizeof(*s));
|
||||
memcpy(s, &rs, sizeof(*s));
|
||||
|
||||
JsonFree(e);
|
||||
|
||||
ArrayAdd(joined.state.events, s);
|
||||
Free(type);
|
||||
Free(key);
|
||||
}
|
||||
|
||||
StateFree(state);
|
||||
RoomUnlock(r);
|
||||
UserFreeList(el);
|
||||
|
||||
joinedObj = JoinedRoomsToJson(&joined);
|
||||
HashMapSet(sync.rooms.join, roomId, JsonValueObject(joinedObj));
|
||||
JoinedRoomsFree(&joined);
|
||||
}
|
||||
UserFreeList(joins);
|
||||
|
||||
if (prevBatch)
|
||||
{
|
||||
UserDropSync(user, prevBatch);
|
||||
nextBatch = UserInitSyncDiff(user);
|
||||
UserFillSyncDiff(user, nextBatch);
|
||||
}
|
||||
sync.next_batch = nextBatch;
|
||||
response = SyncResponseToJson(&sync);
|
||||
SyncResponseFree(&sync);
|
||||
finish:
|
||||
UserUnlock(user);
|
||||
(void) path;
|
||||
return response;
|
||||
}
|
|
@ -37,6 +37,9 @@ ROUTE_IMPL(RouteVersions, path, argp)
|
|||
(void) argp;
|
||||
|
||||
#define DECLARE_SPEC_VERSION(x) ArrayAdd(versions, JsonValueString(x))
|
||||
DECLARE_SPEC_VERSION("v1.0");
|
||||
DECLARE_SPEC_VERSION("v1.1");
|
||||
|
||||
DECLARE_SPEC_VERSION("v1.2");
|
||||
DECLARE_SPEC_VERSION("v1.3");
|
||||
DECLARE_SPEC_VERSION("v1.4");
|
||||
|
|
449
src/State.c
449
src/State.c
|
@ -26,16 +26,208 @@
|
|||
#include <State.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Array.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <Room.h>
|
||||
#include <Event.h>
|
||||
#include <Room.h>
|
||||
|
||||
static HashMap *
|
||||
StateResolveV1(Array * states)
|
||||
int
|
||||
V1Cmp(void *a, void *b)
|
||||
{
|
||||
(void) states;
|
||||
return NULL;
|
||||
HashMap *e1 = a, *e2 = b;
|
||||
int64_t depth1, depth2;
|
||||
|
||||
depth1 =
|
||||
JsonValueAsInteger(JsonGet(e1, 1, "depth"));
|
||||
depth2 =
|
||||
JsonValueAsInteger(JsonGet(e2, 1, "depth"));
|
||||
|
||||
if (depth1 > depth2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (depth1 < depth2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *e1id =
|
||||
JsonValueAsString(JsonGet(e1, 1, "event_id"));
|
||||
char *e2id =
|
||||
JsonValueAsString(JsonGet(e2, 1, "event_id"));
|
||||
unsigned char *sha1 = Sha1(e1id);
|
||||
unsigned char *sha2 = Sha1(e2id);
|
||||
char *str1 = ShaToHex(sha1);
|
||||
char *str2 = ShaToHex(sha2);
|
||||
int ret = strcmp(str1, str2) * -1;
|
||||
|
||||
Free(str1);
|
||||
Free(str2);
|
||||
Free(sha1);
|
||||
Free(sha2);
|
||||
|
||||
/* Descending */
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
static HashMap *
|
||||
StateResolveV1(Room * room, Array * states)
|
||||
{
|
||||
HashMap *R = HashMapCreate();
|
||||
HashMap *conflicts = HashMapCreate();
|
||||
Array *events = NULL, *types = NULL, *conflicting = NULL;
|
||||
size_t i;
|
||||
ssize_t j;
|
||||
|
||||
char *type, *key, *event_id;
|
||||
|
||||
for (i = 0; i < ArraySize(states); i++)
|
||||
{
|
||||
HashMap *state = ArrayGet(states, i);
|
||||
char *tuple;
|
||||
while (HashMapIterate(state, &tuple, (void **) &event_id))
|
||||
{
|
||||
if (HashMapGet(R, tuple))
|
||||
{
|
||||
Array *arr;
|
||||
HashMap *hm;
|
||||
|
||||
/* Conflicts! */
|
||||
HashMapDelete(R, tuple);
|
||||
arr = HashMapGet(conflicts, tuple);
|
||||
if (!arr)
|
||||
{
|
||||
arr = ArrayCreate();
|
||||
}
|
||||
hm = RoomEventFetch(room, event_id);
|
||||
ArrayAdd(arr, hm);
|
||||
HashMapSet(conflicts, tuple, arr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add to R */
|
||||
HashMapSet(R, tuple, StrDuplicate(event_id));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/* R and conflicts are now configured */
|
||||
types = ArrayCreate();
|
||||
ArrayAdd(types, "m.room.power_levels");
|
||||
ArrayAdd(types, "m.room.join_rules");
|
||||
ArrayAdd(types, "m.room.member");
|
||||
for (i = 0; i < ArraySize(types); i++)
|
||||
{
|
||||
char *t = ArrayGet(types, i);
|
||||
HashMap *first;
|
||||
Array *state_keys;
|
||||
|
||||
events = ArrayCreate();
|
||||
while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
|
||||
{
|
||||
if (StrEquals(type, t))
|
||||
{
|
||||
for (j = 0; j < (ssize_t) ArraySize(conflicting); j++)
|
||||
{
|
||||
HashMap *event = ArrayGet(conflicting, j);
|
||||
ArrayAdd(events, event);
|
||||
}
|
||||
}
|
||||
Free(type);
|
||||
Free(key);
|
||||
}
|
||||
ArraySort(events, V1Cmp);
|
||||
/* Add first event. */
|
||||
first = ArrayDelete(events, 0);
|
||||
StateSet(
|
||||
R,
|
||||
JsonValueAsString(JsonGet(first, 1, "type")),
|
||||
JsonValueAsString(JsonGet(first, 1, "state_key")),
|
||||
JsonValueAsString(JsonGet(first, 1, "event_id")));
|
||||
JsonFree(first);
|
||||
|
||||
for (j = 0; j < (ssize_t) ArraySize(events); j++)
|
||||
{
|
||||
HashMap *event = ArrayGet(events, j);
|
||||
PduV1 pdu;
|
||||
char *msg;
|
||||
|
||||
PduV1FromJson(event, &pdu, &msg);
|
||||
if (RoomAuthoriseEventV1(room, pdu, R))
|
||||
{
|
||||
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
PduV1Free(&pdu);
|
||||
JsonFree(event);
|
||||
break;
|
||||
}
|
||||
(void) msg;
|
||||
PduV1Free(&pdu);
|
||||
JsonFree(event);
|
||||
}
|
||||
ArrayFree(events);
|
||||
/* Delete all elements within a key. */
|
||||
state_keys = ArrayCreate();
|
||||
while (StateIterate(conflicts, &type, &key, (void **) &event_id))
|
||||
{
|
||||
if (StrEquals(type, t))
|
||||
{
|
||||
ArrayAdd(state_keys, key);
|
||||
}
|
||||
Free(type);
|
||||
}
|
||||
for (j = 0; j < (ssize_t) ArraySize(state_keys); j++)
|
||||
{
|
||||
char *state_key = ArrayGet(state_keys, j);
|
||||
StateSet(conflicts, t, state_key, NULL);
|
||||
Free(state_key);
|
||||
}
|
||||
ArrayFree(state_keys);
|
||||
}
|
||||
ArrayFree(types);
|
||||
|
||||
while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
|
||||
{
|
||||
ArraySort(conflicting, V1Cmp);
|
||||
for (j = ArraySize(conflicting) - 1; j >= 0; j--)
|
||||
{
|
||||
HashMap *event = ArrayGet(events, j);
|
||||
PduV1 pdu;
|
||||
char *msg;
|
||||
|
||||
PduV1FromJson(event, &pdu, &msg);
|
||||
|
||||
if (RoomAuthoriseEventV1(room, pdu, R))
|
||||
{
|
||||
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
||||
PduV1Free(&pdu);
|
||||
break;
|
||||
}
|
||||
(void) msg;
|
||||
PduV1Free(&pdu);
|
||||
}
|
||||
Free(type);
|
||||
Free(key);
|
||||
}
|
||||
while (HashMapIterate(conflicts, &type, (void **) &conflicting))
|
||||
{
|
||||
for (i = 0; i < ArraySize(conflicting); i++)
|
||||
{
|
||||
JsonFree(ArrayGet(conflicting, i));
|
||||
}
|
||||
ArrayFree(conflicting);
|
||||
}
|
||||
HashMapFree(conflicts);
|
||||
|
||||
return R;
|
||||
}
|
||||
|
||||
static HashMap *
|
||||
|
@ -45,6 +237,18 @@ StateResolveV2(Array * states)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static HashMap *
|
||||
StateFromPrevs(Room *room, Array *states)
|
||||
{
|
||||
switch (RoomVersionGet(room))
|
||||
{
|
||||
case 1:
|
||||
return StateResolveV1(room, states);
|
||||
default:
|
||||
return StateResolveV2(states);
|
||||
}
|
||||
}
|
||||
|
||||
HashMap *
|
||||
StateResolve(Room * room, HashMap * event)
|
||||
{
|
||||
|
@ -53,36 +257,253 @@ StateResolve(Room * room, HashMap * event)
|
|||
|
||||
Array *prevEvents;
|
||||
|
||||
HashMap *ret_state;
|
||||
|
||||
char *room_id, *event_id;
|
||||
|
||||
Db *db;
|
||||
|
||||
if (!room || !event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Return cached state if it exists */
|
||||
db = RoomGetDB(room);
|
||||
room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
||||
event_id = JsonValueAsString(HashMapGet(event, "event_id"));
|
||||
if (DbExists(db, 4, "rooms", room_id, "state", event_id))
|
||||
{
|
||||
DbRef *ref = DbLock(db, 4,
|
||||
"rooms", room_id, "state", event_id
|
||||
);
|
||||
ret_state = StateDeserialise(DbJson(ref));
|
||||
DbUnlock(db, ref);
|
||||
if (ret_state)
|
||||
{
|
||||
return ret_state;
|
||||
}
|
||||
|
||||
/* If a DB error stops us from getting an existing state,
|
||||
* recompute it. */
|
||||
}
|
||||
|
||||
|
||||
states = ArrayCreate();
|
||||
if (!states)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prevEvents = HashMapGet(event, "prev_events");
|
||||
prevEvents = JsonValueAsArray(HashMapGet(event, "prev_events"));
|
||||
|
||||
for (i = 0; i < ArraySize(prevEvents); i++)
|
||||
{
|
||||
HashMap *prevEvent = ArrayGet(prevEvents, i);
|
||||
HashMap *prevEvent =
|
||||
RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i)));
|
||||
HashMap *state = StateResolve(room, prevEvent);
|
||||
|
||||
/* TODO: Apply prevEvent to state if it is a state event */
|
||||
if (HashMapGet(prevEvent, "state_key"))
|
||||
{
|
||||
StateSet(
|
||||
state,
|
||||
JsonValueAsString(HashMapGet(prevEvent, "type")),
|
||||
JsonValueAsString(HashMapGet(prevEvent, "state_key")),
|
||||
JsonValueAsString(HashMapGet(prevEvent, "event_id")));
|
||||
}
|
||||
|
||||
ArrayAdd(states, state);
|
||||
JsonFree(prevEvent);
|
||||
}
|
||||
|
||||
ret_state = StateFromPrevs(room, states);
|
||||
|
||||
for (i = 0; i < ArraySize(states); i++)
|
||||
{
|
||||
HashMap *state = ArrayGet(states, i);
|
||||
StateFree(state);
|
||||
}
|
||||
ArrayFree(states);
|
||||
|
||||
if (ret_state)
|
||||
{
|
||||
HashMap *json = StateSerialise(ret_state);
|
||||
DbRef *ref = DbCreate(db, 4, "rooms", room_id, "state", event_id);
|
||||
DbJsonSet(ref, json);
|
||||
JsonFree(json);
|
||||
DbUnlock(db, ref);
|
||||
}
|
||||
|
||||
return ret_state;
|
||||
}
|
||||
HashMap *
|
||||
StateCurrent(Room *room)
|
||||
{
|
||||
Array *prevEvents;
|
||||
Array *states;
|
||||
size_t i;
|
||||
HashMap *ret;
|
||||
if (!room)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prevEvents = RoomPrevEventsGet(room);
|
||||
states = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(prevEvents); i++)
|
||||
{
|
||||
HashMap *event =
|
||||
JsonValueAsObject(ArrayGet(prevEvents, i));
|
||||
HashMap *state = StateResolve(room, event);
|
||||
|
||||
|
||||
if (HashMapGet(event, "state_key"))
|
||||
{
|
||||
StateSet(
|
||||
state,
|
||||
JsonValueAsString(HashMapGet(event, "type")),
|
||||
JsonValueAsString(HashMapGet(event, "state_key")),
|
||||
JsonValueAsString(HashMapGet(event, "event_id")));
|
||||
}
|
||||
|
||||
ArrayAdd(states, state);
|
||||
}
|
||||
|
||||
switch (RoomVersionGet(room))
|
||||
ret = StateFromPrevs(room, states);
|
||||
|
||||
for (i = 0; i < ArraySize(states); i++)
|
||||
{
|
||||
case 1:
|
||||
return StateResolveV1(states);
|
||||
default:
|
||||
return StateResolveV2(states);
|
||||
HashMap *state = ArrayGet(states, i);
|
||||
StateFree(state);
|
||||
}
|
||||
ArrayFree(states);
|
||||
|
||||
return ret;
|
||||
}
|
||||
bool StateIterate(HashMap *state, char **type, char **key, void **event)
|
||||
{
|
||||
char *tuple;
|
||||
bool ret;
|
||||
if (!state || !type || !key || !event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = HashMapIterate(state, &tuple, event);
|
||||
if (ret)
|
||||
{
|
||||
tuple = StrDuplicate(tuple);
|
||||
*(strchr(tuple, ',')) = '\0';
|
||||
|
||||
*type = tuple;
|
||||
*key = StrDuplicate(tuple + strlen(tuple) + 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
char *
|
||||
StateGet(HashMap *state, char *type, char *key)
|
||||
{
|
||||
char *full_string;
|
||||
char *ret;
|
||||
if (!state || !type || !key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
full_string = StrConcat(3, type, ",", key);
|
||||
ret = HashMapGet(state, full_string);
|
||||
Free(full_string);
|
||||
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
StateSet(HashMap *state, char *type, char *key, char *event)
|
||||
{
|
||||
char *full_string, *old;
|
||||
if (!state || !type || !key)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
full_string = StrConcat(3, type, ",", key);
|
||||
old = HashMapDelete(state, full_string);
|
||||
if (old)
|
||||
{
|
||||
Free(old);
|
||||
}
|
||||
if (event)
|
||||
{
|
||||
HashMapSet(state, full_string, StrDuplicate(event));
|
||||
}
|
||||
Free(full_string);
|
||||
}
|
||||
void
|
||||
StateFree(HashMap *state)
|
||||
{
|
||||
char *full;
|
||||
char *event_id;
|
||||
|
||||
if (!state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
while (HashMapIterate(state, &full, (void **) &event_id))
|
||||
{
|
||||
Free(event_id);
|
||||
}
|
||||
HashMapFree(state);
|
||||
}
|
||||
HashMap *
|
||||
StateDeserialise(HashMap *json_state)
|
||||
{
|
||||
HashMap *raw_state;
|
||||
|
||||
char *state_type;
|
||||
JsonValue *state_keys;
|
||||
|
||||
if (!json_state)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
raw_state = HashMapCreate();
|
||||
|
||||
while (HashMapIterate(json_state, &state_type, (void **) &state_keys))
|
||||
{
|
||||
HashMap *state_keys_obj = JsonValueAsObject(state_keys);
|
||||
char *state_key;
|
||||
JsonValue *event_id;
|
||||
|
||||
while (HashMapIterate(state_keys_obj, &state_key, (void **) &event_id))
|
||||
{
|
||||
char *eid_string = JsonValueAsString(event_id);
|
||||
char *key_name = StrConcat(3, state_type, ",", state_key);
|
||||
|
||||
HashMapSet(raw_state, key_name, StrDuplicate(eid_string));
|
||||
|
||||
Free(key_name);
|
||||
}
|
||||
}
|
||||
|
||||
return raw_state;
|
||||
}
|
||||
HashMap *
|
||||
StateSerialise(HashMap *rawState)
|
||||
{
|
||||
HashMap *returned;
|
||||
char *type, *key, *event;
|
||||
if (!rawState)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
returned = HashMapCreate();
|
||||
while (StateIterate(rawState, &type, &key, (void **) &event))
|
||||
{
|
||||
JsonSet(returned, JsonValueString(event), 2, type, key);
|
||||
Free(type);
|
||||
Free(key);
|
||||
}
|
||||
|
||||
return returned;
|
||||
}
|
||||
|
|
753
src/User.c
753
src/User.c
|
@ -22,16 +22,20 @@
|
|||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <Cytoplasm/Db.h>
|
||||
#include <User.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Parser.h>
|
||||
#include <Room.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
struct User
|
||||
{
|
||||
|
@ -42,6 +46,9 @@ struct User
|
|||
char *deviceId;
|
||||
};
|
||||
|
||||
static pthread_mutex_t pushLock;
|
||||
static HashMap *pushTable = NULL;
|
||||
|
||||
bool
|
||||
UserValidate(char *localpart, char *domain)
|
||||
{
|
||||
|
@ -108,7 +115,7 @@ UserLock(Db * db, char *name)
|
|||
User *user = NULL;
|
||||
DbRef *ref = NULL;
|
||||
|
||||
if (!UserExists(db, name))
|
||||
if (!name || !UserExists(db, name))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -172,16 +179,22 @@ bool
|
|||
UserUnlock(User * user)
|
||||
{
|
||||
bool ret;
|
||||
Db *db;
|
||||
DbRef *ref;
|
||||
|
||||
if (!user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
db = user->db;
|
||||
ref = user->ref;
|
||||
|
||||
Free(user->name);
|
||||
Free(user->deviceId);
|
||||
|
||||
ret = DbUnlock(user->db, user->ref);
|
||||
ret = DbUnlock(db, ref);
|
||||
user->db = NULL;
|
||||
user->ref = NULL;
|
||||
Free(user);
|
||||
|
||||
return ret;
|
||||
|
@ -232,6 +245,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;
|
||||
}
|
||||
|
||||
|
@ -945,3 +961,736 @@ UserIdFree(CommonID * id)
|
|||
Free(id);
|
||||
}
|
||||
}
|
||||
|
||||
HashMap *
|
||||
UserGetTransaction(User *user, char *transaction, char *path)
|
||||
{
|
||||
HashMap *devices;
|
||||
|
||||
if (!user || !transaction || !path)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devices = JsonValueAsObject(
|
||||
HashMapGet(UserGetDevices(user), UserGetDeviceId(user))
|
||||
);
|
||||
|
||||
return JsonDuplicate(JsonValueAsObject(
|
||||
JsonGet(devices, 3, "transactions", path, transaction)
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
UserSetTransaction(User *user, char *transaction, char *path, HashMap *resp)
|
||||
{
|
||||
HashMap *devices;
|
||||
if (!user || !transaction || !path || !resp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
devices = JsonValueAsObject(
|
||||
HashMapGet(UserGetDevices(user), UserGetDeviceId(user))
|
||||
);
|
||||
|
||||
/* Overwrite the transaction */
|
||||
JsonValueFree(JsonSet(
|
||||
devices, JsonValueObject(JsonDuplicate(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"));
|
||||
|
||||
if (!HashMapGet(joins, roomId))
|
||||
{
|
||||
JsonFree(HashMapSet(joins, roomId, JsonValueNull()));
|
||||
}
|
||||
UserNotifyUser(user);
|
||||
}
|
||||
|
||||
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));
|
||||
UserNotifyUser(user);
|
||||
}
|
||||
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);
|
||||
}
|
||||
char *
|
||||
UserInitSyncDiff(User *user)
|
||||
{
|
||||
char *nextBatch;
|
||||
DbRef *syncRef;
|
||||
HashMap *data;
|
||||
|
||||
if (!user)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nextBatch = StrRandom(16);
|
||||
syncRef = DbCreate(user->db, 4, "users", user->name, "sync", nextBatch);
|
||||
if (!syncRef)
|
||||
{
|
||||
Free(nextBatch);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = DbJson(syncRef);
|
||||
|
||||
HashMapSet(data, "nextBatch", JsonValueString(nextBatch));
|
||||
HashMapSet(data, "invites", JsonValueArray(ArrayCreate()));
|
||||
HashMapSet(data, "leaves", JsonValueArray(ArrayCreate()));
|
||||
HashMapSet(data, "joins", JsonValueObject(HashMapCreate()));
|
||||
|
||||
HashMapSet(data, "creation", JsonValueInteger(UtilTsMillis()));
|
||||
|
||||
DbUnlock(user->db, syncRef);
|
||||
|
||||
return nextBatch;
|
||||
}
|
||||
|
||||
void
|
||||
UserPushInviteSync(User *user, char *roomId)
|
||||
{
|
||||
DbRef *syncRef;
|
||||
HashMap *data;
|
||||
Array *entries;
|
||||
Array *invites;
|
||||
size_t i;
|
||||
if (!user || !roomId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entries = DbList(user->db, 3, "users", user->name, "sync");
|
||||
for (i = 0; i < ArraySize(entries); i++)
|
||||
{
|
||||
char *entry = ArrayGet(entries, i);
|
||||
syncRef = DbLock(user->db, 4, "users", user->name, "sync", entry);
|
||||
data = DbJson(syncRef);
|
||||
invites = JsonValueAsArray(HashMapGet(data, "invites"));
|
||||
|
||||
ArrayAdd(invites, JsonValueString(roomId));
|
||||
|
||||
DbUnlock(user->db, syncRef);
|
||||
}
|
||||
DbListFree(entries);
|
||||
UserNotifyUser(user);
|
||||
}
|
||||
void
|
||||
UserPushJoinSync(User *user, char *roomId)
|
||||
{
|
||||
DbRef *syncRef;
|
||||
HashMap *data;
|
||||
Array *entries;
|
||||
HashMap *join;
|
||||
size_t i;
|
||||
if (!user || !roomId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entries = DbList(user->db, 3, "users", user->name, "sync");
|
||||
for (i = 0; i < ArraySize(entries); i++)
|
||||
{
|
||||
char *entry = ArrayGet(entries, i);
|
||||
HashMap *joinEntry;
|
||||
syncRef = DbLock(user->db, 4, "users", user->name, "sync", entry);
|
||||
data = DbJson(syncRef);
|
||||
join = JsonValueAsObject(HashMapGet(data, "joins"));
|
||||
|
||||
/* TODO */
|
||||
joinEntry = HashMapCreate();
|
||||
HashMapSet(joinEntry, "timeline", JsonValueArray(ArrayCreate()));
|
||||
|
||||
if (!HashMapGet(join, roomId))
|
||||
{
|
||||
JsonFree(HashMapSet(join, roomId, JsonValueObject(joinEntry)));
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonFree(joinEntry);
|
||||
}
|
||||
|
||||
DbUnlock(user->db, syncRef);
|
||||
}
|
||||
DbListFree(entries);
|
||||
UserNotifyUser(user);
|
||||
}
|
||||
|
||||
void
|
||||
UserPushEvent(User *user, HashMap *event)
|
||||
{
|
||||
DbRef *syncRef;
|
||||
HashMap *data;
|
||||
Array *entries;
|
||||
HashMap *join;
|
||||
size_t i;
|
||||
char *roomId, *eventId;
|
||||
if (!user || !event)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
roomId = JsonValueAsString(HashMapGet(event, "room_id"));
|
||||
eventId = JsonValueAsString(HashMapGet(event, "event_id"));
|
||||
|
||||
UserPushJoinSync(user, roomId);
|
||||
|
||||
entries = DbList(user->db, 3, "users", user->name, "sync");
|
||||
for (i = 0; i < ArraySize(entries); i++)
|
||||
{
|
||||
char *entry = ArrayGet(entries, i);
|
||||
HashMap *joinEntry;
|
||||
Array *timeline;
|
||||
syncRef = DbLock(user->db, 4, "users", user->name, "sync", entry);
|
||||
data = DbJson(syncRef);
|
||||
join = JsonValueAsObject(HashMapGet(data, "joins"));
|
||||
|
||||
joinEntry = JsonValueAsObject(HashMapGet(join, roomId));
|
||||
timeline = JsonValueAsArray(HashMapGet(joinEntry, "timeline"));
|
||||
|
||||
ArrayAdd(timeline, JsonValueString(eventId));
|
||||
|
||||
DbUnlock(user->db, syncRef);
|
||||
}
|
||||
DbListFree(entries);
|
||||
|
||||
UserNotifyUser(user);
|
||||
}
|
||||
void
|
||||
UserDropSync(User *user, char *batch)
|
||||
{
|
||||
if (!user || !batch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
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"));
|
||||
Array *timeline = ArrayCreate();
|
||||
Room *r = RoomLock(user->db, roomId);
|
||||
Array *prevs = RoomPrevEventsGet(r);
|
||||
size_t j;
|
||||
|
||||
for (j = 0; j < ArraySize(prevs); j++)
|
||||
{
|
||||
HashMap *e = JsonValueAsObject(ArrayGet(prevs, j));
|
||||
/* TODO: Backfill the user a 'lil more. */
|
||||
ArrayAdd(timeline, JsonValueDuplicate(HashMapGet(e, "event_id")));
|
||||
}
|
||||
RoomUnlock(r);
|
||||
|
||||
joinEntry = HashMapCreate();
|
||||
HashMapSet(joinEntry, "timeline", JsonValueArray(timeline));
|
||||
|
||||
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)
|
||||
{
|
||||
Db *db;
|
||||
DbRef *syncRef;
|
||||
HashMap *data;
|
||||
Array *keys;
|
||||
size_t i;
|
||||
if (!user || !batch)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db = user->db;
|
||||
syncRef = DbLock(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(db, syncRef);
|
||||
return keys;
|
||||
}
|
||||
Array *
|
||||
UserGetEvents(User *user, char *batch, char *roomId)
|
||||
{
|
||||
DbRef *syncRef;
|
||||
HashMap *data;
|
||||
HashMap *joins;
|
||||
Array *keys, *ret;
|
||||
size_t i;
|
||||
if (!user || !batch || !roomId)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
syncRef = DbLock(user->db, 4, "users", user->name, "sync", batch);
|
||||
if (!syncRef)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = DbJson(syncRef);
|
||||
|
||||
joins = JsonValueAsObject(JsonGet(data, 2, "joins", roomId));
|
||||
|
||||
keys = (JsonValueAsArray(HashMapGet(joins, "timeline")));
|
||||
ret = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(keys); i++)
|
||||
{
|
||||
char *str = JsonValueAsString(ArrayGet(keys, i));
|
||||
|
||||
ArrayAdd(ret, StrDuplicate(str));
|
||||
}
|
||||
|
||||
DbUnlock(user->db, syncRef);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
UserNewMessageToken(User *user, char *room, char *event)
|
||||
{
|
||||
DbRef *messageRef;
|
||||
HashMap *json;
|
||||
char *messageToken;
|
||||
if (!user || !room || !event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
messageToken = StrRandom(16);
|
||||
messageRef = DbCreate(user->db,
|
||||
4, "users", user->name, "msg", messageToken
|
||||
);
|
||||
json = DbJson(messageRef);
|
||||
|
||||
HashMapSet(json, "room", JsonValueString(room));
|
||||
HashMapSet(json, "from", JsonValueString(event));
|
||||
|
||||
DbUnlock(user->db, messageRef);
|
||||
return messageToken;
|
||||
}
|
||||
|
||||
Array *
|
||||
UserFetchMessages(User *user, int n, char *token, char **next)
|
||||
{
|
||||
Array *messages = NULL;
|
||||
Array *nexts = NULL;
|
||||
DbRef *messageRef;
|
||||
HashMap *json;
|
||||
Room *room;
|
||||
char *roomId;
|
||||
size_t i;
|
||||
|
||||
bool limited = false;
|
||||
bool dir = false;
|
||||
|
||||
if (!user || !token || n == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
n = -n;
|
||||
dir = true;
|
||||
}
|
||||
|
||||
messageRef = DbLock(user->db,
|
||||
4, "users", user->name, "msg", token
|
||||
);
|
||||
json = DbJson(messageRef);
|
||||
if (!messageRef)
|
||||
{
|
||||
/* Regenerate a new one */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
roomId = JsonValueAsString(HashMapGet(json, "room"));
|
||||
room = RoomLock(user->db, roomId);
|
||||
|
||||
/* TODO (very important): CHECK IF THE USER IS ABLE TO SEE
|
||||
* HISTORY. THROUGHOUT THE STEPS HERE. */
|
||||
if (!room)
|
||||
{
|
||||
DbUnlock(user->db, messageRef);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nexts = ArrayCreate();
|
||||
messages = ArrayCreate();
|
||||
|
||||
/* A stack of elements to deal with the DAG. */
|
||||
ArrayAdd(nexts,
|
||||
StrDuplicate(JsonValueAsString(HashMapGet(json, "from")))
|
||||
);
|
||||
for (i = 0; i < (size_t) n && ArraySize(nexts); i++)
|
||||
{
|
||||
char *curr = ArrayDelete(nexts, ArraySize(nexts) - 1);
|
||||
HashMap *event = RoomEventFetch(room, curr);
|
||||
Array *prevEvents;
|
||||
size_t j;
|
||||
|
||||
Free(curr);
|
||||
|
||||
/* Push event into our message list. */
|
||||
ArrayAdd(messages, event);
|
||||
|
||||
prevEvents = JsonValueAsArray(HashMapGet(event, "prev_events"));
|
||||
if (dir)
|
||||
{
|
||||
HashMap *unsign = JsonValueAsObject(HashMapGet(event, "unsigned"));
|
||||
|
||||
/* prevEvents is now nextEvents */
|
||||
prevEvents = JsonValueAsArray(HashMapGet(unsign, "next_events"));
|
||||
}
|
||||
|
||||
for (j = 0; j < ArraySize(prevEvents); j++)
|
||||
{
|
||||
char *prevEvent = JsonValueAsString(ArrayGet(prevEvents, j));
|
||||
|
||||
ArrayAdd(nexts, StrDuplicate(prevEvent));
|
||||
}
|
||||
|
||||
if (ArraySize(prevEvents) == 0)
|
||||
{
|
||||
limited = true;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ArraySize(nexts); i++)
|
||||
{
|
||||
Free(ArrayGet(nexts, i));
|
||||
}
|
||||
ArrayFree(nexts);
|
||||
RoomUnlock(room);
|
||||
|
||||
if (next && !limited)
|
||||
{
|
||||
HashMap *lastMessage = ArrayGet(messages, ArraySize(messages) - 1);
|
||||
char *eId = JsonValueAsString(HashMapGet(lastMessage, "event_id"));
|
||||
|
||||
*next = UserNewMessageToken(user, roomId, eId);
|
||||
}
|
||||
|
||||
DbUnlock(user->db, messageRef);
|
||||
for (i = 0; i < ArraySize(messages); i++)
|
||||
{
|
||||
HashMap *e = ArrayGet(messages, i);
|
||||
ArraySet(messages, i, JsonValueObject(e));
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
void
|
||||
UserFreeMessageToken(User *user, char *token)
|
||||
{
|
||||
if (!user || !token)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DbDelete(user->db,
|
||||
4, "users", user->name, "msg", token
|
||||
);
|
||||
}
|
||||
void
|
||||
UserCleanTemporaryData(User *user)
|
||||
{
|
||||
Array *list;
|
||||
size_t i;
|
||||
|
||||
if (!user)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
list = DbList(user->db, 3, "users", user->name, "msg");
|
||||
for (i = 0; i < ArraySize(list); i++)
|
||||
{
|
||||
char *token = ArrayGet(list, i);
|
||||
UserFreeMessageToken(user, token);
|
||||
}
|
||||
DbListFree(list);
|
||||
|
||||
list = DbList(user->db, 3, "users", user->name, "sync");
|
||||
for (i = 0; i < ArraySize(list); i++)
|
||||
{
|
||||
char *token = ArrayGet(list, i);
|
||||
if (UserIsSyncOld(user, token))
|
||||
{
|
||||
UserDropSync(user, token);
|
||||
}
|
||||
}
|
||||
DbListFree(list);
|
||||
}
|
||||
bool
|
||||
UserIsSyncOld(User *user, char *token)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *map;
|
||||
int64_t dt;
|
||||
|
||||
if (!user || !token)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ref = DbLock(user->db, 4, "users", user->name, "sync", token);
|
||||
map = DbJson(ref);
|
||||
|
||||
dt = UtilTsMillis() - JsonValueAsInteger(HashMapGet(map, "creation"));
|
||||
|
||||
DbUnlock(user->db, ref);
|
||||
return dt > (5 * 60 * 1000); /* 5-minutes of timeout. */
|
||||
}
|
||||
bool
|
||||
UserSyncExists(User *user, char *sync)
|
||||
{
|
||||
if (!user || !sync)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return DbLock(user->db, 4, "users", user->name, "sync", sync);
|
||||
}
|
||||
|
||||
|
||||
extern void
|
||||
UserInitialisePushTable(void)
|
||||
{
|
||||
if (pushTable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&pushLock, NULL);
|
||||
|
||||
pthread_mutex_lock(&pushLock);
|
||||
pushTable = HashMapCreate();
|
||||
pthread_mutex_unlock(&pushLock);
|
||||
}
|
||||
|
||||
void
|
||||
UserNotifyUser(User *user)
|
||||
{
|
||||
if (!user || !pushTable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&pushLock);
|
||||
HashMapSet(pushTable, user->name, user->name);
|
||||
pthread_mutex_unlock(&pushLock);
|
||||
}
|
||||
|
||||
bool
|
||||
UserGetNotification(char *user)
|
||||
{
|
||||
bool ret;
|
||||
if (!user || !pushTable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
pthread_mutex_lock(&pushLock);
|
||||
ret = !!HashMapGet(pushTable, user);
|
||||
HashMapDelete(pushTable, user);
|
||||
pthread_mutex_unlock(&pushLock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
UserDestroyPushTable(void)
|
||||
{
|
||||
if (!pushTable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&pushLock);
|
||||
HashMapFree(pushTable);
|
||||
pushTable = NULL;
|
||||
pthread_mutex_unlock(&pushLock);
|
||||
pthread_mutex_destroy(&pushLock);
|
||||
}
|
||||
|
|
|
@ -79,6 +79,16 @@
|
|||
*/
|
||||
extern int CanonicalJsonEncode(HashMap *, Stream *);
|
||||
|
||||
/**
|
||||
* Computes a JSON object encoded as Canonical JSON's SHA-256
|
||||
* hash.
|
||||
*
|
||||
* This function returns a SHA-256 hexstream stored on the heap,
|
||||
* which will need to be freed with
|
||||
* .Fn Free .
|
||||
*/
|
||||
extern unsigned char * CanonicalJsonHash(HashMap *);
|
||||
|
||||
/**
|
||||
* Encode a JSON value following the rules of Canonical JSON.
|
||||
* See the documentation for
|
||||
|
|
|
@ -41,6 +41,10 @@
|
|||
#include <Cytoplasm/Db.h>
|
||||
|
||||
#include <Schema/RoomCreateRequest.h>
|
||||
#include <Schema/PduV1.h>
|
||||
|
||||
#include <Parser.h>
|
||||
#include <User.h>
|
||||
|
||||
/**
|
||||
* The functions in this API operate on an opaque structure.
|
||||
|
@ -51,7 +55,7 @@ typedef struct Room Room;
|
|||
* Create a new room in the given database using the given
|
||||
* RoomCreateRequest.
|
||||
*/
|
||||
extern Room * RoomCreate(Db *, RoomCreateRequest *);
|
||||
extern Room * RoomCreate(Db *, User *, RoomCreateRequest *, ServerPart);
|
||||
|
||||
/**
|
||||
* Lock the existing room in the specified database,
|
||||
|
@ -63,6 +67,11 @@ extern Room * RoomCreate(Db *, RoomCreateRequest *);
|
|||
*/
|
||||
extern Room * RoomLock(Db *, char *);
|
||||
|
||||
/**
|
||||
* Returns the database structure a room is tied to.
|
||||
*/
|
||||
extern Db * RoomGetDB(Room *);
|
||||
|
||||
/**
|
||||
* Unlock a room handle, returning it to the database.
|
||||
* This function returns the result of calling
|
||||
|
@ -96,6 +105,13 @@ extern int RoomVersionGet(Room *);
|
|||
* client events.
|
||||
*/
|
||||
extern HashMap * RoomStateGet(Room *);
|
||||
/**
|
||||
* Resolves the room's state before a specific point,
|
||||
* (with the event hashmap taking priority),
|
||||
* like
|
||||
* .Fn RoomStateGet .
|
||||
*/
|
||||
extern HashMap * RoomStateGetID(Room *, char *);
|
||||
|
||||
/**
|
||||
* Get a list of the most recent events in the
|
||||
|
@ -128,4 +144,94 @@ extern int RoomPrevEventsSet(Room *, Array *);
|
|||
*/
|
||||
extern HashMap * RoomEventSend(Room *, HashMap *);
|
||||
|
||||
/**
|
||||
* Sends an invite to a user in a room, and tries
|
||||
* to notify such user of it.
|
||||
*/
|
||||
extern void RoomSendInvite(User *, bool, char *, Room *);
|
||||
|
||||
/**
|
||||
* Fetch a single event's PDU in a room into an
|
||||
* hashmap, given an event ID, from the database
|
||||
* if possible, or otherwise fetched from a remote
|
||||
* homeserver participating in the 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.
|
||||
*/
|
||||
extern bool RoomAuthoriseEventV1(Room *, PduV1, HashMap *);
|
||||
|
||||
/**
|
||||
* Gets the room's creator as a ServerPart. This value should
|
||||
* not be freed, as it lives alongside the room itself
|
||||
*/
|
||||
extern ServerPart RoomGetCreator(Room *);
|
||||
|
||||
/**
|
||||
* Puts a PDUv1 into the event list, while updating the leaf
|
||||
* list.
|
||||
*/
|
||||
extern bool RoomAddEventV1(Room *, PduV1);
|
||||
|
||||
/**
|
||||
* Creates a barebones JSON object to be sent to
|
||||
* .Fn RoomEventFetch .
|
||||
*/
|
||||
extern HashMap * RoomEventCreate(char *, char *, char *, HashMap *);
|
||||
|
||||
/**
|
||||
* Computes an approximation of the PDU depth by looking at
|
||||
* the leaves stored in the room data.
|
||||
*/
|
||||
extern uint64_t RoomGetDepth(Room *);
|
||||
|
||||
/**
|
||||
* Tries to find an alias from room alias to an ID stored
|
||||
* on the heap, or NULL if it does not exist.
|
||||
*/
|
||||
extern char * RoomResolveAlias(Db *, char *);
|
||||
|
||||
/**
|
||||
* Tries to resolve a list of aliases from a room ID into
|
||||
* an array of strings stored on the heap.
|
||||
*/
|
||||
extern Array * RoomReverseAlias(Db *, char *);
|
||||
|
||||
/**
|
||||
* Frees the array returned by
|
||||
* .Fn RoomReverseAlias .
|
||||
*/
|
||||
extern void RoomFreeReverse(Array *);
|
||||
|
||||
/**
|
||||
* Checks whenever a room contains a specific user.
|
||||
*/
|
||||
extern bool RoomContainsUser(Room *, char *);
|
||||
|
||||
/**
|
||||
* Checks whenever an user can join a specific room,
|
||||
* given it's permissions.
|
||||
*/
|
||||
extern bool RoomCanJoin(Room *, char *);
|
||||
|
||||
/**
|
||||
* Makes a local user join a room, and returns true if
|
||||
* the room was joined.
|
||||
*/
|
||||
extern bool RoomJoin(Room *, User *);
|
||||
|
||||
/**
|
||||
* Adds or overwrites a room alias.
|
||||
*/
|
||||
extern void RoomAddAlias(Db *, char *, char *, char *, char *);
|
||||
|
||||
#endif /* TELODENDRIA_ROOM_H */
|
||||
|
|
|
@ -100,6 +100,14 @@ ROUTE(RouteConfig);
|
|||
ROUTE(RoutePrivileges);
|
||||
|
||||
ROUTE(RouteCreateRoom);
|
||||
ROUTE(RouteSendEvent);
|
||||
ROUTE(RouteSendState);
|
||||
ROUTE(RouteJoinRoom);
|
||||
ROUTE(RouteJoinRoomAlias);
|
||||
ROUTE(RouteFetchEvent);
|
||||
ROUTE(RouteJoinedRooms);
|
||||
ROUTE(RouteSync);
|
||||
ROUTE(RouteMessages);
|
||||
|
||||
ROUTE(RouteAliasDirectory);
|
||||
ROUTE(RouteRoomAliases);
|
||||
|
@ -107,7 +115,6 @@ ROUTE(RouteRoomAliases);
|
|||
ROUTE(RouteAdminDeactivate);
|
||||
|
||||
ROUTE(RouteAdminTokens);
|
||||
|
||||
#undef ROUTE
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,11 +47,45 @@ extern char *StateGet(HashMap *, char *, char *);
|
|||
/**
|
||||
* Set a state tuple to a value.
|
||||
*/
|
||||
extern char *StateSet(HashMap *, char *, char *, char *);
|
||||
extern void StateSet(HashMap *, char *, char *, char *);
|
||||
|
||||
/**
|
||||
* Iterates through a statemap, with (type, key) -> event.
|
||||
* The type and keys are stored on the heap, and will need
|
||||
* to be freed.
|
||||
* This function behaves like
|
||||
* .Fn HashMapIterate .
|
||||
*/
|
||||
extern bool StateIterate(HashMap *, char **, char **, void **);
|
||||
|
||||
/**
|
||||
* Compute the room state before the specified event was sent.
|
||||
*/
|
||||
extern HashMap * StateResolve(Room *, HashMap *);
|
||||
|
||||
/**
|
||||
* Computes the current state from the room's leaves.
|
||||
*/
|
||||
extern HashMap * StateCurrent(Room *);
|
||||
|
||||
/**
|
||||
* Frees an entire state table from the heap.
|
||||
*/
|
||||
extern void StateFree(HashMap *);
|
||||
|
||||
/**
|
||||
* Deserialises a state map from JSON to the internal format
|
||||
* used by this API.
|
||||
*
|
||||
* The returned value is independent from the JSON format,
|
||||
* and should be freed with
|
||||
* .Fn StateFree .
|
||||
*/
|
||||
extern HashMap * StateDeserialise(HashMap *);
|
||||
/**
|
||||
* Serialises a state map from the internal format to JSON
|
||||
* used for the database, for example
|
||||
*/
|
||||
extern HashMap * StateSerialise(HashMap *);
|
||||
|
||||
#endif /* TELODENDRIA_STATE_H */
|
||||
|
|
|
@ -91,6 +91,7 @@ typedef struct UserLoginInfo
|
|||
char *refreshToken;
|
||||
} UserLoginInfo;
|
||||
|
||||
|
||||
/**
|
||||
* Take a localpart and domain as separate parameters and validate them
|
||||
* against the rules of the Matrix specification. The reasion the
|
||||
|
@ -303,9 +304,181 @@ extern int UserDecodePrivilege(const char *);
|
|||
*/
|
||||
extern CommonID * UserIdParse(char *, char *);
|
||||
|
||||
/**
|
||||
* Gets the reply sent to the user from a transaction ID, and an opaque
|
||||
* ""path"" ID, which is unique per route.
|
||||
*/
|
||||
extern HashMap * UserGetTransaction(User *, char *, char *);
|
||||
|
||||
/**
|
||||
* Stores a transaction for a path with a given response JSON.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
extern void UserIdFree(CommonID *);
|
||||
|
||||
/**
|
||||
* Initialises a sync diff table, and returns a next_batch parameter,
|
||||
* which is stored on the heap.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
extern void UserPushInviteSync(User *, char *);
|
||||
|
||||
/**
|
||||
* Pushes a join onto all sync diff tables.
|
||||
*/
|
||||
extern void UserPushJoinSync(User *, char *);
|
||||
|
||||
/**
|
||||
* Pushes an event (ID) into every diff table of a user.
|
||||
*/
|
||||
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 *);
|
||||
|
||||
/**
|
||||
* Get a list of event IDs for a diff table(and room ID),
|
||||
* to be freed by
|
||||
* .Fn UserFreeList .
|
||||
*/
|
||||
extern Array * UserGetEvents(User *, char *, char *);
|
||||
|
||||
/**
|
||||
* Drops a sync diff, denoted by it's previous batch ID, if it
|
||||
* exists.
|
||||
*/
|
||||
extern void UserDropSync(User *, char *);
|
||||
|
||||
/**
|
||||
* Verifies if the 'sync' data is considered old, and can be
|
||||
* reasonably removed from the database.
|
||||
*/
|
||||
extern bool UserIsSyncOld(User *, char *);
|
||||
|
||||
/**
|
||||
* Verifies if a sync diff exists.
|
||||
*/
|
||||
extern bool UserSyncExists(User *, char *);
|
||||
|
||||
/**
|
||||
* Creates a new "message token" start from a room and event ID,
|
||||
* given a specified direction(with false<=>'b' and true<=>'f')
|
||||
* to be used by /messages.
|
||||
*/
|
||||
extern char * UserNewMessageToken(User *, char *, char *);
|
||||
|
||||
/**
|
||||
* Grabs events from a message token(with the correct direction)
|
||||
* into an array of HashMaps(which store PDU objects).
|
||||
* If the string pointer is not NULL, this function sets its
|
||||
* value to a new message token(which is stored on the heap),
|
||||
* if and only if, there are any new events to retrieve.
|
||||
*
|
||||
* If the counter is negative, then it is treated as a 'f',
|
||||
* otherwise, it is treated as 'b'.
|
||||
*/
|
||||
extern Array * UserFetchMessages(User *, int, char *, char **);
|
||||
|
||||
/**
|
||||
* Deletes a "message token".
|
||||
*/
|
||||
extern void UserFreeMessageToken(User *, char *);
|
||||
|
||||
|
||||
/**
|
||||
* Deletes temporary data about a specific user.
|
||||
*/
|
||||
extern void UserCleanTemporaryData(User *);
|
||||
|
||||
/**
|
||||
* Initialise a temporary sync table to signal any /sync changes.
|
||||
* This should be paired with a call to
|
||||
* .Fn UserDestroyPushTable .
|
||||
*/
|
||||
extern void UserInitialisePushTable(void);
|
||||
|
||||
/**
|
||||
* Notifies the user of a sync update.
|
||||
*/
|
||||
extern void UserNotifyUser(User *);
|
||||
|
||||
/**
|
||||
* Verifies if the user has been notified, and if it is, then
|
||||
* removes the notification
|
||||
*/
|
||||
extern bool UserGetNotification(char *);
|
||||
|
||||
/**
|
||||
* Destroys the temporary push table created by
|
||||
* .Fn UserInitialisePushTable .
|
||||
*/
|
||||
extern void UserDestroyPushTable(void);
|
||||
#endif /* TELODENDRIA_USER_H */
|
||||
|
|
Loading…
Reference in New Issue