diff --git a/TODO.txt b/TODO.txt index 92f1f0b..ca70f61 100644 --- a/TODO.txt +++ b/TODO.txt @@ -46,7 +46,8 @@ Milestone: v0.3.0 [x] Replace all usages of curl with http [~] Proper HTTP request router [x] Support regex matching - [ ] Replace current routing system + [x] Replace current routing system + [ ] Add route for requestToken endpoints [ ] Token permissions [ ] Move configuration to database diff --git a/src/Main.c b/src/Main.c index 5b0df05..4e7aa2c 100644 --- a/src/Main.c +++ b/src/Main.c @@ -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); config = JsonDecode(configFile); @@ -237,7 +246,6 @@ main(int argc, char **argv) Log(LOG_DEBUG, "Changed working directory to: %s", tConfig->dataDir); } - if (tConfig->flags & CONFIG_LOG_FILE) { Stream *logFile = StreamOpen("telodendria.log", "a"); @@ -536,6 +544,9 @@ finish: DbClose(matrixArgs.db); Log(LOG_DEBUG, "Closed database."); + HttpRouterFree(matrixArgs.router); + Log(LOG_DEBUG, "Freed routing tree."); + ConfigFree(tConfig); Log(LOG_DEBUG, "Exiting with code '%d'.", exit); diff --git a/src/Matrix.c b/src/Matrix.c index c4580bd..673ac0e 100644 --- a/src/Matrix.c +++ b/src/Matrix.c @@ -32,6 +32,7 @@ #include #include +#include #include void @@ -41,12 +42,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp) Stream *stream; HashMap *response = NULL; - char *key; - char *requestPath; - char *requestPathCpy; - MATRIX_PATH *pathParts; - char *pathPart; RouteArgs routeArgs; requestPath = HttpRequestPath(context); @@ -74,46 +70,21 @@ MatrixHttpHandler(HttpServerContext * context, void *argp) 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.context = context; - routeArgs.path = pathParts; - pathPart = MATRIX_PATH_POP(pathParts); - - if (MATRIX_PATH_EQUALS(pathPart, ".well-known")) - { - response = RouteWellKnown(&routeArgs); - } - else if (MATRIX_PATH_EQUALS(pathPart, "_matrix")) - { - response = RouteMatrix(&routeArgs); - } - else + if (!HttpRouterRoute(args->router, requestPath, &routeArgs, (void **) &response)) { + HttpResponseHeader(context, "Content-Type", "application/json"); HttpResponseStatus(context, HTTP_NOT_FOUND); response = MatrixErrorCreate(M_NOT_FOUND); } - Free(pathPart); - /* * If the route handler returned a JSON object, take care * 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. */ if (response) @@ -129,17 +100,6 @@ MatrixHttpHandler(HttpServerContext * context, void *argp) 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)", HttpRequestMethodToString(HttpRequestMethodGet(context)), requestPath, diff --git a/src/Routes/RouteChangePwd.c b/src/Routes/RouteChangePwd.c index e2d8d07..bddeae4 100644 --- a/src/Routes/RouteChangePwd.c +++ b/src/Routes/RouteChangePwd.c @@ -45,8 +45,9 @@ PasswordFlow(void) return ret; } -ROUTE_IMPL(RouteChangePwd, args) +ROUTE_IMPL(RouteChangePwd, path, argp) { + RouteArgs *args = argp; Db *db = args->matrixArgs->db; User *user = NULL; @@ -64,8 +65,9 @@ ROUTE_IMPL(RouteChangePwd, args) char *token; char *newPassword; - if (MATRIX_PATH_PARTS(args->path) != 0 || - HttpRequestMethodGet(args->context) != HTTP_POST) + (void) path; + + if (HttpRequestMethodGet(args->context) != HTTP_POST) { HttpResponseStatus(args->context, HTTP_BAD_REQUEST); return MatrixErrorCreate(M_UNRECOGNIZED); diff --git a/src/Routes/RouteLogin.c b/src/Routes/RouteLogin.c index 0cc8fd6..6acb0cb 100644 --- a/src/Routes/RouteLogin.c +++ b/src/Routes/RouteLogin.c @@ -31,8 +31,9 @@ #include #include -ROUTE_IMPL(RouteLogin, args) +ROUTE_IMPL(RouteLogin, path, argp) { + RouteArgs *args = argp; HashMap *request = NULL; HashMap *response = NULL; Array *enabledFlows; @@ -56,11 +57,7 @@ ROUTE_IMPL(RouteLogin, args) UserLoginInfo *loginInfo; char *fullUsername; - if (MATRIX_PATH_PARTS(args->path) > 0) - { - HttpResponseStatus(args->context, HTTP_NOT_FOUND); - return MatrixErrorCreate(M_NOT_FOUND); - } + (void) path; switch (HttpRequestMethodGet(args->context)) { diff --git a/src/Routes/RouteLogout.c b/src/Routes/RouteLogout.c index 3aaf307..63f3c51 100644 --- a/src/Routes/RouteLogout.c +++ b/src/Routes/RouteLogout.c @@ -31,8 +31,9 @@ #include #include -ROUTE_IMPL(RouteLogout, args) +ROUTE_IMPL(RouteLogout, path, argp) { + RouteArgs *args = argp; HashMap *response = NULL; char *tokenstr; @@ -47,12 +48,6 @@ ROUTE_IMPL(RouteLogout, args) 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); if (response) { @@ -66,18 +61,14 @@ ROUTE_IMPL(RouteLogout, args) 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(pathPart, "all")) + if (!MATRIX_PATH_EQUALS(ArrayGet(path, 0), "all")) { - Free(pathPart); HttpResponseStatus(args->context, HTTP_NOT_FOUND); response = MatrixErrorCreate(M_NOT_FOUND); goto finish; } - Free(pathPart); if (!UserDeleteTokens(user, NULL)) { diff --git a/src/Routes/RouteMatrix.c b/src/Routes/RouteMatrix.c deleted file mode 100644 index 2ef00f9..0000000 --- a/src/Routes/RouteMatrix.c +++ /dev/null @@ -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 - -#include - -#include -#include -#include -#include - -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; -} diff --git a/src/Routes/RouteRefresh.c b/src/Routes/RouteRefresh.c index 10ef1ba..04de9e9 100644 --- a/src/Routes/RouteRefresh.c +++ b/src/Routes/RouteRefresh.c @@ -32,8 +32,9 @@ #include -ROUTE_IMPL(RouteRefresh, args) +ROUTE_IMPL(RouteRefresh, path, argp) { + RouteArgs *args = argp; HashMap *request; HashMap *response = NULL; @@ -50,11 +51,7 @@ ROUTE_IMPL(RouteRefresh, args) DbRef *rtRef = NULL; DbRef *oAtRef = NULL; - if (MATRIX_PATH_PARTS(args->path) > 0) - { - HttpResponseStatus(args->context, HTTP_NOT_FOUND); - return MatrixErrorCreate(M_NOT_FOUND); - } + (void) path; if (HttpRequestMethodGet(args->context) != HTTP_POST) { diff --git a/src/Routes/RouteRegister.c b/src/Routes/RouteRegister.c index af138b3..f99704f 100644 --- a/src/Routes/RouteRegister.c +++ b/src/Routes/RouteRegister.c @@ -48,8 +48,9 @@ RouteRegisterRegFlow(void) return response; } -ROUTE_IMPL(RouteRegister, args) +ROUTE_IMPL(RouteRegister, path, argp) { + RouteArgs *args = argp; HashMap *request = NULL; HashMap *response = NULL; @@ -72,7 +73,7 @@ ROUTE_IMPL(RouteRegister, args) Array *uiaFlows = NULL; int uiaResult; - if (MATRIX_PATH_PARTS(args->path) == 0) + if (ArraySize(path) == 0) { if (HttpRequestMethodGet(args->context) != HTTP_POST) { @@ -261,10 +262,8 @@ finish: } else { - char *pathPart = MATRIX_PATH_POP(args->path); - if (HttpRequestMethodGet(args->context) == HTTP_GET && - MATRIX_PATH_EQUALS(pathPart, "available")) + MATRIX_PATH_EQUALS(ArrayGet(path, 0), "available")) { username = HashMapGet( HttpRequestParams(args->context), "username"); @@ -290,32 +289,11 @@ finish: 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 { HttpResponseStatus(args->context, HTTP_NOT_FOUND); response = MatrixErrorCreate(M_UNRECOGNIZED); } - - Free(pathPart); } return response; diff --git a/src/Routes/RouteStatic.c b/src/Routes/RouteStatic.c deleted file mode 100644 index 55755d4..0000000 --- a/src/Routes/RouteStatic.c +++ /dev/null @@ -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 -#include -#include - -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; -} diff --git a/src/Static/StaticItWorks.c b/src/Routes/RouteStaticDefault.c similarity index 86% rename from src/Static/StaticItWorks.c rename to src/Routes/RouteStaticDefault.c index d49c43e..41c2970 100644 --- a/src/Static/StaticItWorks.c +++ b/src/Routes/RouteStaticDefault.c @@ -21,12 +21,18 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include +#include #include -void -StaticItWorks(Stream * stream) +ROUTE_IMPL(RouteStaticDefault, path, argp) { + 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."); StreamPuts(stream, @@ -52,4 +58,6 @@ StaticItWorks(Stream * stream) ); HtmlEnd(stream); + + return NULL; } diff --git a/src/Static/StaticLogin.c b/src/Routes/RouteStaticLogin.c similarity index 95% rename from src/Static/StaticLogin.c rename to src/Routes/RouteStaticLogin.c index 81c1492..b417b11 100644 --- a/src/Static/StaticLogin.c +++ b/src/Routes/RouteStaticLogin.c @@ -21,12 +21,19 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include +#include #include -void -StaticLogin(Stream * stream) +ROUTE_IMPL(RouteStaticLogin, path, argp) { + RouteArgs *args = argp; + Stream *stream = HttpServerStream(args->context); + + (void) path; + + HttpResponseHeader(args->context, "Content-Type", "text/html"); + HttpSendHeaders(args->context); + HtmlBegin(stream, "Log In"); StreamPuts(stream, @@ -144,4 +151,6 @@ StaticLogin(Stream * stream) HtmlEndJs(stream); HtmlEnd(stream); + + return NULL; } diff --git a/src/Routes/RouteTokenValid.c b/src/Routes/RouteTokenValid.c index 62cc409..e17cf43 100644 --- a/src/Routes/RouteTokenValid.c +++ b/src/Routes/RouteTokenValid.c @@ -30,8 +30,9 @@ #include #include -ROUTE_IMPL(RouteTokenValid, args) +ROUTE_IMPL(RouteTokenValid, path, argp) { + RouteArgs *args = argp; Db *db = args->matrixArgs->db; HashMap *response = NULL; @@ -41,11 +42,14 @@ ROUTE_IMPL(RouteTokenValid, args) char *tokenstr; + (void) path; + if (HttpRequestMethodGet(args->context) != HTTP_GET) { HttpResponseStatus(args->context, HTTP_BAD_REQUEST); return MatrixErrorCreate(M_UNRECOGNIZED); } + request = JsonDecode(HttpServerStream(args->context)); if (!request) { @@ -62,8 +66,10 @@ ROUTE_IMPL(RouteTokenValid, args) return response; } + info = RegTokenGetInfo(db, tokenstr); response = HashMapCreate(); + if (!RegTokenValid(info)) { JsonFree(request); diff --git a/src/Routes/RouteUserProfile.c b/src/Routes/RouteUserProfile.c index d512d37..7d47e2f 100644 --- a/src/Routes/RouteUserProfile.c +++ b/src/Routes/RouteUserProfile.c @@ -31,8 +31,9 @@ #include #include -ROUTE_IMPL(RouteUserProfile, args) +ROUTE_IMPL(RouteUserProfile, path, argp) { + RouteArgs *args = argp; Db *db = args->matrixArgs->db; HashMap *request = NULL; @@ -47,13 +48,7 @@ ROUTE_IMPL(RouteUserProfile, args) char *token = NULL; char *value = NULL; - if (MATRIX_PATH_PARTS(args->path) < 1) - { - HttpResponseStatus(args->context, HTTP_BAD_REQUEST); - response = MatrixErrorCreate(M_MISSING_PARAM); - goto finish; - } - username = MATRIX_PATH_POP(args->path); + username = ArrayGet(path, 0); userId = UserIdParse(username, serverName); if (!userId) { @@ -80,15 +75,10 @@ ROUTE_IMPL(RouteUserProfile, args) response = MatrixErrorCreate(M_NOT_FOUND); goto finish; } - if (MATRIX_PATH_PARTS(args->path) > 1) + + if (ArraySize(path) > 1) { - HttpResponseStatus(args->context, HTTP_BAD_REQUEST); - response = MatrixErrorCreate(M_INVALID_PARAM); - goto finish; - } - else if (MATRIX_PATH_PARTS(args->path) == 1) - { - entry = MATRIX_PATH_POP(args->path); + entry = ArrayGet(path, 1); response = HashMapCreate(); value = UserGetProfile(user, entry); @@ -111,7 +101,7 @@ ROUTE_IMPL(RouteUserProfile, args) } goto finish; case HTTP_PUT: - if (MATRIX_PATH_PARTS(args->path) == 1) + if (ArraySize(path) > 1) { request = JsonDecode(HttpServerStream(args->context)); if (!request) @@ -132,7 +122,7 @@ ROUTE_IMPL(RouteUserProfile, args) response = MatrixErrorCreate(M_UNKNOWN_TOKEN); goto finish; } - entry = MATRIX_PATH_POP(args->path); + entry = ArrayGet(path, 1); if (strcmp(entry, "displayname") == 0 || strcmp(entry, "avatar_url") == 0) { diff --git a/src/Static/StaticError.c b/src/Routes/RouteVersions.c similarity index 76% rename from src/Static/StaticError.c rename to src/Routes/RouteVersions.c index 7bdc535..8ed0c39 100644 --- a/src/Static/StaticError.c +++ b/src/Routes/RouteVersions.c @@ -21,21 +21,22 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include -#include -#include +#include -void -StaticError(Stream * stream, HttpStatus error) +#include +#include +#include + +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, "

%s

", - HttpStatusToString(error)); - - HtmlEnd(stream); + HashMapSet(response, "versions", JsonValueArray(versions)); + return response; } diff --git a/src/Routes/RouteWellKnown.c b/src/Routes/RouteWellKnown.c index 6d23b85..26cda71 100644 --- a/src/Routes/RouteWellKnown.c +++ b/src/Routes/RouteWellKnown.c @@ -30,29 +30,17 @@ #include #include -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, args->matrixArgs->config->identityServer); } else { - Free(pathPart); HttpResponseStatus(args->context, HTTP_NOT_FOUND); return MatrixErrorCreate(M_NOT_FOUND); } diff --git a/src/Routes/RouteWhoami.c b/src/Routes/RouteWhoami.c index b97e749..6bd57fc 100644 --- a/src/Routes/RouteWhoami.c +++ b/src/Routes/RouteWhoami.c @@ -31,8 +31,9 @@ #include #include -ROUTE_IMPL(RouteWhoami, args) +ROUTE_IMPL(RouteWhoami, path, argp) { + RouteArgs *args = argp; Db *db = args->matrixArgs->db; HashMap *response = NULL; @@ -44,11 +45,7 @@ ROUTE_IMPL(RouteWhoami, args) char *userID; char *deviceID; - if (MATRIX_PATH_PARTS(args->path) != 0) - { - HttpResponseStatus(args->context, HTTP_BAD_REQUEST); - return MatrixErrorCreate(M_UNRECOGNIZED); - } + (void) path; /* Get the request */ response = MatrixGetAccessToken(args->context, &token); diff --git a/src/Telodendria.c b/src/Telodendria.c index 14f50ae..33c8ac8 100644 --- a/src/Telodendria.c +++ b/src/Telodendria.c @@ -26,6 +26,9 @@ #include #include +#include +#include + #include const char @@ -194,3 +197,53 @@ TelodendriaPrintHeader(void) "Documentation/Support: https://telodendria.io"); 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; +} diff --git a/src/User.c b/src/User.c index 080c91a..687fcf6 100644 --- a/src/User.c +++ b/src/User.c @@ -615,7 +615,7 @@ UserDeleteToken(User * user, char *token) } char * -UserGetProfile(User *user, char *name) +UserGetProfile(User * user, char *name) { HashMap *json = NULL; @@ -625,12 +625,12 @@ UserGetProfile(User *user, char *name) } json = DbJson(user->ref); - + return JsonValueAsString(JsonGet(json, 2, "profile", name)); } void -UserSetProfile(User *user, char *name, char *val) +UserSetProfile(User * user, char *name, char *val) { HashMap *json = NULL; diff --git a/src/include/Matrix.h b/src/include/Matrix.h index 3b4e26f..59bfefe 100644 --- a/src/include/Matrix.h +++ b/src/include/Matrix.h @@ -25,6 +25,7 @@ #define TELODENDRIA_MATRIX_H #include +#include #include #include @@ -71,6 +72,7 @@ typedef struct MatrixHttpHandlerArgs { Config *config; Db *db; + HttpRouter *router; } MatrixHttpHandlerArgs; extern void diff --git a/src/include/Routes.h b/src/include/Routes.h index 7dbf209..150b977 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -31,14 +31,6 @@ #include #include -#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) \ ((pathPart != NULL) && (strcmp(pathPart, str) == 0)) @@ -46,37 +38,30 @@ typedef struct RouteArgs { MatrixHttpHandlerArgs *matrixArgs; HttpServerContext *context; - MATRIX_PATH *path; } RouteArgs; #define ROUTE(name) \ - extern HashMap * \ - name(RouteArgs *) + extern void * \ + name(Array *, void *) -#define ROUTE_IMPL(name, argsName) \ - HashMap * \ - name(RouteArgs * argsName) +#define ROUTE_IMPL(name, matchesName, argsName) \ + void * \ + name(Array * matchesName, void * argsName) -ROUTE(RouteWellKnown); /* /.well-known */ -ROUTE(RouteMatrix); /* /_matrix */ -ROUTE(RouteStatic); /* /_matrix/static */ +ROUTE(RouteVersions); +ROUTE(RouteWellKnown); -ROUTE(RouteLogin); /* /_matrix/client/(r0|v3)/login */ -ROUTE(RouteLogout); /* /_matrix/client/(r0|v3)/logout */ -ROUTE(RouteRegister); /* /_matrix/client/(r0|v3)/register */ -ROUTE(RouteRefresh); /* /_matrix/client/(r0|v3)/refresh */ -ROUTE(RouteWhoami); /* /_matrix/client/(r0|v3)/account/wh - * oami */ -ROUTE(RouteChangePwd); /* /_matrix/client/(r0|v3)/account/pa - * ssword */ +ROUTE(RouteLogin); +ROUTE(RouteLogout); +ROUTE(RouteRegister); +ROUTE(RouteRefresh); +ROUTE(RouteWhoami); +ROUTE(RouteChangePwd); +ROUTE(RouteTokenValid); +ROUTE(RouteUserProfile); -ROUTE(RouteTokenValid); /* /_matrix/client/v1/register/m.logi - * n.registration_token/validity */ - -ROUTE(RouteUserProfile); /* This route handles: - /_matrix/client/(r0|v3)/profile/(.*), - /_matrix/client/(r0|v3)/profile/(.*)/avatar_url and - /_matrix/client/(r0|v3)/profile/(.*)/displayname*/ +ROUTE(RouteStaticDefault); +ROUTE(RouteStaticLogin); #undef ROUTE diff --git a/src/include/Static.h b/src/include/Static.h deleted file mode 100644 index ccba81e..0000000 --- a/src/include/Static.h +++ /dev/null @@ -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 - -#include - -extern void - StaticItWorks(Stream *); - -extern void - StaticLogin(Stream *); - -extern void - StaticError(Stream *, HttpStatus); - -#endif /* TELODENDRIA_STATIC_H */ diff --git a/src/include/Telodendria.h b/src/include/Telodendria.h index 50f452d..d01313c 100644 --- a/src/include/Telodendria.h +++ b/src/include/Telodendria.h @@ -26,6 +26,7 @@ #include #include +#include #define TELODENDRIA_LOGO_WIDTH 56 #define TELODENDRIA_LOGO_HEIGHT 22 @@ -48,4 +49,7 @@ extern void extern void TelodendriaPrintHeader(void); +extern HttpRouter * + TelodendriaBuildRouter(void); + #endif