forked from lda/telodendria
This pull request makes a very small commit on top of #44. Closes #44. Closes #9. Closes #22. Co-authored-by: LoaD Accumulator <lda@freetards.xyz> Co-authored-by: lda <lda@freetards.xyz> Co-authored-by: lda <lda@noreply.git.telodendria.io> Reviewed-on: Telodendria/Telodendria#51
This commit is contained in:
parent
243de4f1a0
commit
54420f0036
14 changed files with 817 additions and 77 deletions
|
@ -41,6 +41,8 @@ will now be maintained separately and have its own releases as well.
|
||||||
custom scripts.
|
custom scripts.
|
||||||
- Greatly simplified some endpoint code by using Cytoplasm's `j2s` for
|
- Greatly simplified some endpoint code by using Cytoplasm's `j2s` for
|
||||||
parsing request bodies.
|
parsing request bodies.
|
||||||
|
- Create a parser API for grammars found in Matrix, and refactor the
|
||||||
|
User API to use it.
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
|
@ -58,6 +60,10 @@ the ability to change only a subset of the configuration.
|
||||||
- **GET** `/_telodendria/admin/tokens/[token]`
|
- **GET** `/_telodendria/admin/tokens/[token]`
|
||||||
- **POST** `/_telodendria/admin/tokens`
|
- **POST** `/_telodendria/admin/tokens`
|
||||||
- **DELETE** `/_telodendria/admin/tokens/[token]`
|
- **DELETE** `/_telodendria/admin/tokens/[token]`
|
||||||
|
- **GET** `/_matrix/client/v3/directory/room/[alias]`
|
||||||
|
- **PUT** `/_matrix/client/v3/directory/room/[alias]`
|
||||||
|
- **DELETE** `/_matrix/client/v3/directory/room/[alias]`
|
||||||
|
- **GET** `/_matrix/client/v3/rooms/[id]/aliases`
|
||||||
|
|
||||||
## v0.3.0
|
## v0.3.0
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ registration tokens.
|
||||||
configuration.
|
configuration.
|
||||||
- **GRANT_PRIVILEGES:** Allows a user to modify his or her own
|
- **GRANT_PRIVILEGES:** Allows a user to modify his or her own
|
||||||
privileges or the privileges of other local users.
|
privileges or the privileges of other local users.
|
||||||
- **ALIAS:** Allows a user to modify room aliases created by other
|
- **ALIAS:** Allows a user to modify and see room aliases created by
|
||||||
users. By default, users can only manage their own room aliases, but
|
other users. By default, users can only manage their own room aliases,
|
||||||
an administrator may wish to take over an alias or remove an offensive
|
but an administrator may wish to take over an alias or remove an
|
||||||
alias.
|
offensive alias.
|
||||||
- **PROC_CONTROL:** Allows a user to get statistics on the running
|
- **PROC_CONTROL:** Allows a user to get statistics on the running
|
||||||
process, as well as shutdown and resetart the Telodendria daemon
|
process, as well as shutdown and resetart the Telodendria daemon
|
||||||
itself. Typically this will pair well with **CONFIG**, because there
|
itself. Typically this will pair well with **CONFIG**, because there
|
||||||
|
|
503
src/Parser.c
Normal file
503
src/Parser.c
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
/*
|
||||||
|
* 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 <Parser.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Int.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Iterate through a char **. */
|
||||||
|
#define Iterate(s) (*(*s)++)
|
||||||
|
|
||||||
|
/* Parse an extended localpart */
|
||||||
|
static int
|
||||||
|
ParseUserLocalpart(char **str, char **out)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
char *start;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
if (!str || !out)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* An extended localpart contains every ASCII printable character,
|
||||||
|
* except an ':'. */
|
||||||
|
start = *str;
|
||||||
|
while (isascii((c = Iterate(str))) && c != ':' && c)
|
||||||
|
{
|
||||||
|
/* Do nothing */
|
||||||
|
}
|
||||||
|
length = (size_t) (*str - start) - 1;
|
||||||
|
if (length < 1)
|
||||||
|
{
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (c == ':')
|
||||||
|
{
|
||||||
|
--(*str);
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = Malloc(length + 1);
|
||||||
|
memcpy(*out, start, length);
|
||||||
|
(*out)[length] = '\0';
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* Parses an IPv4 address. */
|
||||||
|
static int
|
||||||
|
ParseIPv4(char **str, char **out)
|
||||||
|
{
|
||||||
|
/* Be *very* careful with this buffer */
|
||||||
|
char buffer[4];
|
||||||
|
char *start;
|
||||||
|
size_t length;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
int digit = 0;
|
||||||
|
int digits = 0;
|
||||||
|
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
start = *str;
|
||||||
|
|
||||||
|
/* An IPv4 address is made of 4 blocks between 1-3 digits, like so:
|
||||||
|
* (1-3)*DIGIT.(1-3)*DIGIT.(1-3)*DIGIT.(1-3)*DIGIT */
|
||||||
|
while ((isdigit(c = Iterate(str)) || c == '.') && c && digits < 4)
|
||||||
|
{
|
||||||
|
if (isdigit(c))
|
||||||
|
{
|
||||||
|
digit++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (digit < 1 || digit > 3)
|
||||||
|
{
|
||||||
|
/* Current digit is too long for the spec! */
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(buffer, *str - digit - 1, digit);
|
||||||
|
if (atoi(buffer) > 255)
|
||||||
|
{
|
||||||
|
/* Current digit is too large for the spec! */
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
digit = 0;
|
||||||
|
digits++; /* We have parsed a digit. */
|
||||||
|
}
|
||||||
|
if (c == '.' || digits != 3)
|
||||||
|
{
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
length = (size_t) (*str - start) - 1;
|
||||||
|
*out = Malloc(length + 1);
|
||||||
|
memcpy(*out, start, length);
|
||||||
|
(*str)--;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
IsIPv6Char(char c)
|
||||||
|
{
|
||||||
|
return isxdigit(c) || c == ':' || c == '.';
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
ParseIPv6(char **str, char **out)
|
||||||
|
{
|
||||||
|
char *start;
|
||||||
|
size_t length;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
int filled = 0;
|
||||||
|
int digit = 0;
|
||||||
|
int digits = 0;
|
||||||
|
|
||||||
|
start = *str;
|
||||||
|
length = 0;
|
||||||
|
|
||||||
|
if (Iterate(str) != '[')
|
||||||
|
{
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((c = Iterate(str)) && IsIPv6Char(c))
|
||||||
|
{
|
||||||
|
char *ipv4;
|
||||||
|
if (isxdigit(c))
|
||||||
|
{
|
||||||
|
digit++;
|
||||||
|
length++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == ':')
|
||||||
|
{
|
||||||
|
if (**str == ':')
|
||||||
|
{
|
||||||
|
digit = 0;
|
||||||
|
if (!filled)
|
||||||
|
{
|
||||||
|
filled = 1;
|
||||||
|
length++;
|
||||||
|
c = Iterate(str); /* Skip over the character */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* RFC3513 says the following:
|
||||||
|
* > 'The "::" can only appear once in an address.' */
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (digit < 1 || digit > 4)
|
||||||
|
{
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
/* We do not have to check whenever the digit here is valid,
|
||||||
|
* because it has to be. */
|
||||||
|
digit = 0;
|
||||||
|
digits++;
|
||||||
|
|
||||||
|
length++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* The only remaining character being '.', we are probably dealing
|
||||||
|
* with an IPv4 literal. */
|
||||||
|
*str -= digit + 1;
|
||||||
|
length -= digit + 1;
|
||||||
|
if (ParseIPv4(str, &ipv4))
|
||||||
|
{
|
||||||
|
length += strlen(ipv4);
|
||||||
|
Free(ipv4);
|
||||||
|
c = Iterate(str);
|
||||||
|
filled = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
--(*str);
|
||||||
|
if (Iterate(str) != ']')
|
||||||
|
{
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = (size_t) (*str - start);
|
||||||
|
if (length < 4 || length > 47)
|
||||||
|
{
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
*out = Malloc(length + 1);
|
||||||
|
memset(*out, '\0', length + 1);
|
||||||
|
memcpy(*out, start, length);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
fail:
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
ParseHostname(char **str, char **out)
|
||||||
|
{
|
||||||
|
char *start;
|
||||||
|
size_t length = 0;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
start = *str;
|
||||||
|
while ((c = Iterate(str)) &&
|
||||||
|
(isalnum(c) || c == '.' || c == '-') &&
|
||||||
|
++length < 256)
|
||||||
|
{
|
||||||
|
/* Do nothing. */
|
||||||
|
}
|
||||||
|
if (length < 1 || length > 255)
|
||||||
|
{
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
length = (size_t) (*str - start) - 1;
|
||||||
|
*out = Malloc(length + 1);
|
||||||
|
memcpy(*out, start, length);
|
||||||
|
(*str)--;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ParseServerName(char **str, ServerPart *out)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
char *start;
|
||||||
|
char *startPort;
|
||||||
|
size_t chars = 0;
|
||||||
|
|
||||||
|
char *host = NULL;
|
||||||
|
char *port = NULL;
|
||||||
|
|
||||||
|
if (!str || !out)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = *str;
|
||||||
|
|
||||||
|
if (!host)
|
||||||
|
{
|
||||||
|
/* If we can parse an IPv4 address, use that. */
|
||||||
|
ParseIPv4(str, &host);
|
||||||
|
}
|
||||||
|
if (!host)
|
||||||
|
{
|
||||||
|
/* If we can parse an IPv6 address, use that. */
|
||||||
|
ParseIPv6(str, &host);
|
||||||
|
}
|
||||||
|
if (!host)
|
||||||
|
{
|
||||||
|
/* If we can parse an hostname, use that. */
|
||||||
|
ParseHostname(str, &host);
|
||||||
|
}
|
||||||
|
if (!host)
|
||||||
|
{
|
||||||
|
/* Can't parse a valid server name. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Now, there's only 2 options: a ':', or the end(everything else.) */
|
||||||
|
if (**str != ':')
|
||||||
|
{
|
||||||
|
/* We're done. */
|
||||||
|
out->hostname = host;
|
||||||
|
out->port = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* TODO: Separate this out */
|
||||||
|
startPort = ++(*str);
|
||||||
|
while(isdigit(c = Iterate(str)) && c && ++chars < 5)
|
||||||
|
{
|
||||||
|
/* Do nothing. */
|
||||||
|
}
|
||||||
|
if (chars < 1 || chars > 5)
|
||||||
|
{
|
||||||
|
*str = start;
|
||||||
|
Free(host);
|
||||||
|
host = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
port = Malloc(chars + 1);
|
||||||
|
memcpy(port, startPort, chars);
|
||||||
|
port[chars] = '\0';
|
||||||
|
if (atol(port) > 65535)
|
||||||
|
{
|
||||||
|
Free(port);
|
||||||
|
Free(host);
|
||||||
|
*str = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->hostname = host;
|
||||||
|
out->port = port;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int
|
||||||
|
ParseServerPart(char *str, ServerPart *part)
|
||||||
|
{
|
||||||
|
/* This is a wrapper behind the internal ParseServerName. */
|
||||||
|
if (!str || !part)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ParseServerName(&str, part);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ServerPartFree(ServerPart part)
|
||||||
|
{
|
||||||
|
if (part.hostname)
|
||||||
|
{
|
||||||
|
Free(part.hostname);
|
||||||
|
}
|
||||||
|
if (part.port)
|
||||||
|
{
|
||||||
|
Free(part.port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ParseCommonID(char *str, CommonID *id)
|
||||||
|
{
|
||||||
|
char sigil;
|
||||||
|
|
||||||
|
if (!str || !id)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There must at least be 2 chararacters: the sigil and a string.*/
|
||||||
|
if (strlen(str) < 2)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigil = *str++;
|
||||||
|
/* Some sigils have the following restriction:
|
||||||
|
* > MUST NOT exceed 255 bytes (including the # sigil and the domain).
|
||||||
|
*/
|
||||||
|
if ((sigil == '#' || sigil == '@') && strlen(str) > 255)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
id->sigil = sigil;
|
||||||
|
id->local = NULL;
|
||||||
|
id->server.hostname = NULL;
|
||||||
|
id->server.port = NULL;
|
||||||
|
|
||||||
|
switch (sigil)
|
||||||
|
{
|
||||||
|
case '$':
|
||||||
|
/* For event IDs, it depends on the version, so we're just
|
||||||
|
* accepting it all. */
|
||||||
|
if (!ParseUserLocalpart(&str, &id->local))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*str == ':')
|
||||||
|
{
|
||||||
|
(*str)++;
|
||||||
|
if (!ParseServerName(&str, &id->server))
|
||||||
|
{
|
||||||
|
Free(id->local);
|
||||||
|
id->local = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '!':
|
||||||
|
case '#': /* It seems like the localpart should be the same as the
|
||||||
|
user's: everything, except ':'. */
|
||||||
|
case '@':
|
||||||
|
if (!ParseUserLocalpart(&str, &id->local))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*str++ != ':')
|
||||||
|
{
|
||||||
|
Free(id->local);
|
||||||
|
id->local = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!ParseServerName(&str, &id->server))
|
||||||
|
{
|
||||||
|
Free(id->local);
|
||||||
|
id->local = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CommonIDFree(CommonID id)
|
||||||
|
{
|
||||||
|
if (id.local)
|
||||||
|
{
|
||||||
|
Free(id.local);
|
||||||
|
}
|
||||||
|
ServerPartFree(id.server);
|
||||||
|
}
|
||||||
|
int
|
||||||
|
ValidCommonID(char *str, char sigil)
|
||||||
|
{
|
||||||
|
CommonID id;
|
||||||
|
int ret;
|
||||||
|
memset(&id, 0, sizeof(CommonID));
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ParseCommonID(str, &id) && id.sigil == sigil;
|
||||||
|
|
||||||
|
CommonIDFree(id);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParserRecomposeServerPart(ServerPart serverPart)
|
||||||
|
{
|
||||||
|
if (serverPart.hostname && serverPart.port)
|
||||||
|
{
|
||||||
|
return StrConcat(3, serverPart.hostname, ":", serverPart.port);
|
||||||
|
}
|
||||||
|
if (serverPart.hostname)
|
||||||
|
{
|
||||||
|
return StrDuplicate(serverPart.hostname);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParserRecomposeCommonID(CommonID id)
|
||||||
|
{
|
||||||
|
char *ret = Malloc(2 * sizeof(char));
|
||||||
|
ret[0] = id.sigil;
|
||||||
|
ret[1] = '\0';
|
||||||
|
|
||||||
|
if (id.local)
|
||||||
|
{
|
||||||
|
char *tmp = StrConcat(2, ret, id.local);
|
||||||
|
Free(ret);
|
||||||
|
|
||||||
|
ret = tmp;
|
||||||
|
}
|
||||||
|
if (id.server.hostname)
|
||||||
|
{
|
||||||
|
char *server = ParserRecomposeServerPart(id.server);
|
||||||
|
char *tmp = StrConcat(4, "@", ret, ":", server);
|
||||||
|
Free(ret);
|
||||||
|
Free(server);
|
||||||
|
|
||||||
|
ret = tmp;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
int
|
||||||
|
ParserServerNameEquals(ServerPart serverPart, char *str)
|
||||||
|
{
|
||||||
|
char *idServer;
|
||||||
|
int ret;
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idServer = ParserRecomposeServerPart(serverPart);
|
||||||
|
|
||||||
|
ret = StrEquals(idServer, str);
|
||||||
|
Free(idServer);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -25,8 +25,12 @@
|
||||||
|
|
||||||
#include <Routes.h>
|
#include <Routes.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <Config.h>
|
||||||
|
#include <Parser.h>
|
||||||
#include <User.h>
|
#include <User.h>
|
||||||
|
|
||||||
ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
||||||
|
@ -38,20 +42,40 @@ ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
||||||
HashMap *response;
|
HashMap *response;
|
||||||
|
|
||||||
Db *db = args->matrixArgs->db;
|
Db *db = args->matrixArgs->db;
|
||||||
DbRef *ref;
|
DbRef *ref = NULL;
|
||||||
HashMap *aliases;
|
HashMap *aliases;
|
||||||
|
HashMap *idObject;
|
||||||
JsonValue *val;
|
JsonValue *val;
|
||||||
|
Array *arr;
|
||||||
|
|
||||||
char *token;
|
char *token;
|
||||||
|
char *msg;
|
||||||
User *user = NULL;
|
User *user = NULL;
|
||||||
|
|
||||||
/* TODO: Return HTTP 400 M_INVALID_PARAM if alias is invalid */
|
CommonID aliasID;
|
||||||
|
Config config;
|
||||||
|
|
||||||
|
aliasID.sigil = '\0';
|
||||||
|
aliasID.local = NULL;
|
||||||
|
aliasID.server.hostname = NULL;
|
||||||
|
aliasID.server.port = NULL;
|
||||||
|
|
||||||
|
ConfigLock(db, &config);
|
||||||
|
|
||||||
|
if (!ParseCommonID(alias, &aliasID) || aliasID.sigil != '#')
|
||||||
|
{
|
||||||
|
msg = "Invalid room alias.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
ref = DbLock(db, 1, "aliases");
|
ref = DbLock(db, 1, "aliases");
|
||||||
if (!ref && !(ref = DbCreate(db, 1, "aliases")))
|
if (!ref && !(ref = DbCreate(db, 1, "aliases")))
|
||||||
{
|
{
|
||||||
|
msg = "Unable to access alias database.",
|
||||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||||
response = MatrixErrorCreate(M_UNKNOWN, "Unable to access alias database.");
|
response = MatrixErrorCreate(M_UNKNOWN, msg);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +93,9 @@ ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
msg = "There is no mapped room ID for this room alias.";
|
||||||
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
|
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
|
||||||
response = MatrixErrorCreate(M_NOT_FOUND, "There is no mapped room ID for this room alias.");
|
response = MatrixErrorCreate(M_NOT_FOUND, msg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HTTP_PUT:
|
case HTTP_PUT:
|
||||||
|
@ -92,9 +117,21 @@ ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
||||||
if (HttpRequestMethodGet(args->context) == HTTP_PUT)
|
if (HttpRequestMethodGet(args->context) == HTTP_PUT)
|
||||||
{
|
{
|
||||||
HashMap *newAlias;
|
HashMap *newAlias;
|
||||||
|
char *id;
|
||||||
|
char *serverPart;
|
||||||
|
|
||||||
/* TODO: Validate alias domain and make sure it matches
|
serverPart = ParserRecomposeServerPart(aliasID.server);
|
||||||
* server name and is well formed. */
|
if (!StrEquals(serverPart, config.serverName))
|
||||||
|
{
|
||||||
|
msg = "Invalid server name.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||||
|
|
||||||
|
Free(serverPart);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(serverPart);
|
||||||
|
|
||||||
if (JsonGet(aliases, 2, "alias", alias))
|
if (JsonGet(aliases, 2, "alias", alias))
|
||||||
{
|
{
|
||||||
|
@ -111,40 +148,81 @@ ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!JsonValueAsString(HashMapGet(request, "room_id")))
|
id = JsonValueAsString(HashMapGet(request, "room_id"));
|
||||||
|
if (!id)
|
||||||
{
|
{
|
||||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
response = MatrixErrorCreate(M_BAD_JSON, "Missing or invalid room_id.");
|
response = MatrixErrorCreate(M_BAD_JSON, "Missing or invalid room_id.");
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Validate room ID to make sure it is well
|
if (!ValidCommonID(id, '!'))
|
||||||
* formed. */
|
{
|
||||||
|
msg = "Invalid room ID.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
newAlias = HashMapCreate();
|
newAlias = HashMapCreate();
|
||||||
HashMapSet(newAlias, "createdBy", JsonValueString(UserGetName(user)));
|
HashMapSet(newAlias, "createdBy", JsonValueString(UserGetName(user)));
|
||||||
HashMapSet(newAlias, "id", JsonValueDuplicate(HashMapGet(request, "room_id")));
|
HashMapSet(newAlias, "id", JsonValueString(id));
|
||||||
HashMapSet(newAlias, "servers", JsonValueArray(ArrayCreate()));
|
HashMapSet(newAlias, "servers", JsonValueArray(ArrayCreate()));
|
||||||
|
|
||||||
JsonSet(aliases, JsonValueObject(newAlias), 2, "alias", alias);
|
JsonSet(aliases, JsonValueObject(newAlias), 2, "alias", alias);
|
||||||
|
|
||||||
|
if (!(idObject = JsonValueAsObject(JsonGet(aliases, 2, "id", id))))
|
||||||
|
{
|
||||||
|
arr = ArrayCreate();
|
||||||
|
idObject = HashMapCreate();
|
||||||
|
HashMapSet(idObject, "aliases", JsonValueArray(arr));
|
||||||
|
JsonSet(aliases, JsonValueObject(idObject), 2, "id", id);
|
||||||
|
}
|
||||||
|
val = HashMapGet(idObject, "aliases");
|
||||||
|
arr = JsonValueAsArray(val);
|
||||||
|
ArrayAdd(arr, JsonValueString(alias));
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!JsonGet(aliases, 2, "alias", alias))
|
HashMap *roomAlias;
|
||||||
|
char *id;
|
||||||
|
|
||||||
|
if (!(val = JsonGet(aliases, 2, "alias", alias)))
|
||||||
{
|
{
|
||||||
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
|
HttpResponseStatus(args->context, HTTP_NOT_FOUND);
|
||||||
response = MatrixErrorCreate(M_NOT_FOUND, "Room alias not found.");
|
response = MatrixErrorCreate(M_NOT_FOUND, "Room alias not found.");
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
roomAlias = JsonValueAsObject(val);
|
||||||
|
id = StrDuplicate(JsonValueAsString(HashMapGet(roomAlias, "id")));
|
||||||
|
|
||||||
if (!(UserGetPrivileges(user) & USER_ALIAS) && !StrEquals(UserGetName(user), JsonValueAsString(JsonGet(aliases, 3, "alias", alias, "createdBy"))))
|
if (!(UserGetPrivileges(user) & USER_ALIAS) && !StrEquals(UserGetName(user), JsonValueAsString(JsonGet(roomAlias, 1, "createdBy"))))
|
||||||
{
|
{
|
||||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
response = MatrixErrorCreate(M_UNAUTHORIZED, NULL);
|
response = MatrixErrorCreate(M_UNAUTHORIZED, NULL);
|
||||||
|
Free(id);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonValueFree(HashMapDelete(JsonValueAsObject(HashMapGet(aliases, "alias")), alias));
|
JsonValueFree(HashMapDelete(JsonValueAsObject(HashMapGet(aliases, "alias")), alias));
|
||||||
|
|
||||||
|
idObject = JsonValueAsObject(JsonGet(aliases, 2, "id", id));
|
||||||
|
if (idObject)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
val = HashMapGet(idObject, "aliases");
|
||||||
|
arr = JsonValueAsArray(val);
|
||||||
|
for (i = 0; i < ArraySize(arr); i++)
|
||||||
|
{
|
||||||
|
if (StrEquals(JsonValueAsString(ArrayGet(arr, i)), alias))
|
||||||
|
{
|
||||||
|
JsonValueFree(ArrayDelete(arr, i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Free(id);
|
||||||
}
|
}
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
|
|
||||||
|
@ -156,6 +234,8 @@ ROUTE_IMPL(RouteAliasDirectory, path, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
|
CommonIDFree(aliasID);
|
||||||
|
ConfigUnlock(&config);
|
||||||
UserUnlock(user);
|
UserUnlock(user);
|
||||||
DbUnlock(db, ref);
|
DbUnlock(db, ref);
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
|
|
|
@ -64,7 +64,7 @@ ROUTE_IMPL(RouteFilter, path, argp)
|
||||||
HashMap *response = NULL;
|
HashMap *response = NULL;
|
||||||
|
|
||||||
User *user = NULL;
|
User *user = NULL;
|
||||||
UserId *id = NULL;
|
CommonID *id = NULL;
|
||||||
char *token = NULL;
|
char *token = NULL;
|
||||||
|
|
||||||
char *serverName = NULL;
|
char *serverName = NULL;
|
||||||
|
@ -97,7 +97,7 @@ ROUTE_IMPL(RouteFilter, path, argp)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StrEquals(id->server, serverName))
|
if (!ParserServerNameEquals(id->server, serverName))
|
||||||
{
|
{
|
||||||
msg = "Cannot use /filter for non-local users.";
|
msg = "Cannot use /filter for non-local users.";
|
||||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
@ -119,7 +119,7 @@ ROUTE_IMPL(RouteFilter, path, argp)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StrEquals(id->localpart, UserGetName(user)))
|
if (!StrEquals(id->local, UserGetName(user)))
|
||||||
{
|
{
|
||||||
msg = "Unauthorized to use /filter.";
|
msg = "Unauthorized to use /filter.";
|
||||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
|
|
|
@ -49,7 +49,7 @@ ROUTE_IMPL(RouteLogin, path, argp)
|
||||||
LoginRequest loginRequest;
|
LoginRequest loginRequest;
|
||||||
LoginRequestUserIdentifier userIdentifier;
|
LoginRequestUserIdentifier userIdentifier;
|
||||||
|
|
||||||
UserId *userId = NULL;
|
CommonID *userId = NULL;
|
||||||
|
|
||||||
Db *db = args->matrixArgs->db;
|
Db *db = args->matrixArgs->db;
|
||||||
|
|
||||||
|
@ -160,8 +160,8 @@ ROUTE_IMPL(RouteLogin, path, argp)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StrEquals(userId->server, config.serverName)
|
if (!ParserServerNameEquals(userId->server, config.serverName)
|
||||||
|| !UserExists(db, userId->localpart))
|
|| !UserExists(db, userId->local))
|
||||||
{
|
{
|
||||||
msg = "Unknown user ID.";
|
msg = "Unknown user ID.";
|
||||||
HttpResponseStatus(args->context, HTTP_FORBIDDEN);
|
HttpResponseStatus(args->context, HTTP_FORBIDDEN);
|
||||||
|
@ -175,7 +175,7 @@ ROUTE_IMPL(RouteLogin, path, argp)
|
||||||
password = loginRequest.password;
|
password = loginRequest.password;
|
||||||
refreshToken = loginRequest.refresh_token;
|
refreshToken = loginRequest.refresh_token;
|
||||||
|
|
||||||
user = UserLock(db, userId->localpart);
|
user = UserLock(db, userId->local);
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,25 +25,80 @@
|
||||||
|
|
||||||
#include <Routes.h>
|
#include <Routes.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Db.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
#include <User.h>
|
||||||
|
|
||||||
ROUTE_IMPL(RouteRoomAliases, path, argp)
|
ROUTE_IMPL(RouteRoomAliases, path, argp)
|
||||||
{
|
{
|
||||||
RouteArgs *args = argp;
|
RouteArgs *args = argp;
|
||||||
char *roomId = ArrayGet(path, 0);
|
char *roomId = ArrayGet(path, 0);
|
||||||
|
char *token;
|
||||||
|
char *msg;
|
||||||
|
|
||||||
HashMap *request = NULL;
|
|
||||||
HashMap *response = NULL;
|
HashMap *response = NULL;
|
||||||
|
HashMap *aliases = NULL;
|
||||||
|
HashMap *reversealias = NULL;
|
||||||
|
|
||||||
|
JsonValue *val;
|
||||||
|
|
||||||
Db *db = args->matrixArgs->db;
|
Db *db = args->matrixArgs->db;
|
||||||
DbRef *ref = NULL;
|
DbRef *ref = NULL;
|
||||||
|
|
||||||
(void) roomId;
|
User *user = NULL;
|
||||||
|
|
||||||
/* TODO: Placeholder; remove. */
|
if (HttpRequestMethodGet(args->context) != HTTP_GET)
|
||||||
goto finish;
|
{
|
||||||
|
msg = "Route only accepts GET.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_FORBIDDEN);
|
||||||
|
response = MatrixErrorCreate(M_FORBIDDEN, msg);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Check whenever the user is in the room or if its world readable
|
||||||
|
* once this is implemented instead of just checking for the ALIAS
|
||||||
|
* privilege. */
|
||||||
|
if (!(UserGetPrivileges(user) & USER_ALIAS))
|
||||||
|
{
|
||||||
|
msg = "User is not allowed to get this room's aliases.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_FORBIDDEN);
|
||||||
|
response = MatrixErrorCreate(M_FORBIDDEN, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbLock(db, 1, "aliases");
|
||||||
|
aliases = DbJson(ref);
|
||||||
|
reversealias = JsonValueAsObject(JsonGet(aliases, 2, "id", roomId));
|
||||||
|
if (!reversealias)
|
||||||
|
{
|
||||||
|
/* We do not know about the room ID. */
|
||||||
|
msg = "Unknown room ID.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = HashMapCreate();
|
||||||
|
val = JsonGet(reversealias, 1, "aliases");
|
||||||
|
HashMapSet(response, "aliases", JsonValueDuplicate(val));
|
||||||
finish:
|
finish:
|
||||||
DbUnlock(db, ref);
|
DbUnlock(db, ref);
|
||||||
JsonFree(request);
|
UserUnlock(user);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ ROUTE_IMPL(RouteUserProfile, path, argp)
|
||||||
HashMap *request = NULL;
|
HashMap *request = NULL;
|
||||||
HashMap *response = NULL;
|
HashMap *response = NULL;
|
||||||
|
|
||||||
UserId *userId = NULL;
|
CommonID *userId = NULL;
|
||||||
User *user = NULL;
|
User *user = NULL;
|
||||||
|
|
||||||
char *serverName;
|
char *serverName;
|
||||||
|
@ -73,7 +73,7 @@ ROUTE_IMPL(RouteUserProfile, path, argp)
|
||||||
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
response = MatrixErrorCreate(M_INVALID_PARAM, msg);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
if (strcmp(userId->server, serverName))
|
if (!ParserServerNameEquals(userId->server, serverName))
|
||||||
{
|
{
|
||||||
/* TODO: Implement lookup over federation. */
|
/* TODO: Implement lookup over federation. */
|
||||||
msg = "User profile endpoint currently doesn't support lookup over "
|
msg = "User profile endpoint currently doesn't support lookup over "
|
||||||
|
@ -87,7 +87,7 @@ ROUTE_IMPL(RouteUserProfile, path, argp)
|
||||||
switch (HttpRequestMethodGet(args->context))
|
switch (HttpRequestMethodGet(args->context))
|
||||||
{
|
{
|
||||||
case HTTP_GET:
|
case HTTP_GET:
|
||||||
user = UserLock(db, userId->localpart);
|
user = UserLock(db, userId->local);
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
msg = "Couldn't lock user.";
|
msg = "Couldn't lock user.";
|
||||||
|
@ -147,11 +147,11 @@ ROUTE_IMPL(RouteUserProfile, path, argp)
|
||||||
StrEquals(entry, "avatar_url"))
|
StrEquals(entry, "avatar_url"))
|
||||||
{
|
{
|
||||||
/* Check if user has privilege to do that action. */
|
/* Check if user has privilege to do that action. */
|
||||||
if (StrEquals(userId->localpart, UserGetName(user)))
|
if (StrEquals(userId->local, UserGetName(user)))
|
||||||
{
|
{
|
||||||
value = JsonValueAsString(HashMapGet(request, entry));
|
value = JsonValueAsString(HashMapGet(request, entry));
|
||||||
/* TODO: Make UserSetProfile notify other
|
/* TODO: Make UserSetProfile notify other parties of
|
||||||
* parties of the change */
|
* the change */
|
||||||
UserSetProfile(user, entry, value);
|
UserSetProfile(user, entry, value);
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
|
@ -37,9 +37,14 @@ ROUTE_IMPL(RouteVersions, path, argp)
|
||||||
(void) argp;
|
(void) argp;
|
||||||
|
|
||||||
#define DECLARE_SPEC_VERSION(x) ArrayAdd(versions, JsonValueString(x))
|
#define DECLARE_SPEC_VERSION(x) ArrayAdd(versions, JsonValueString(x))
|
||||||
|
DECLARE_SPEC_VERSION("v1.2");
|
||||||
|
DECLARE_SPEC_VERSION("v1.3");
|
||||||
|
DECLARE_SPEC_VERSION("v1.4");
|
||||||
|
DECLARE_SPEC_VERSION("v1.5");
|
||||||
|
DECLARE_SPEC_VERSION("v1.6");
|
||||||
|
|
||||||
|
/* The curently supported version. */
|
||||||
DECLARE_SPEC_VERSION("v1.7");
|
DECLARE_SPEC_VERSION("v1.7");
|
||||||
/* Declare additional spec version support here. */
|
|
||||||
|
|
||||||
#undef DECLARE_SPEC_VERSION
|
#undef DECLARE_SPEC_VERSION
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ ROUTE_IMPL(RouteWhoami, path, argp)
|
||||||
char *token;
|
char *token;
|
||||||
char *userID;
|
char *userID;
|
||||||
char *deviceID;
|
char *deviceID;
|
||||||
char *msg;
|
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
|
|
||||||
|
|
|
@ -351,7 +351,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
|
||||||
char *password = JsonValueAsString(HashMapGet(auth, "password"));
|
char *password = JsonValueAsString(HashMapGet(auth, "password"));
|
||||||
HashMap *identifier = JsonValueAsObject(HashMapGet(auth, "identifier"));
|
HashMap *identifier = JsonValueAsObject(HashMapGet(auth, "identifier"));
|
||||||
char *type;
|
char *type;
|
||||||
UserId *userId;
|
CommonID *userId;
|
||||||
User *user;
|
User *user;
|
||||||
|
|
||||||
if (!password || !identifier)
|
if (!password || !identifier)
|
||||||
|
@ -366,7 +366,8 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
|
||||||
config.serverName);
|
config.serverName);
|
||||||
|
|
||||||
if (!type || !StrEquals(type, "m.id.user")
|
if (!type || !StrEquals(type, "m.id.user")
|
||||||
|| !userId || !StrEquals(userId->server, config.serverName))
|
|| !userId
|
||||||
|
|| !ParserServerNameEquals(userId->server, config.serverName))
|
||||||
{
|
{
|
||||||
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
|
||||||
ret = BuildResponse(flows, db, response, session, dbRef);
|
ret = BuildResponse(flows, db, response, session, dbRef);
|
||||||
|
@ -374,7 +375,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
user = UserLock(db, userId->localpart);
|
user = UserLock(db, userId->local);
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(context, HTTP_UNAUTHORIZED);
|
||||||
|
|
40
src/User.c
40
src/User.c
|
@ -31,6 +31,8 @@
|
||||||
#include <Cytoplasm/Int64.h>
|
#include <Cytoplasm/Int64.h>
|
||||||
#include <Cytoplasm/UInt64.h>
|
#include <Cytoplasm/UInt64.h>
|
||||||
|
|
||||||
|
#include <Parser.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct User
|
struct User
|
||||||
|
@ -882,10 +884,11 @@ finish:
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserId *
|
CommonID *
|
||||||
UserIdParse(char *id, char *defaultServer)
|
UserIdParse(char *id, char *defaultServer)
|
||||||
{
|
{
|
||||||
UserId *userId;
|
CommonID *userId;
|
||||||
|
char *server;
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
{
|
{
|
||||||
|
@ -898,48 +901,38 @@ UserIdParse(char *id, char *defaultServer)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
userId = Malloc(sizeof(UserId));
|
userId = Malloc(sizeof(CommonID));
|
||||||
if (!userId)
|
if (!userId)
|
||||||
{
|
{
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
memset(userId, 0, sizeof(CommonID));
|
||||||
|
|
||||||
/* Fully-qualified user ID */
|
/* Fully-qualified user ID */
|
||||||
if (*id == '@')
|
if (*id == '@')
|
||||||
{
|
{
|
||||||
char *localStart = id + 1;
|
if (!ParseCommonID(id, userId) || !userId->server.hostname)
|
||||||
char *serverStart = localStart;
|
|
||||||
|
|
||||||
while (*serverStart != ':' && *serverStart != '\0')
|
|
||||||
{
|
{
|
||||||
serverStart++;
|
UserIdFree(userId);
|
||||||
}
|
|
||||||
|
|
||||||
if (*serverStart == '\0')
|
|
||||||
{
|
|
||||||
Free(userId);
|
|
||||||
userId = NULL;
|
userId = NULL;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
*serverStart = '\0';
|
|
||||||
serverStart++;
|
|
||||||
|
|
||||||
userId->localpart = StrDuplicate(localStart);
|
|
||||||
userId->server = StrDuplicate(serverStart);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Treat it as just a localpart */
|
/* Treat it as just a localpart */
|
||||||
userId->localpart = StrDuplicate(id);
|
userId->local = StrDuplicate(id);
|
||||||
userId->server = StrDuplicate(defaultServer);
|
ParseServerPart(defaultServer, &userId->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UserHistoricalValidate(userId->localpart, userId->server))
|
server = ParserRecomposeServerPart(userId->server);
|
||||||
|
if (!UserHistoricalValidate(userId->local, server))
|
||||||
{
|
{
|
||||||
UserIdFree(userId);
|
UserIdFree(userId);
|
||||||
userId = NULL;
|
userId = NULL;
|
||||||
}
|
}
|
||||||
|
Free(server);
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
Free(id);
|
Free(id);
|
||||||
|
@ -947,12 +940,11 @@ finish:
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UserIdFree(UserId * id)
|
UserIdFree(CommonID * id)
|
||||||
{
|
{
|
||||||
if (id)
|
if (id)
|
||||||
{
|
{
|
||||||
Free(id->localpart);
|
CommonIDFree(*id);
|
||||||
Free(id->server);
|
|
||||||
Free(id);
|
Free(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
106
src/include/Parser.h
Normal file
106
src/include/Parser.h
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef TELODENDRIA_PARSER_H
|
||||||
|
#define TELODENDRIA_PARSER_H
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @Nm Parser
|
||||||
|
* @Nd Functions for dealing with grammars found in Matrix
|
||||||
|
* @Dd November 25 2023
|
||||||
|
* @Xr User
|
||||||
|
*
|
||||||
|
* The
|
||||||
|
* .Nm
|
||||||
|
* API provides an interface for parsing grammars within the
|
||||||
|
* Matrix specification
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The host[:port] format in a servername.
|
||||||
|
*/
|
||||||
|
typedef struct ServerPart {
|
||||||
|
char *hostname;
|
||||||
|
char *port;
|
||||||
|
} ServerPart;
|
||||||
|
/**
|
||||||
|
* A common identifier in the form '&local[:server]', where & determines the
|
||||||
|
* *type* of the identifier.
|
||||||
|
*/
|
||||||
|
typedef struct CommonID {
|
||||||
|
char sigil;
|
||||||
|
char *local;
|
||||||
|
ServerPart server;
|
||||||
|
} CommonID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a common identifier, as per the Common Identifier Format as defined
|
||||||
|
* by the [matrix] specification.
|
||||||
|
*/
|
||||||
|
extern int ParseCommonID(char *, CommonID *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the server part in a common identifier.
|
||||||
|
*/
|
||||||
|
extern int ParseServerPart(char *, ServerPart *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whenever the string is a valid common ID with the correct sigil.
|
||||||
|
*/
|
||||||
|
extern int ValidCommonID(char *, char);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees a CommonID's values. Note that it doesn't free the CommonID itself.
|
||||||
|
*/
|
||||||
|
extern void CommonIDFree(CommonID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees a ServerPart's values. Note that it doesn't free the ServerPart
|
||||||
|
* itself, and that
|
||||||
|
* .Fn CommonIDFree
|
||||||
|
* automatically deals with its server part.
|
||||||
|
*/
|
||||||
|
extern void ServerPartFree(ServerPart);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recompose a Common ID into a string which lives in the heap, and must
|
||||||
|
* therefore be freed with
|
||||||
|
* .Fn Free .
|
||||||
|
*/
|
||||||
|
extern char * ParserRecomposeCommonID(CommonID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recompose a server part into a string which lives in the heap, and must
|
||||||
|
* therefore be freed with
|
||||||
|
* .Fn Free .
|
||||||
|
*/
|
||||||
|
extern char * ParserRecomposeServerPart(ServerPart);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares whenever a ServerName is equivalent to a server name string.
|
||||||
|
*/
|
||||||
|
extern int ParserServerNameEquals(ServerPart, char *);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* TELODENDRIA_PARSER_H */
|
|
@ -43,6 +43,8 @@
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
|
|
||||||
|
#include <Parser.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Many functions here operate on an opaque user structure.
|
* Many functions here operate on an opaque user structure.
|
||||||
*/
|
*/
|
||||||
|
@ -88,15 +90,6 @@ typedef struct UserLoginInfo
|
||||||
char *refreshToken;
|
char *refreshToken;
|
||||||
} UserLoginInfo;
|
} UserLoginInfo;
|
||||||
|
|
||||||
/**
|
|
||||||
* A description of a Matrix user ID.
|
|
||||||
*/
|
|
||||||
typedef struct UserId
|
|
||||||
{
|
|
||||||
char *localpart;
|
|
||||||
char *server;
|
|
||||||
} UserId;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take a localpart and domain as separate parameters and validate them
|
* Take a localpart and domain as separate parameters and validate them
|
||||||
* against the rules of the Matrix specification. The reasion the
|
* against the rules of the Matrix specification. The reasion the
|
||||||
|
@ -303,15 +296,15 @@ extern Array *UserEncodePrivileges(int);
|
||||||
extern int UserDecodePrivilege(const char *);
|
extern int UserDecodePrivilege(const char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse either a localpart or a fully qualified Matrix ID. If the
|
* Parse either a localpart or a fully qualified Matrix common ID. If the
|
||||||
* first argument is a localpart, then the second argument is used as
|
* first argument is a localpart, then the second argument is used as
|
||||||
* the server name.
|
* the server name.
|
||||||
*/
|
*/
|
||||||
extern UserId * UserIdParse(char *, char *);
|
extern CommonID * UserIdParse(char *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free the memory associated with the parsed Matrix ID.
|
* Frees the user's common ID and the memory allocated for it.
|
||||||
*/
|
*/
|
||||||
extern void UserIdFree(UserId *);
|
extern void UserIdFree(CommonID *);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_USER_H */
|
#endif /* TELODENDRIA_USER_H */
|
||||||
|
|
Loading…
Reference in a new issue