Implement /_matrix/client/versions and /.well-known/matrix/client

This commit is contained in:
Jordan Bancino 2022-09-29 13:41:26 -04:00
parent 13fa07bfcc
commit 45324ce77a
13 changed files with 305 additions and 20 deletions

View File

@ -55,11 +55,13 @@ Phase 2: Building a foundation
Phase 3: Welcome to Matrix
[ ] client-Server API
[~] client-Server API
[x] Error responses
[x] CORS headers
[ ] /_matrix/client/versions
[ ] Well-known URIs
[x] /_matrix/client/versions
[x] Well-known URIs
[x] Make base-url optional in config
[x] Make identity-server optional in config
[ ] Client authentication
[ ] Capabilities negotiation
[ ] Server-Server API
@ -88,7 +90,7 @@ Documentation
[x] Make a note in Getting the Code that the password for the anoncvs account is just anoncvs
[x] Add contributors list
[x] Add list of make.sh recipes and what they do
[ ] Improve Google Lighthouse score on website
[x] Improve Google Lighthouse score on website
[!] Image elements do not have explicit width and height
[x] Background and foreground colors do not have sufficient contrast ratio (msg-error div)
[x] Lists do not contain only <li> elements

View File

@ -3,6 +3,7 @@
#
server-name "localhost";
base-url "http://localhost:8008";
chroot "./chroot";
id "jordan";
data-dir "./data";

View File

@ -10,7 +10,11 @@
#
listen "8008";
server-name "example.com";
base-url "https://matrix.example.com";
identity-server "https://identity.example.com";
chroot "/var/telodendria";
id "_telodendria" "_telodendria";
data-dir "./data";

View File

@ -1,4 +1,4 @@
.Dd $Mdocdate: September 23 2022 $
.Dd $Mdocdate: September 29 2022 $
.Dt TELODENDRIA.CONF 5
.Os Telodendria Project
.Sh NAME
@ -55,6 +55,29 @@ over.
.Ar name
should be a DNS name that can be publically resolved. This directive
is required.
.It Ic base-url Ar url
Set the server's base URL.
.Ar url
should be a valid URL, complete with the protocol. It does not need to
be the same as the server name; in fact, it is common for a subdomain of
the server name to be the base URL for the Matrix homeserver.
.Pp
This URL is the URL at which Matrix clients will connect to the server,
and is thus served as a part of the
.Pa .well-known
manifest.
.Pp
This directive is optional. If it is not specified, it is automatically
deduced from the server name.
.It Ic identity-server Ar url
The identity server that clients should use to perform identity lookups.
.Pp
.Ar url
follows the same rules as
.Ic base-url .
.Pp
This directive is optional. If it is not specified, it is automatically
set to be the same as the base URL.
.It Ic chroot Ar directory
Change the root directory to the specified directory as soon as possible.
Note that all other paths and files specified in

View File

@ -78,7 +78,7 @@ ArrayDelete(Array * array, size_t index)
size_t i;
void *element;
if (!array)
if (!array || array->size <= index)
{
return NULL;
}

View File

@ -31,6 +31,8 @@
#include <Json.h>
#include <Util.h>
#include <Routes.h>
void
MatrixHttpHandler(HttpServerContext * context, void *argp)
{
@ -99,15 +101,39 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
ArrayAdd(pathParts, decoded);
}
/* TODO: Route requests here */
pathPart = MATRIX_PATH_POP(pathParts);
if (MATRIX_PATH_EQUALS(pathPart, ".well-known"))
{
response = RouteWellKnown(args, context, pathParts);
}
else if (MATRIX_PATH_EQUALS(pathPart, "_matrix"))
{
response = RouteMatrix(args, context, pathParts);
}
else
{
HttpResponseStatus(context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_NOT_FOUND);
}
free(pathPart);
HttpSendHeaders(context);
stream = HttpStream(context);
response = MatrixErrorCreate(M_UNKNOWN);
if (!response)
{
Log(lc, LOG_WARNING, "A route handler returned NULL.");
HttpResponseStatus(context, HTTP_INTERNAL_SERVER_ERROR);
response = MatrixErrorCreate(M_UNKNOWN);
}
JsonEncode(response, stream);
fprintf(stream, "\n");
/* By this point, ArraySize(pathParts) should be 0, but just in
* case some elements remain, free them up now */
for (i = 0; i < ArraySize(pathParts); i++)
{
free(ArrayGet(pathParts, i));

68
src/Routes/RouteMatrix.c Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
*
* 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 <string.h>
#include <stdlib.h>
#include <Json.h>
#include <HashMap.h>
ROUTE(RouteMatrix)
{
HashMap *response = NULL;
char *pathPart = MATRIX_PATH_POP(path);
(void) args;
if (!MATRIX_PATH_EQUALS(pathPart, "client") || ArraySize(path) != 1)
{
free(pathPart);
HttpResponseStatus(context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
free(pathPart);
pathPart = MATRIX_PATH_POP(path);
if (MATRIX_PATH_EQUALS(pathPart, "versions"))
{
Array *versions = ArrayCreate();
free(pathPart);
ArrayAdd(versions, JsonValueString(UtilStringDuplicate("v1.4")));
response = HashMapCreate();
HashMapSet(response, "versions", JsonValueArray(versions));
return response;
}
else
{
free(pathPart);
HttpResponseStatus(context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
*
* 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 <string.h>
#include <stdlib.h>
#include <Json.h>
#include <HashMap.h>
ROUTE(RouteWellKnown)
{
HashMap *response = NULL;
char *pathPart = MATRIX_PATH_POP(path);
if (!MATRIX_PATH_EQUALS(pathPart, "matrix") || ArraySize(path) != 1)
{
free(pathPart);
HttpResponseStatus(context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
free(pathPart);
pathPart = MATRIX_PATH_POP(path);
if (MATRIX_PATH_EQUALS(pathPart, "client"))
{
HashMap *homeserver = HashMapCreate();
free(pathPart);
response = HashMapCreate();
HashMapSet(homeserver, "base_url", JsonValueString(UtilStringDuplicate(args->config->baseUrl)));
HashMapSet(response, "m.homeserver", JsonValueObject(homeserver));
if (args->config->identityServer)
{
HashMap *identityServer = HashMapCreate();
HashMapSet(identityServer, "base_url", JsonValueString(UtilStringDuplicate(args->config->identityServer)));
HashMapSet(response, "m.identity_server", identityServer);
}
return response;
}
else
{
free(pathPart);
HttpResponseStatus(context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
}

View File

@ -104,7 +104,7 @@ main(int argc, char **argv)
/* Signal handling */
struct sigaction sigAction;
MatrixHttpHandlerArgs *matrixArgs;
MatrixHttpHandlerArgs matrixArgs;
lc = LogConfigCreate();
@ -250,6 +250,8 @@ main(int argc, char **argv)
LogConfigIndent(lc);
Log(lc, LOG_DEBUG, "Listen On: %d", tConfig->listenPort);
Log(lc, LOG_DEBUG, "Server Name: %s", tConfig->serverName);
Log(lc, LOG_DEBUG, "Base URL: %s", tConfig->baseUrl);
Log(lc, LOG_DEBUG, "Identity Server: %s", tConfig->identityServer);
Log(lc, LOG_DEBUG, "Chroot: %s", tConfig->chroot);
Log(lc, LOG_DEBUG, "Run As: %s:%s", tConfig->uid, tConfig->gid);
Log(lc, LOG_DEBUG, "Data Directory: %s", tConfig->dataDir);
@ -284,19 +286,13 @@ main(int argc, char **argv)
Log(lc, LOG_DEBUG, "Found user/group information using getpwnam() and getgrnam().");
}
matrixArgs = malloc(sizeof(MatrixHttpHandlerArgs));
if (!matrixArgs)
{
Log(lc, LOG_ERROR, "Unable to allocate memory for HTTP handler arguments.");
exit = EXIT_FAILURE;
goto finish;
}
matrixArgs->lc = lc;
/* Arguments to pass into the HTTP handler */
matrixArgs.lc = lc;
matrixArgs.config = tConfig;
/* Bind the socket before possibly dropping permissions */
httpServer = HttpServerCreate(tConfig->listenPort, tConfig->threads, tConfig->maxConnections,
MatrixHttpHandler, matrixArgs);
MatrixHttpHandler, &matrixArgs);
if (!httpServer)
{
Log(lc, LOG_ERROR, "Unable to create HTTP server on port %d: %s",

View File

@ -116,6 +116,45 @@ TelodendriaConfigParse(HashMap * config, LogConfig * lc)
ASSERT_VALUES("server-name", 1);
COPY_VALUE(tConfig->serverName, 0);
directive = (ConfigDirective *) HashMapGet(config, "base-url");
children = ConfigChildrenGet(directive);
value = ConfigValuesGet(directive);
if (directive)
{
ASSERT_NO_CHILDREN("base-url");
ASSERT_VALUES("base-url", 1);
COPY_VALUE(tConfig->baseUrl, 0);
}
else
{
Log(lc, LOG_WARNING, "Base URL not specified. Assuming it's 'https://%s'.", tConfig->serverName);
tConfig->baseUrl = malloc(strlen(tConfig->serverName) + 10);
if (!tConfig->baseUrl)
{
Log(lc, LOG_ERROR, "Error allocating memory for default config value 'base-url'.");
goto error;
}
sprintf(tConfig->baseUrl, "https://%s", tConfig->serverName);
}
directive = (ConfigDirective *) HashMapGet(config, "identity-server");
children = ConfigChildrenGet(directive);
value = ConfigValuesGet(directive);
if (directive)
{
ASSERT_NO_CHILDREN("identity-server");
ASSERT_VALUES("identity-server", 1);
COPY_VALUE(tConfig->identityServer, 0);
}
else
{
Log(lc, LOG_WARNING, "Identity server not specified. No identity server will be advertised.");
tConfig->identityServer = NULL;
}
GET_DIRECTIVE("chroot");
ASSERT_NO_CHILDREN("chroot");
ASSERT_VALUES("chroot", 1);
@ -138,7 +177,7 @@ TelodendriaConfigParse(HashMap * config, LogConfig * lc)
Log(lc, LOG_ERROR,
"Wrong value count in directive 'id': got '%d', but expected 1 or 2.",
ArraySize(value));
break;
goto error;
}
GET_DIRECTIVE("data-dir");
@ -348,6 +387,9 @@ TelodendriaConfigFree(TelodendriaConfig * tConfig)
}
free(tConfig->serverName);
free(tConfig->baseUrl);
free(tConfig->identityServer);
free(tConfig->chroot);
free(tConfig->uid);
free(tConfig->gid);

View File

@ -28,6 +28,12 @@
#include <Log.h>
#include <HashMap.h>
#include <TelodendriaConfig.h>
#define MATRIX_PATH_POP(arr) ArrayDelete(arr, 0)
#define MATRIX_PATH_EQUALS(pathPart, str) ((pathPart != NULL) && (strcmp(pathPart, str) == 0))
typedef enum MatrixError
{
M_FORBIDDEN,
@ -72,6 +78,7 @@ typedef enum MatrixError
typedef struct MatrixHttpHandlerArgs
{
LogConfig *lc;
TelodendriaConfig *config;
} MatrixHttpHandlerArgs;
extern void

39
src/include/Routes.h Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2022 Jordan Bancino <@jordan:bancino.net>
*
* 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.
*/
#ifndef TELODENDRIA_ROUTES_H
#define TELODENDRIA_ROUTES_H
#include <HashMap.h>
#include <Array.h>
#include <HttpServer.h>
#include <Util.h>
#include <Matrix.h>
#define ROUTE(name) HashMap * name(MatrixHttpHandlerArgs *args, HttpServerContext *context, Array *path)
extern ROUTE(RouteWellKnown);
extern ROUTE(RouteMatrix);
#endif

View File

@ -53,6 +53,9 @@ typedef enum TelodendriaConfigFlag
typedef struct TelodendriaConfig
{
char *serverName;
char *baseUrl;
char *identityServer;
char *chroot;
char *uid;
char *gid;