Refactor routing system to use HttpRouter.

This commit is contained in:
Jordan Bancino 2023-04-14 21:20:56 +00:00
parent a90f7c4b9e
commit 83971dfaff
23 changed files with 173 additions and 460 deletions

View file

@ -46,7 +46,8 @@ Milestone: v0.3.0
[x] Replace all usages of curl with http [x] Replace all usages of curl with http
[~] Proper HTTP request router [~] Proper HTTP request router
[x] Support regex matching [x] Support regex matching
[ ] Replace current routing system [x] Replace current routing system
[ ] Add route for requestToken endpoints
[ ] Token permissions [ ] Token permissions
[ ] Move configuration to database [ ] Move configuration to database

View file

@ -178,6 +178,15 @@ main(int argc, char **argv)
} }
} }
Log(LOG_NOTICE, "Building routing tree...");
matrixArgs.router = TelodendriaBuildRouter();
if (!matrixArgs.router)
{
Log(LOG_ERR, "Unable to build routing tree.");
exit = EXIT_FAILURE;
goto finish;
}
Log(LOG_NOTICE, "Processing configuration file '%s'.", configArg); Log(LOG_NOTICE, "Processing configuration file '%s'.", configArg);
config = JsonDecode(configFile); config = JsonDecode(configFile);
@ -237,7 +246,6 @@ main(int argc, char **argv)
Log(LOG_DEBUG, "Changed working directory to: %s", tConfig->dataDir); Log(LOG_DEBUG, "Changed working directory to: %s", tConfig->dataDir);
} }
if (tConfig->flags & CONFIG_LOG_FILE) if (tConfig->flags & CONFIG_LOG_FILE)
{ {
Stream *logFile = StreamOpen("telodendria.log", "a"); Stream *logFile = StreamOpen("telodendria.log", "a");
@ -536,6 +544,9 @@ finish:
DbClose(matrixArgs.db); DbClose(matrixArgs.db);
Log(LOG_DEBUG, "Closed database."); Log(LOG_DEBUG, "Closed database.");
HttpRouterFree(matrixArgs.router);
Log(LOG_DEBUG, "Freed routing tree.");
ConfigFree(tConfig); ConfigFree(tConfig);
Log(LOG_DEBUG, "Exiting with code '%d'.", exit); Log(LOG_DEBUG, "Exiting with code '%d'.", exit);

View file

@ -32,6 +32,7 @@
#include <Json.h> #include <Json.h>
#include <Str.h> #include <Str.h>
#include <HttpRouter.h>
#include <Routes.h> #include <Routes.h>
void void
@ -41,12 +42,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
Stream *stream; Stream *stream;
HashMap *response = NULL; HashMap *response = NULL;
char *key;
char *requestPath; char *requestPath;
char *requestPathCpy;
MATRIX_PATH *pathParts;
char *pathPart;
RouteArgs routeArgs; RouteArgs routeArgs;
requestPath = HttpRequestPath(context); requestPath = HttpRequestPath(context);
@ -74,46 +70,21 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
return; return;
} }
pathParts = MATRIX_PATH_CREATE();
requestPathCpy = StrDuplicate(requestPath);
key = requestPathCpy;
while ((pathPart = strtok_r(key, "/", &key)))
{
char *decoded = HttpUrlDecode(pathPart);
MATRIX_PATH_APPEND(pathParts, decoded);
}
Free(requestPathCpy);
routeArgs.matrixArgs = args; routeArgs.matrixArgs = args;
routeArgs.context = context; routeArgs.context = context;
routeArgs.path = pathParts;
pathPart = MATRIX_PATH_POP(pathParts); if (!HttpRouterRoute(args->router, requestPath, &routeArgs, (void **) &response))
if (MATRIX_PATH_EQUALS(pathPart, ".well-known"))
{
response = RouteWellKnown(&routeArgs);
}
else if (MATRIX_PATH_EQUALS(pathPart, "_matrix"))
{
response = RouteMatrix(&routeArgs);
}
else
{ {
HttpResponseHeader(context, "Content-Type", "application/json");
HttpResponseStatus(context, HTTP_NOT_FOUND); HttpResponseStatus(context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_NOT_FOUND); response = MatrixErrorCreate(M_NOT_FOUND);
} }
Free(pathPart);
/* /*
* If the route handler returned a JSON object, take care * If the route handler returned a JSON object, take care
* of sending it here. * of sending it here.
* *
* Otherwise, if the route handler returned NULL, so assume * Otherwise, if the route handler returned NULL, assume
* that it sent its own headers and and body. * that it sent its own headers and and body.
*/ */
if (response) if (response)
@ -129,17 +100,6 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
StreamPrintf(stream, "\n"); StreamPrintf(stream, "\n");
} }
/*
* By this point, there should be no path parts remaining, but if
* there are, free them up now.
*/
while ((pathPart = MATRIX_PATH_POP(pathParts)) != NULL)
{
Free(pathPart);
}
MATRIX_PATH_FREE(pathParts);
Log(LOG_INFO, "%s %s (%d %s)", Log(LOG_INFO, "%s %s (%d %s)",
HttpRequestMethodToString(HttpRequestMethodGet(context)), HttpRequestMethodToString(HttpRequestMethodGet(context)),
requestPath, requestPath,

View file

@ -45,8 +45,9 @@ PasswordFlow(void)
return ret; return ret;
} }
ROUTE_IMPL(RouteChangePwd, args) ROUTE_IMPL(RouteChangePwd, path, argp)
{ {
RouteArgs *args = argp;
Db *db = args->matrixArgs->db; Db *db = args->matrixArgs->db;
User *user = NULL; User *user = NULL;
@ -64,8 +65,9 @@ ROUTE_IMPL(RouteChangePwd, args)
char *token; char *token;
char *newPassword; char *newPassword;
if (MATRIX_PATH_PARTS(args->path) != 0 || (void) path;
HttpRequestMethodGet(args->context) != HTTP_POST)
if (HttpRequestMethodGet(args->context) != HTTP_POST)
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_UNRECOGNIZED); return MatrixErrorCreate(M_UNRECOGNIZED);

View file

@ -31,8 +31,9 @@
#include <Memory.h> #include <Memory.h>
#include <User.h> #include <User.h>
ROUTE_IMPL(RouteLogin, args) ROUTE_IMPL(RouteLogin, path, argp)
{ {
RouteArgs *args = argp;
HashMap *request = NULL; HashMap *request = NULL;
HashMap *response = NULL; HashMap *response = NULL;
Array *enabledFlows; Array *enabledFlows;
@ -56,11 +57,7 @@ ROUTE_IMPL(RouteLogin, args)
UserLoginInfo *loginInfo; UserLoginInfo *loginInfo;
char *fullUsername; char *fullUsername;
if (MATRIX_PATH_PARTS(args->path) > 0) (void) path;
{
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
switch (HttpRequestMethodGet(args->context)) switch (HttpRequestMethodGet(args->context))
{ {

View file

@ -31,8 +31,9 @@
#include <Memory.h> #include <Memory.h>
#include <User.h> #include <User.h>
ROUTE_IMPL(RouteLogout, args) ROUTE_IMPL(RouteLogout, path, argp)
{ {
RouteArgs *args = argp;
HashMap *response = NULL; HashMap *response = NULL;
char *tokenstr; char *tokenstr;
@ -47,12 +48,6 @@ ROUTE_IMPL(RouteLogout, args)
return MatrixErrorCreate(M_UNRECOGNIZED); return MatrixErrorCreate(M_UNRECOGNIZED);
} }
if (MATRIX_PATH_PARTS(args->path) > 1)
{
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
response = MatrixGetAccessToken(args->context, &tokenstr); response = MatrixGetAccessToken(args->context, &tokenstr);
if (response) if (response)
{ {
@ -66,18 +61,14 @@ ROUTE_IMPL(RouteLogout, args)
return MatrixErrorCreate(M_UNKNOWN_TOKEN); return MatrixErrorCreate(M_UNKNOWN_TOKEN);
} }
if (MATRIX_PATH_PARTS(args->path) == 1) if (ArraySize(path) == 1)
{ {
char *pathPart = MATRIX_PATH_POP(args->path); if (!MATRIX_PATH_EQUALS(ArrayGet(path, 0), "all"))
if (!MATRIX_PATH_EQUALS(pathPart, "all"))
{ {
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND); HttpResponseStatus(args->context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_NOT_FOUND); response = MatrixErrorCreate(M_NOT_FOUND);
goto finish; goto finish;
} }
Free(pathPart);
if (!UserDeleteTokens(user, NULL)) if (!UserDeleteTokens(user, NULL))
{ {

View file

@ -1,166 +0,0 @@
/*
* Copyright (C) 2022-2023 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 <Memory.h>
#include <Json.h>
#include <HashMap.h>
#include <Str.h>
ROUTE_IMPL(RouteMatrix, args)
{
HashMap *response = NULL;
char *pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "static"))
{
Free(pathPart);
return RouteStatic(args);
}
if (!MATRIX_PATH_EQUALS(pathPart, "client") || MATRIX_PATH_PARTS(args->path) < 1)
{
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "versions"))
{
Array *versions = ArrayCreate();
Free(pathPart);
ArrayAdd(versions, JsonValueString("v1.6"));
response = HashMapCreate();
HashMapSet(response, "versions", JsonValueArray(versions));
return response;
}
else if (MATRIX_PATH_EQUALS(pathPart, "v3") ||
MATRIX_PATH_EQUALS(pathPart, "r0"))
{
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "login"))
{
response = RouteLogin(args);
}
else if (MATRIX_PATH_EQUALS(pathPart, "logout"))
{
response = RouteLogout(args);
}
else if (MATRIX_PATH_EQUALS(pathPart, "register"))
{
response = RouteRegister(args);
}
else if (MATRIX_PATH_EQUALS(pathPart, "refresh"))
{
response = RouteRefresh(args);
}
else if (MATRIX_PATH_EQUALS(pathPart, "account"))
{
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "whoami"))
{
response = RouteWhoami(args);
}
else if (MATRIX_PATH_EQUALS(pathPart, "password"))
{
response = RouteChangePwd(args);
}
else
{
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_NOT_FOUND);
}
}
else if (MATRIX_PATH_EQUALS(pathPart, "profile"))
{
response = RouteUserProfile(args);
}
else
{
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_NOT_FOUND);
}
Free(pathPart);
return response;
}
else if (MATRIX_PATH_EQUALS(pathPart, "v1"))
{
/* TODO: This *really* does not look good. */
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "register"))
{
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "m.login.registration_token"))
{
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "validity"))
{
Free(pathPart);
response = RouteTokenValid(args);
}
else
{
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
}
else
{
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
}
else
{
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
}
else
{
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
return response;
}

View file

@ -32,8 +32,9 @@
#include <User.h> #include <User.h>
ROUTE_IMPL(RouteRefresh, args) ROUTE_IMPL(RouteRefresh, path, argp)
{ {
RouteArgs *args = argp;
HashMap *request; HashMap *request;
HashMap *response = NULL; HashMap *response = NULL;
@ -50,11 +51,7 @@ ROUTE_IMPL(RouteRefresh, args)
DbRef *rtRef = NULL; DbRef *rtRef = NULL;
DbRef *oAtRef = NULL; DbRef *oAtRef = NULL;
if (MATRIX_PATH_PARTS(args->path) > 0) (void) path;
{
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
if (HttpRequestMethodGet(args->context) != HTTP_POST) if (HttpRequestMethodGet(args->context) != HTTP_POST)
{ {

View file

@ -48,8 +48,9 @@ RouteRegisterRegFlow(void)
return response; return response;
} }
ROUTE_IMPL(RouteRegister, args) ROUTE_IMPL(RouteRegister, path, argp)
{ {
RouteArgs *args = argp;
HashMap *request = NULL; HashMap *request = NULL;
HashMap *response = NULL; HashMap *response = NULL;
@ -72,7 +73,7 @@ ROUTE_IMPL(RouteRegister, args)
Array *uiaFlows = NULL; Array *uiaFlows = NULL;
int uiaResult; int uiaResult;
if (MATRIX_PATH_PARTS(args->path) == 0) if (ArraySize(path) == 0)
{ {
if (HttpRequestMethodGet(args->context) != HTTP_POST) if (HttpRequestMethodGet(args->context) != HTTP_POST)
{ {
@ -261,10 +262,8 @@ finish:
} }
else else
{ {
char *pathPart = MATRIX_PATH_POP(args->path);
if (HttpRequestMethodGet(args->context) == HTTP_GET && if (HttpRequestMethodGet(args->context) == HTTP_GET &&
MATRIX_PATH_EQUALS(pathPart, "available")) MATRIX_PATH_EQUALS(ArrayGet(path, 0), "available"))
{ {
username = HashMapGet( username = HashMapGet(
HttpRequestParams(args->context), "username"); HttpRequestParams(args->context), "username");
@ -290,32 +289,11 @@ finish:
response = MatrixErrorCreate(M_USER_IN_USE); response = MatrixErrorCreate(M_USER_IN_USE);
} }
} }
else if (HttpRequestMethodGet(args->context) == HTTP_POST &&
(MATRIX_PATH_EQUALS(pathPart, "email") ||
MATRIX_PATH_EQUALS(pathPart, "msisdn")))
{
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (!MATRIX_PATH_EQUALS(pathPart, "requestToken"))
{
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_UNRECOGNIZED);
}
else
{
/* TODO: Validate request body and potentially return
* M_BAD_JSON */
HttpResponseStatus(args->context, HTTP_FORBIDDEN);
response = MatrixErrorCreate(M_THREEPID_DENIED);
}
}
else else
{ {
HttpResponseStatus(args->context, HTTP_NOT_FOUND); HttpResponseStatus(args->context, HTTP_NOT_FOUND);
response = MatrixErrorCreate(M_UNRECOGNIZED); response = MatrixErrorCreate(M_UNRECOGNIZED);
} }
Free(pathPart);
} }
return response; return response;

View file

@ -1,61 +0,0 @@
/*
* Copyright (C) 2022-2023 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 <Static.h>
#include <Memory.h>
ROUTE_IMPL(RouteStatic, args)
{
Stream *stream = HttpServerStream(args->context);
char *pathPart = MATRIX_PATH_POP(args->path);
HttpResponseHeader(args->context, "Content-Type", "text/html");
HttpSendHeaders(args->context);
if (!pathPart)
{
StaticItWorks(stream);
}
else if (MATRIX_PATH_EQUALS(pathPart, "client"))
{
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "login"))
{
StaticLogin(stream);
}
else
{
StaticError(stream, HTTP_NOT_FOUND);
}
}
else
{
StaticError(stream, HTTP_NOT_FOUND);
}
Free(pathPart);
return NULL;
}

View file

@ -21,12 +21,18 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#include <Static.h> #include <Routes.h>
#include <Html.h> #include <Html.h>
void ROUTE_IMPL(RouteStaticDefault, path, argp)
StaticItWorks(Stream * stream)
{ {
RouteArgs *args = argp;
Stream *stream = HttpServerStream(args->context);
(void) path;
HttpResponseHeader(args->context, "Content-Type", "text/html");
HttpSendHeaders(args->context);
HtmlBegin(stream, "It works! Telodendria is running."); HtmlBegin(stream, "It works! Telodendria is running.");
StreamPuts(stream, StreamPuts(stream,
@ -52,4 +58,6 @@ StaticItWorks(Stream * stream)
); );
HtmlEnd(stream); HtmlEnd(stream);
return NULL;
} }

View file

@ -21,12 +21,19 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#include <Static.h> #include <Routes.h>
#include <Html.h> #include <Html.h>
void ROUTE_IMPL(RouteStaticLogin, path, argp)
StaticLogin(Stream * stream)
{ {
RouteArgs *args = argp;
Stream *stream = HttpServerStream(args->context);
(void) path;
HttpResponseHeader(args->context, "Content-Type", "text/html");
HttpSendHeaders(args->context);
HtmlBegin(stream, "Log In"); HtmlBegin(stream, "Log In");
StreamPuts(stream, StreamPuts(stream,
@ -144,4 +151,6 @@ StaticLogin(Stream * stream)
HtmlEndJs(stream); HtmlEndJs(stream);
HtmlEnd(stream); HtmlEnd(stream);
return NULL;
} }

View file

@ -30,8 +30,9 @@
#include <HashMap.h> #include <HashMap.h>
#include <Str.h> #include <Str.h>
ROUTE_IMPL(RouteTokenValid, args) ROUTE_IMPL(RouteTokenValid, path, argp)
{ {
RouteArgs *args = argp;
Db *db = args->matrixArgs->db; Db *db = args->matrixArgs->db;
HashMap *response = NULL; HashMap *response = NULL;
@ -41,11 +42,14 @@ ROUTE_IMPL(RouteTokenValid, args)
char *tokenstr; char *tokenstr;
(void) path;
if (HttpRequestMethodGet(args->context) != HTTP_GET) if (HttpRequestMethodGet(args->context) != HTTP_GET)
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_UNRECOGNIZED); return MatrixErrorCreate(M_UNRECOGNIZED);
} }
request = JsonDecode(HttpServerStream(args->context)); request = JsonDecode(HttpServerStream(args->context));
if (!request) if (!request)
{ {
@ -62,8 +66,10 @@ ROUTE_IMPL(RouteTokenValid, args)
return response; return response;
} }
info = RegTokenGetInfo(db, tokenstr); info = RegTokenGetInfo(db, tokenstr);
response = HashMapCreate(); response = HashMapCreate();
if (!RegTokenValid(info)) if (!RegTokenValid(info))
{ {
JsonFree(request); JsonFree(request);

View file

@ -31,8 +31,9 @@
#include <Json.h> #include <Json.h>
#include <Str.h> #include <Str.h>
ROUTE_IMPL(RouteUserProfile, args) ROUTE_IMPL(RouteUserProfile, path, argp)
{ {
RouteArgs *args = argp;
Db *db = args->matrixArgs->db; Db *db = args->matrixArgs->db;
HashMap *request = NULL; HashMap *request = NULL;
@ -47,13 +48,7 @@ ROUTE_IMPL(RouteUserProfile, args)
char *token = NULL; char *token = NULL;
char *value = NULL; char *value = NULL;
if (MATRIX_PATH_PARTS(args->path) < 1) username = ArrayGet(path, 0);
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
response = MatrixErrorCreate(M_MISSING_PARAM);
goto finish;
}
username = MATRIX_PATH_POP(args->path);
userId = UserIdParse(username, serverName); userId = UserIdParse(username, serverName);
if (!userId) if (!userId)
{ {
@ -80,15 +75,10 @@ ROUTE_IMPL(RouteUserProfile, args)
response = MatrixErrorCreate(M_NOT_FOUND); response = MatrixErrorCreate(M_NOT_FOUND);
goto finish; goto finish;
} }
if (MATRIX_PATH_PARTS(args->path) > 1)
if (ArraySize(path) > 1)
{ {
HttpResponseStatus(args->context, HTTP_BAD_REQUEST); entry = ArrayGet(path, 1);
response = MatrixErrorCreate(M_INVALID_PARAM);
goto finish;
}
else if (MATRIX_PATH_PARTS(args->path) == 1)
{
entry = MATRIX_PATH_POP(args->path);
response = HashMapCreate(); response = HashMapCreate();
value = UserGetProfile(user, entry); value = UserGetProfile(user, entry);
@ -111,7 +101,7 @@ ROUTE_IMPL(RouteUserProfile, args)
} }
goto finish; goto finish;
case HTTP_PUT: case HTTP_PUT:
if (MATRIX_PATH_PARTS(args->path) == 1) if (ArraySize(path) > 1)
{ {
request = JsonDecode(HttpServerStream(args->context)); request = JsonDecode(HttpServerStream(args->context));
if (!request) if (!request)
@ -132,7 +122,7 @@ ROUTE_IMPL(RouteUserProfile, args)
response = MatrixErrorCreate(M_UNKNOWN_TOKEN); response = MatrixErrorCreate(M_UNKNOWN_TOKEN);
goto finish; goto finish;
} }
entry = MATRIX_PATH_POP(args->path); entry = ArrayGet(path, 1);
if (strcmp(entry, "displayname") == 0 || if (strcmp(entry, "displayname") == 0 ||
strcmp(entry, "avatar_url") == 0) strcmp(entry, "avatar_url") == 0)
{ {

View file

@ -21,21 +21,22 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#include <Static.h> #include <Routes.h>
#include <Html.h>
#include <Http.h>
void #include <Json.h>
StaticError(Stream * stream, HttpStatus error) #include <Array.h>
#include <HashMap.h>
ROUTE_IMPL(RouteVersions, path, argp)
{ {
char title[10]; HashMap *response = HashMapCreate();
Array *versions = ArrayCreate();
sprintf(title, "Error %d", error); (void) path;
(void) argp;
HtmlBegin(stream, title); ArrayAdd(versions, JsonValueString("v1.6"));
StreamPrintf(stream, "<h2 style=\"text-align: center\">%s</h2>", HashMapSet(response, "versions", JsonValueArray(versions));
HttpStatusToString(error)); return response;
HtmlEnd(stream);
} }

View file

@ -30,29 +30,17 @@
#include <HashMap.h> #include <HashMap.h>
#include <Str.h> #include <Str.h>
ROUTE_IMPL(RouteWellKnown, args) ROUTE_IMPL(RouteWellKnown, path, argp)
{ {
char *pathPart = MATRIX_PATH_POP(args->path); RouteArgs *args = argp;
if (!MATRIX_PATH_EQUALS(pathPart, "matrix") || MATRIX_PATH_PARTS(args->path) != 1) if (MATRIX_PATH_EQUALS(ArrayGet(path, 0), "client"))
{ {
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND);
}
Free(pathPart);
pathPart = MATRIX_PATH_POP(args->path);
if (MATRIX_PATH_EQUALS(pathPart, "client"))
{
Free(pathPart);
return MatrixClientWellKnown(args->matrixArgs->config->baseUrl, return MatrixClientWellKnown(args->matrixArgs->config->baseUrl,
args->matrixArgs->config->identityServer); args->matrixArgs->config->identityServer);
} }
else else
{ {
Free(pathPart);
HttpResponseStatus(args->context, HTTP_NOT_FOUND); HttpResponseStatus(args->context, HTTP_NOT_FOUND);
return MatrixErrorCreate(M_NOT_FOUND); return MatrixErrorCreate(M_NOT_FOUND);
} }

View file

@ -31,8 +31,9 @@
#include <Memory.h> #include <Memory.h>
#include <User.h> #include <User.h>
ROUTE_IMPL(RouteWhoami, args) ROUTE_IMPL(RouteWhoami, path, argp)
{ {
RouteArgs *args = argp;
Db *db = args->matrixArgs->db; Db *db = args->matrixArgs->db;
HashMap *response = NULL; HashMap *response = NULL;
@ -44,11 +45,7 @@ ROUTE_IMPL(RouteWhoami, args)
char *userID; char *userID;
char *deviceID; char *deviceID;
if (MATRIX_PATH_PARTS(args->path) != 0) (void) path;
{
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
return MatrixErrorCreate(M_UNRECOGNIZED);
}
/* Get the request */ /* Get the request */
response = MatrixGetAccessToken(args->context, &token); response = MatrixGetAccessToken(args->context, &token);

View file

@ -26,6 +26,9 @@
#include <Memory.h> #include <Memory.h>
#include <Log.h> #include <Log.h>
#include <HttpRouter.h>
#include <Routes.h>
#include <time.h> #include <time.h>
const char const char
@ -194,3 +197,53 @@ TelodendriaPrintHeader(void)
"Documentation/Support: https://telodendria.io"); "Documentation/Support: https://telodendria.io");
Log(LOG_INFO, ""); Log(LOG_INFO, "");
} }
HttpRouter *
TelodendriaBuildRouter(void)
{
HttpRouter *router = HttpRouterCreate();
if (!router)
{
return NULL;
}
#define R(path, func) \
if (!HttpRouterAdd(router, path, func)) \
{ \
Log(LOG_ERR, "Unable to add route: %s", path); \
HttpRouterFree(router); \
return NULL; \
}
R("/.well-known/matrix/(client|server)", RouteWellKnown);
R("/_matrix/client/versions", RouteVersions);
R("/_matrix/static", RouteStaticDefault);
R("/_matrix/static/client/login", RouteStaticLogin);
R("/_matrix/client/v3/login", RouteLogin);
R("/_matrix/client/v3/logout", RouteLogout);
R("/_matrix/client/v3/logout/(all)", RouteLogout);
R("/_matrix/client/v3/register", RouteRegister);
R("/_matrix/client/v3/register/(available)", RouteRegister);
R("/_matrix/client/v3/refresh", RouteRefresh);
R("/_matrix/client/v3/account/whoami", RouteWhoami);
R("/_matrix/client/v3/account/password", RouteChangePwd);
R("/_matrix/client/v1/register/m.login.registration_token/validity", RouteTokenValid);
#if 0
R("/_matrix/client/v3/account/password/(email|msisdn)/requestToken", RouteRequestToken);
R("/_matrix/client/v3/register/(email|msisdn)/requestToken", RouteRequestToken);
#endif
R("/_matrix/client/v3/profile/(.*)", RouteUserProfile);
R("/_matrix/client/v3/profile/(.*)/(avatar_url|displayname)", RouteUserProfile);
#undef R
return router;
}

View file

@ -615,7 +615,7 @@ UserDeleteToken(User * user, char *token)
} }
char * char *
UserGetProfile(User *user, char *name) UserGetProfile(User * user, char *name)
{ {
HashMap *json = NULL; HashMap *json = NULL;
@ -630,7 +630,7 @@ UserGetProfile(User *user, char *name)
} }
void void
UserSetProfile(User *user, char *name, char *val) UserSetProfile(User * user, char *name, char *val)
{ {
HashMap *json = NULL; HashMap *json = NULL;

View file

@ -25,6 +25,7 @@
#define TELODENDRIA_MATRIX_H #define TELODENDRIA_MATRIX_H
#include <HttpServer.h> #include <HttpServer.h>
#include <HttpRouter.h>
#include <Log.h> #include <Log.h>
#include <HashMap.h> #include <HashMap.h>
@ -71,6 +72,7 @@ typedef struct MatrixHttpHandlerArgs
{ {
Config *config; Config *config;
Db *db; Db *db;
HttpRouter *router;
} MatrixHttpHandlerArgs; } MatrixHttpHandlerArgs;
extern void extern void

View file

@ -31,14 +31,6 @@
#include <HttpServer.h> #include <HttpServer.h>
#include <Matrix.h> #include <Matrix.h>
#define MATRIX_PATH Array
#define MATRIX_PATH_CREATE() ArrayCreate()
#define MATRIX_PATH_APPEND(path, part) ArrayAdd(path, part)
#define MATRIX_PATH_FREE(path) ArrayFree(path)
#define MATRIX_PATH_POP(path) ArrayDelete(path, 0)
#define MATRIX_PATH_PARTS(path) ArraySize(path)
#define MATRIX_PATH_EQUALS(pathPart, str) \ #define MATRIX_PATH_EQUALS(pathPart, str) \
((pathPart != NULL) && (strcmp(pathPart, str) == 0)) ((pathPart != NULL) && (strcmp(pathPart, str) == 0))
@ -46,37 +38,30 @@ typedef struct RouteArgs
{ {
MatrixHttpHandlerArgs *matrixArgs; MatrixHttpHandlerArgs *matrixArgs;
HttpServerContext *context; HttpServerContext *context;
MATRIX_PATH *path;
} RouteArgs; } RouteArgs;
#define ROUTE(name) \ #define ROUTE(name) \
extern HashMap * \ extern void * \
name(RouteArgs *) name(Array *, void *)
#define ROUTE_IMPL(name, argsName) \ #define ROUTE_IMPL(name, matchesName, argsName) \
HashMap * \ void * \
name(RouteArgs * argsName) name(Array * matchesName, void * argsName)
ROUTE(RouteWellKnown); /* /.well-known */ ROUTE(RouteVersions);
ROUTE(RouteMatrix); /* /_matrix */ ROUTE(RouteWellKnown);
ROUTE(RouteStatic); /* /_matrix/static */
ROUTE(RouteLogin); /* /_matrix/client/(r0|v3)/login */ ROUTE(RouteLogin);
ROUTE(RouteLogout); /* /_matrix/client/(r0|v3)/logout */ ROUTE(RouteLogout);
ROUTE(RouteRegister); /* /_matrix/client/(r0|v3)/register */ ROUTE(RouteRegister);
ROUTE(RouteRefresh); /* /_matrix/client/(r0|v3)/refresh */ ROUTE(RouteRefresh);
ROUTE(RouteWhoami); /* /_matrix/client/(r0|v3)/account/wh ROUTE(RouteWhoami);
* oami */ ROUTE(RouteChangePwd);
ROUTE(RouteChangePwd); /* /_matrix/client/(r0|v3)/account/pa ROUTE(RouteTokenValid);
* ssword */ ROUTE(RouteUserProfile);
ROUTE(RouteTokenValid); /* /_matrix/client/v1/register/m.logi ROUTE(RouteStaticDefault);
* n.registration_token/validity */ ROUTE(RouteStaticLogin);
ROUTE(RouteUserProfile); /* This route handles:
/_matrix/client/(r0|v3)/profile/(.*),
/_matrix/client/(r0|v3)/profile/(.*)/avatar_url and
/_matrix/client/(r0|v3)/profile/(.*)/displayname*/
#undef ROUTE #undef ROUTE

View file

@ -1,40 +0,0 @@
/*
* Copyright (C) 2022-2023 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_STATIC_H
#define TELODENDRIA_STATIC_H
#include <stdio.h>
#include <Http.h>
extern void
StaticItWorks(Stream *);
extern void
StaticLogin(Stream *);
extern void
StaticError(Stream *, HttpStatus);
#endif /* TELODENDRIA_STATIC_H */

View file

@ -26,6 +26,7 @@
#include <Memory.h> #include <Memory.h>
#include <Log.h> #include <Log.h>
#include <HttpRouter.h>
#define TELODENDRIA_LOGO_WIDTH 56 #define TELODENDRIA_LOGO_WIDTH 56
#define TELODENDRIA_LOGO_HEIGHT 22 #define TELODENDRIA_LOGO_HEIGHT 22
@ -48,4 +49,7 @@ extern void
extern void extern void
TelodendriaPrintHeader(void); TelodendriaPrintHeader(void);
extern HttpRouter *
TelodendriaBuildRouter(void);
#endif #endif