forked from Telodendria/Telodendria
Merge branch 'master' into refactor-6
This commit is contained in:
commit
a8a9bc57c6
14 changed files with 154 additions and 60 deletions
23
README.md
23
README.md
|
@ -1,5 +1,4 @@
|
|||
<p align="center"><img src="https://telodendria.io/assets/Telodendria-500x500.png"></p>
|
||||
<h1 align="center">Telodendria</h1>
|
||||
<h1 style="text-align: center;">Telodendria</h1>
|
||||
|
||||
Telodendria is an extremely powerful, yet lightweight and portable
|
||||
chat server designed to be easy to install and configure. Powered by
|
||||
|
@ -12,7 +11,7 @@ hosting a complicated, high-maintenance homeserver or joining an
|
|||
existing homeserver for privacy or other reasons, then Telodendria
|
||||
might be for you.
|
||||
|
||||
> **Note:** Telodendria still in development. See [Status](#status).
|
||||
!!!! **Note:** Telodendria still in development. See **Status** below.
|
||||
|
||||
## What is Matrix?
|
||||
|
||||
|
@ -64,12 +63,13 @@ incredibly outdated. Telodendria, on the other hand, aims to be stable.
|
|||
It should *just work* for long periods of time between upgrades, and
|
||||
you should never feel like Telodendria is going to change significantly
|
||||
between upgrades.
|
||||
- **Well-Documented:** Telodendria places as much emphasis on documentation as on code, which means you can be sure that the documentation will always remain up-to-date, accurate, and most importantly, reasonably exhaustive.
|
||||
|
||||
[Read Technical Rationale →](docs/dev/rationale.md)
|
||||
[Read Technical Rationale →](https://git.telodendria.io/Telodendria/Telodendria/src/branch/master/docs/dev/rationale.md)
|
||||
|
||||
## Get Started
|
||||
|
||||
Check out the [Documentation](docs/README.md) to get started with
|
||||
Check out the [Documentation](https://git.telodendria.io/Telodendria/telodendria/src/branch/master/docs/README.md) to get started with
|
||||
Telodendria.
|
||||
|
||||
## Status
|
||||
|
@ -79,13 +79,13 @@ not yet deliver on all of its promises. Currently, Telodendria is not
|
|||
ready for end-users yet. While it features very basic user
|
||||
authentication, it does not actually work as a chat server yet.
|
||||
|
||||
We are hoping to ship Telodendria `v0.4.0` by May of 2024. This
|
||||
We are hoping to ship Telodendria `v1.7.0-alpha4` by May of 2024. This
|
||||
release should be usable for communication between **local users**
|
||||
only. Additional features, including federation with other Matrix
|
||||
homeservers will be added in future releases.
|
||||
|
||||
You can help speed up development by [sponsoring](#sponsorship)
|
||||
Telodendria or [getting involved](docs/CONTRIBUTING.md).
|
||||
You can help speed up development by **sponsoring**
|
||||
Telodendria or [getting involved](https://git.telodendria.io/Telodendria/Telodendria/src/branch/master/docs/CONTRIBUTING.md).
|
||||
|
||||
## Sponsorship
|
||||
|
||||
|
@ -96,7 +96,7 @@ Telodendria's long-term success, please consider sponsoring the
|
|||
project.
|
||||
|
||||
You can make a recurring donation to Telodendria using
|
||||
[LiberaPay](https://bancino.net/Telodendria/donate). You can also make
|
||||
[LiberaPay](https://liberapay.com/Telodendria/donate). You can also make
|
||||
one-time donations using
|
||||
[Stripe](https://donate.stripe.com/8wM29AfF5bRJc48eUU). If you would
|
||||
like to make a recurring donation larger than that allowed by
|
||||
|
@ -108,7 +108,7 @@ LiberaPay, please contact Jordan Bancino over Matrix at
|
|||
While there are no set sponsorship tiers at this time, sponsoring
|
||||
Telodendria is a mutually beneficial relationship. Depending on the
|
||||
amount you donate, you can get your name, logo, and website links
|
||||
on the [Sponsors](docs/SPONSORS.md) page, the project `README`, or the
|
||||
on the [Sponsors](../sponsors) page, the project `README`, or the
|
||||
main website.
|
||||
|
||||
## License
|
||||
|
@ -116,7 +116,7 @@ main website.
|
|||
All of the code and documentation for Telodendria is licensed under a
|
||||
modified MIT license. The MIT license is an extremely permissive
|
||||
license that has very few restrictions. Please consult the
|
||||
[`LICENSE.txt`](LICENSE.txt) file for the actual license text. It is
|
||||
[`LICENSE.txt`](https://git.telodendria.io/Telodendria/Telodendria/src/branch/master/LICENSE.txt) file for the actual license text. It is
|
||||
important to note that the Telodendria license text differs from the
|
||||
original MIT license in the following ways:
|
||||
|
||||
|
@ -133,4 +133,3 @@ to use the logo in any way as long as it represents or links to the
|
|||
official project. If Telodendria is forked, the logo must be removed
|
||||
completely from the project, and optionally replaced by a different
|
||||
one.
|
||||
|
||||
|
|
41
configure
vendored
41
configure
vendored
|
@ -14,12 +14,12 @@ INCLUDE="src/include"
|
|||
TOOLS="tools/src"
|
||||
SCHEMA="Schema"
|
||||
|
||||
CFLAGS="-Wall -Wextra -pedantic -std=c89 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE}"
|
||||
CFLAGS="-Wall -Wextra -pedantic -std=c89 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE} -I${BUILD}"
|
||||
LIBS="-lm -pthread -lCytoplasm"
|
||||
|
||||
|
||||
# Set default args for all platforms
|
||||
SCRIPT_ARGS="--prefix=/usr/local --enable-ld-extra --bin-name=telodendria --version=0.4.0 --static $@"
|
||||
SCRIPT_ARGS="--cc=cc --prefix=/usr/local --enable-ld-extra --bin-name=telodendria --version=1.7.0-alpha4 --static $@"
|
||||
|
||||
echo "Processing options..."
|
||||
echo "Ran with arguments: $SCRIPT_ARGS"
|
||||
|
@ -27,6 +27,9 @@ echo "Ran with arguments: $SCRIPT_ARGS"
|
|||
# Process all arguments
|
||||
for arg in $SCRIPT_ARGS; do
|
||||
case "$arg" in
|
||||
--cc=*)
|
||||
CC=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--prefix=*)
|
||||
PREFIX=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
|
@ -60,7 +63,7 @@ for arg in $SCRIPT_ARGS; do
|
|||
STATIC=""
|
||||
;;
|
||||
*)
|
||||
echo "Invalid argument: $1"
|
||||
echo "Invalid argument: $arg"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
@ -112,8 +115,8 @@ compile_obj() {
|
|||
src="$1"
|
||||
obj="$2"
|
||||
|
||||
pref=$(cc -I${INCLUDE} -MM -MT "${obj}" "${src}")
|
||||
echo "$pref $(collect ${SCHEMA}/ .json .h ${INCLUDE}/Schema/ print_obj)"
|
||||
pref=$(${CC} -I${INCLUDE} -I${BUILD} -MM -MT "${obj}" "${src}")
|
||||
echo "$pref $(collect ${SCHEMA}/ .json .h ${BUILD}/Schema/ print_obj)"
|
||||
echo "${TAB}@mkdir -p $(dirname ${obj})"
|
||||
echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\""
|
||||
}
|
||||
|
@ -153,13 +156,19 @@ compile_schema() {
|
|||
src="$1"
|
||||
out="$2"
|
||||
|
||||
echo "${INCLUDE}/Schema/${out}.h:"
|
||||
echo "${TAB}@mkdir -p ${INCLUDE}/Schema ${SRC}/Schema"
|
||||
echo "${TAB}j2s -s \"${src}\" -h \"${INCLUDE}/Schema/${out}.h\" -c \"${SRC}/Schema/${out}.c\""
|
||||
obj="${BUILD}/Schema/${out}.o"
|
||||
|
||||
echo "${SRC}/Schema/${out}.c:"
|
||||
echo "${TAB}@mkdir -p ${INCLUDE}/Schema ${SRC}/Schema"
|
||||
echo "${TAB}j2s -s \"${src}\" -h \"${INCLUDE}/Schema/${out}.h\" -c \"${SRC}/Schema/${out}.c\""
|
||||
echo "${BUILD}/Schema/${out}.h:"
|
||||
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
||||
echo "${TAB}j2s -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""
|
||||
|
||||
echo "${BUILD}/Schema/${out}.c:"
|
||||
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
||||
echo "${TAB}j2s -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""
|
||||
|
||||
echo "${obj}: ${src} ${BUILD}/Schema/${out}.c"
|
||||
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
||||
echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${BUILD}/Schema/${out}.c\""
|
||||
}
|
||||
|
||||
install_out() {
|
||||
|
@ -185,22 +194,16 @@ uninstall_out() {
|
|||
|
||||
echo "Generating Makefile..."
|
||||
|
||||
OBJS=$(collect ${SRC}/ .c .o ${BUILD}/ print_obj)
|
||||
OBJS="$(collect ${SRC}/ .c .o ${BUILD}/ print_obj) $(collect ${SCHEMA}/ .json .o ${BUILD}/Schema/ print_obj)"
|
||||
TAB=$(printf '\t')
|
||||
|
||||
# If objects don't include the schema (this is the first configure),
|
||||
# then include them manually.
|
||||
if ! echo "${OBJS}" | grep "Schema" > /dev/null; then
|
||||
OBJS="${OBJS} $(collect ${SCHEMA}/ .json .o ${BUILD}/Schema/ print_obj)"
|
||||
fi
|
||||
|
||||
cat << EOF > Makefile
|
||||
.POSIX:
|
||||
|
||||
# Generated by '$0' on $(date).
|
||||
# This file should generally not be manually edited.
|
||||
|
||||
CC = cc
|
||||
CC = ${CC}
|
||||
PREFIX = ${PREFIX}
|
||||
CFLAGS = ${CFLAGS}
|
||||
LDFLAGS = ${LDFLAGS}
|
||||
|
|
|
@ -5,12 +5,22 @@ It is intended to be updated with every commit that makes a user-facing change w
|
|||
reporting in the change log. As such, it changes frequently between releases. Final
|
||||
change log entries are published as [Releases](releases).
|
||||
|
||||
## v0.4.0
|
||||
## v1.7.0-alpha4
|
||||
|
||||
**Not Released Yet.**
|
||||
|
||||
This release brings filters, rooms, and events! The core of the Matrix protocol architecture
|
||||
is not in place.
|
||||
This release brings filters, rooms, and events! The core of the Matrix
|
||||
protocol architecture is now in place.
|
||||
|
||||
Note that the versioning scheme has changed from `v0.X.0` to
|
||||
`v1.7.0-alphaX`. This is so that Telodendria releases correspond to the
|
||||
Matrix specification that they implement, in accordance with
|
||||
[this blog post](https://telodendria.io/blog/on-matrixs-release-cadence-and-state-resolution-v1).
|
||||
This versioning scheme change does not indicate a drastic leap forward
|
||||
in Telodendria's development—the `-alpha4` suffix indicates that
|
||||
this is the 4th pre-release, with the target being a stable `v1.7.0`.
|
||||
Note also that we still have a *long* way to go before we reach that
|
||||
stable release.
|
||||
|
||||
### Matrix Specification
|
||||
|
||||
|
@ -21,19 +31,23 @@ The following endpoints were added:
|
|||
|
||||
### Bug Fixes & General Improvements
|
||||
|
||||
- Fixed a double-free in `RouteUserProfile()` that would cause errors with certain
|
||||
Matrix clients. (#35)
|
||||
- Fixed a double-free in `RouteUserProfile()` that would cause errors
|
||||
with certain Matrix clients. (#35)
|
||||
- Improved compatibility with NetBSD on various platforms.
|
||||
- Moved [Cytoplasm](/Telodendria/Cytoplasm) to its own repository.
|
||||
- Use a `configure` script and `make` to build Telodendria instead of custom scripts.
|
||||
- Moved [Cytoplasm](/Telodendria/Cytoplasm) to its own repository. It
|
||||
will now be maintained separately and have its own releases as well.
|
||||
- Use a `configure` script and `make` to build Telodendria instead of
|
||||
custom scripts.
|
||||
|
||||
### New Features
|
||||
|
||||
- Implemented `/_telodendria/admin/v1/deactivate/[localpart]` for admins to be able to
|
||||
deactivate users.
|
||||
- Moved all administrator API endpoints to `/_telodendria/admin/v1`, because later revisions
|
||||
of the administrator API may break clients, so we want a way to give those breaking revisions
|
||||
new endpoints.
|
||||
- Moved all administrator API endpoints to `/_telodendria/admin/v1`,
|
||||
because later revisions of the administrator API may break clients, so
|
||||
we want a way to give those breaking revisions new endpoints.
|
||||
- Implemented `/_telodendria/admin/v1/deactivate/[localpart]` for admins
|
||||
to be able to deactivate users.
|
||||
- Added a **PUT** option to `/_telodendria/admin/v1/config` that gives
|
||||
the ability to change only a subset of the configuration.
|
||||
|
||||
## v0.3.0
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@ As mentioned in [Setup](../setup.md), Telodendria's configuration is
|
|||
intended to be managed via the configuration API. Consult the
|
||||
[Configuration](../config.md) document for a complete list of supported
|
||||
configuration options. This document simply describes the API used to
|
||||
update the configuration.
|
||||
update the configuration described in that document.
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### **GET** `/_telodendria/admin/config`
|
||||
### **GET** `/_telodendria/admin/v1/config`
|
||||
|
||||
Retrieve the current configuration.
|
||||
|
||||
|
@ -20,7 +20,7 @@ Retrieve the current configuration.
|
|||
|---------------|-------------|
|
||||
| 200 | The current configuration was successfully retrieved.|
|
||||
|
||||
### **POST** `/_telodendria/admin/config`
|
||||
### **POST** `/_telodendria/admin/v1/config`
|
||||
|
||||
Installs a new configuration. This endpoint validates the request body,
|
||||
ensuring it is a proper configuration, then it replaces the existing
|
||||
|
@ -40,3 +40,23 @@ configuration with the new one.
|
|||
|-------|------|-------------|
|
||||
| `restart_required` | `Boolean` | Whether or not the process needs to be restarted to finish applying the configuration. If this is `true`, then the restart endpoint should be used at a convenient time to apply the configuration.
|
||||
|
||||
### **PUT** `/_telodendria/admin/v1/config`
|
||||
|
||||
Update the currently installed configuration instead of completely replacing it. This endpoint
|
||||
validates the request body, merges it on top of the current configuration, validates the resulting
|
||||
configuration, then updates it in the database. This is useful when only one or two properties
|
||||
in the configuration needs to be changed.
|
||||
|
||||
| Requires Token | Rate Limited |
|
||||
|----------------|--------------|
|
||||
| Yes | Yes |
|
||||
|
||||
| Response Code | Description |
|
||||
|---------------|-------------|
|
||||
| 200 | The new configuration was successfully installed.|
|
||||
|
||||
#### 200 Response Format
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `restart_required` | `Boolean` | Whether or not the process needs to be restarted to finish applying the configuration. If this is `true`, then the restart endpoint should be used at a convenient time to apply the configuration.
|
||||
|
|
|
@ -41,7 +41,7 @@ this privilege level.
|
|||
|
||||
The following API endpoints are implemented for managing privileges.
|
||||
|
||||
### **GET** `/_telodendria/admin/privileges/[localpart]`
|
||||
### **GET** `/_telodendria/admin/v1/privileges/[localpart]`
|
||||
|
||||
Retrieve the permissions for a user. If the localpart is omitted, then
|
||||
retrieve the privileges for the user that owns the access token being
|
||||
|
@ -62,7 +62,7 @@ used. Note that the owner of the access token must have the
|
|||
|-------|------|-------------|
|
||||
| `privileges` | `Array` | An array of privileges, as described above. The privileges are encoded as JSON strings.|
|
||||
|
||||
### **POST** `/_telodendria/admin/privileges/[localpart]`
|
||||
### **POST** `/_telodendria/admin/v1/privileges/[localpart]`
|
||||
|
||||
Update the privileges of a local user by replacing the privileges array
|
||||
with the one specified in the request. Like the **GET** version of this
|
||||
|
@ -89,7 +89,7 @@ owns the access token.
|
|||
|-------|------|-------------|
|
||||
| `privileges` | `Array` | An array of privileges, as described above. The privileges are encoded as JSON strings.|
|
||||
|
||||
### **PUT** `/_telodendria/admin/privileges/[localpart]`
|
||||
### **PUT** `/_telodendria/admin/v1/privileges/[localpart]`
|
||||
|
||||
Update the privileges of a local user by adding the privileges
|
||||
specified in the request to the users existing privileges.
|
||||
|
@ -114,7 +114,7 @@ specified in the request to the users existing privileges.
|
|||
|-------|------|-------------|
|
||||
| `privileges` | `Array` | An array of privileges, as described above. The privileges are encoded as JSON strings.|
|
||||
|
||||
### **DELETE** `/_telodendria/admin/privileges/[localpart]`
|
||||
### **DELETE** `/_telodendria/admin/v1/privileges/[localpart]`
|
||||
|
||||
Update the privileges of a local user by removing the privileges
|
||||
specified in the request from the user's existing privileges.
|
||||
|
|
|
@ -5,7 +5,7 @@ administrator to manage the Telodendria process itself.
|
|||
|
||||
## API Endpoints
|
||||
|
||||
### **POST** `/_telodendria/admin/restart`
|
||||
### **POST** `/_telodendria/admin/v1/restart`
|
||||
|
||||
Restart the Telodendria daemon cleanly. This endpoint will respond
|
||||
immediately after signaling to the daemon that it should be restarted
|
||||
|
@ -26,7 +26,7 @@ starts over.
|
|||
|
||||
On success, this endpoint simply returns an empty JSON object.
|
||||
|
||||
### **POST** `/_telodendria/admin/shutdown`
|
||||
### **POST** `/_telodendria/admin/v1/shutdown`
|
||||
|
||||
Shut down the Telodendria process cleanly. This endpoint will respond
|
||||
immediately after signalling to the daemon that it should be shut
|
||||
|
|
|
@ -5,7 +5,7 @@ information about how the server process is performing.
|
|||
|
||||
## API Endpoints
|
||||
|
||||
### **GET** `/_telodendria/admin/stats`
|
||||
### **GET** `/_telodendria/admin/v1/stats`
|
||||
|
||||
Retrieve basic statistics about the currently running Telodendria
|
||||
process.
|
||||
|
|
|
@ -28,5 +28,7 @@
|
|||
HashMap *
|
||||
FilterApply(Filter * filter, HashMap * event)
|
||||
{
|
||||
(void) filter;
|
||||
(void) event;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -128,10 +128,6 @@ start:
|
|||
httpServers = NULL;
|
||||
restart = 0;
|
||||
|
||||
/* For getopt() */
|
||||
opterr = 1;
|
||||
optind = 1;
|
||||
|
||||
/* Local variables */
|
||||
exit = EXIT_SUCCESS;
|
||||
flags = 0;
|
||||
|
|
|
@ -42,6 +42,8 @@ struct Room
|
|||
Room *
|
||||
RoomCreate(Db * db, RoomCreateRequest * req)
|
||||
{
|
||||
(void) db;
|
||||
(void) req;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ ROUTE_IMPL(RouteConfig, path, argp)
|
|||
|
||||
HashMap *request = NULL;
|
||||
Config *newConf;
|
||||
HashMap *newJson = NULL;
|
||||
|
||||
(void) path;
|
||||
|
||||
|
@ -121,10 +122,56 @@ ROUTE_IMPL(RouteConfig, path, argp)
|
|||
JsonFree(request);
|
||||
break;
|
||||
case HTTP_PUT:
|
||||
/* TODO: Support incremental changes to the config */
|
||||
request = JsonDecode(HttpServerStream(args->context));
|
||||
if (!request)
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
newJson = JsonDuplicate(DbJson(config->ref));
|
||||
JsonMerge(newJson, request);
|
||||
|
||||
newConf = ConfigParse(newJson);
|
||||
|
||||
if (!newConf)
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||
response = MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (newConf->ok)
|
||||
{
|
||||
if (DbJsonSet(config->ref, newJson))
|
||||
{
|
||||
response = HashMapCreate();
|
||||
/*
|
||||
* TODO: Apply configuration and set this only if a main
|
||||
* component was reconfigured, such as the listeners.
|
||||
*/
|
||||
HashMapSet(response, "restart_required", JsonValueBoolean(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||
response = MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_BAD_JSON, newConf->err);
|
||||
}
|
||||
|
||||
ConfigFree(newConf);
|
||||
JsonFree(request);
|
||||
JsonFree(newJson);
|
||||
break;
|
||||
default:
|
||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||
response = MatrixErrorCreate(M_UNRECOGNIZED, NULL);
|
||||
response = MatrixErrorCreate(M_UNRECOGNIZED, "Unknown request method.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,10 @@ ROUTE_IMPL(RouteRoomAliases, path, argp)
|
|||
Db *db = args->matrixArgs->db;
|
||||
DbRef *ref = NULL;
|
||||
|
||||
(void) roomId;
|
||||
|
||||
/* TODO: Placeholder; remove. */
|
||||
goto finish;
|
||||
finish:
|
||||
DbUnlock(db, ref);
|
||||
JsonFree(request);
|
||||
|
|
|
@ -35,7 +35,12 @@ ROUTE_IMPL(RouteVersions, path, argp)
|
|||
(void) path;
|
||||
(void) argp;
|
||||
|
||||
ArrayAdd(versions, JsonValueString("v1.6"));
|
||||
#define DECLARE_SPEC_VERSION(x) ArrayAdd(versions, JsonValueString(x))
|
||||
|
||||
DECLARE_SPEC_VERSION("v1.7");
|
||||
/* Declare additional spec version support here. */
|
||||
|
||||
#undef DECLARE_SPEC_VERSION
|
||||
|
||||
HashMapSet(response, "versions", JsonValueArray(versions));
|
||||
return response;
|
||||
|
|
|
@ -33,12 +33,14 @@
|
|||
static HashMap *
|
||||
StateResolveV1(Array * states)
|
||||
{
|
||||
(void) states;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HashMap *
|
||||
StateResolveV2(Array * states)
|
||||
{
|
||||
(void) states;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue