forked from Telodendria/Telodendria
Compare commits
39 commits
implement-
...
master
Author | SHA1 | Date | |
---|---|---|---|
71491ab6fc | |||
5041e0c991 | |||
66c237727b | |||
505d01cea1 | |||
c7433bb745 | |||
8546ddb909 | |||
0e946b2422 | |||
12e11f74a1 | |||
3304f8c08f | |||
59e3a6d3e2 | |||
46dcf8ab59 | |||
21b830e514 | |||
42223c94bb | |||
e083ece2ed | |||
2fcd51e810 | |||
775b1d9571 | |||
6dd4440125 | |||
e263eca5dc | |||
ac9372a30a | |||
a8924b8437 | |||
ff85b72899 | |||
bccbb3bcac | |||
dede82ad33 | |||
fde2b26857 | |||
6305f5d76e | |||
95a5f6f087 | |||
129802fe94 | |||
c7d44866c3 | |||
85672985eb | |||
19443a1c24 | |||
15fb6d8c2a | |||
cd22aea772 | |||
ae0724f01c | |||
e62389aa14 | |||
f2a4a64b27 | |||
258f509413 | |||
2e92daeb00 | |||
ac7ea4dec1 | |||
54420f0036 |
29 changed files with 662 additions and 245 deletions
27
.forgejo/workflows/compile.yaml
Normal file
27
.forgejo/workflows/compile.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
name: Compile Telodendria
|
||||||
|
run-name: Compile Telodendria on ${{ forgejo.actor }}
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'ma*'
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
"Compile Telodendria":
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [alpine]
|
||||||
|
arch: [aarch64]
|
||||||
|
runs-on: ["${{ matrix.os }}", "${{ matrix.arch }}"]
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Configure Telodendria
|
||||||
|
run: ./configure
|
||||||
|
- name: Configure & Build Cytoplasm
|
||||||
|
run: make cytoplasm
|
||||||
|
- name: Build Telodendria
|
||||||
|
run: make
|
44
.forgejo/workflows/release.yaml
Normal file
44
.forgejo/workflows/release.yaml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
name: Release Telodendria
|
||||||
|
run-name: Release Telodendria on ${{ forgejo.actor }}
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
"Release Telodendria":
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [alpine]
|
||||||
|
arch: [aarch64]
|
||||||
|
runs-on: ["${{ matrix.os }}", "${{ matrix.arch }}"]
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Archive submodules
|
||||||
|
run: git submodule foreach --recursive 'git archive --format tar --prefix=$displaypath/ -o submodule.tar HEAD'
|
||||||
|
- name: Archive repository
|
||||||
|
run: git archive --format tar -o release.tar HEAD
|
||||||
|
- name: Produce release archive
|
||||||
|
run: |
|
||||||
|
TOPDIR=$(pwd) git submodule --quiet foreach --recursive 'cd $TOPDIR; tar --concatenate --file=release.tar $displaypath/submodule.tar; rm -fv $displaypath/submodule.tar'
|
||||||
|
gzip release.tar
|
||||||
|
mkdir release
|
||||||
|
mv release.tar.gz release/Telodendria-$GITHUB_REF_NAME.tar.gz
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Telodendria-$GITHUB_REF_NAME.tar.gz
|
||||||
|
path: release/Telodendria-$GITHUB_REF_NAME.tar.gz
|
||||||
|
- name: Publish release
|
||||||
|
uses: actions/forgejo-release@v2
|
||||||
|
with:
|
||||||
|
tag: $GITHUB_REF_NAME
|
||||||
|
title: "Telodendria $GITHUB_REF_NAME"
|
||||||
|
release-dir: release/
|
||||||
|
release-notes: "docs/CHANGELOG.md"
|
||||||
|
direction: upload
|
||||||
|
prerelease: true
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "Cytoplasm"]
|
||||||
|
path = Cytoplasm
|
||||||
|
url = https://git.telodendria.io/Telodendria/Cytoplasm.git
|
1
Cytoplasm
Submodule
1
Cytoplasm
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 4f316ff7b3a955b831ca4aefb8679ddf3396a7d0
|
13
Schema/UserDirectoryRequest.json
Normal file
13
Schema/UserDirectoryRequest.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"header": "Schema\/UserDirectoryRequest.h",
|
||||||
|
"types": {
|
||||||
|
"UserDirectoryRequest": {
|
||||||
|
"fields": {
|
||||||
|
"search_term": { "type": "string" },
|
||||||
|
"limit": { "type": "integer" }
|
||||||
|
},
|
||||||
|
"type": "struct"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"guard": "TELODENDRIA_SCHEMA_USERDIRECTORYREQUEST_H"
|
||||||
|
}
|
123
configure
vendored
123
configure
vendored
|
@ -13,13 +13,40 @@ SRC="src"
|
||||||
INCLUDE="src/include"
|
INCLUDE="src/include"
|
||||||
TOOLS="tools/src"
|
TOOLS="tools/src"
|
||||||
SCHEMA="Schema"
|
SCHEMA="Schema"
|
||||||
|
CYTOPLASM="Cytoplasm"
|
||||||
|
|
||||||
CFLAGS="-Wall -Wextra -pedantic -std=c89 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE} -I${BUILD}"
|
CFLAGS="-O1 -D_DEFAULT_SOURCE -I${INCLUDE} -I${BUILD}"
|
||||||
LIBS="-lm -pthread -lCytoplasm"
|
LIBS="-lm -pthread -lCytoplasm"
|
||||||
|
|
||||||
|
|
||||||
# Set default args for all platforms
|
# Set default args for all platforms
|
||||||
SCRIPT_ARGS="--cc=cc --prefix=/usr/local --enable-ld-extra --bin-name=telodendria --version=1.7.0-alpha4 --static $@"
|
SCRIPT_ARGS="--prefix=/usr/local --bin-name=telodendria --version=1.7.0-alpha4"
|
||||||
|
|
||||||
|
if [ -f "${CYTOPLASM}/configure" ]; then
|
||||||
|
SCRIPT_ARGS="${SCRIPT_ARGS} --cytoplasm=${CYTOPLASM}"
|
||||||
|
else
|
||||||
|
SCRIPT_ARGS="${SCRIPT_ARGS} --cytoplasm=" # No cytoplasm path.
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set compiler depending on the platform.
|
||||||
|
case "$(uname)" in
|
||||||
|
Linux|NetBSD)
|
||||||
|
# These systems typically use GCC.
|
||||||
|
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=gcc"
|
||||||
|
;;
|
||||||
|
OpenBSD|FreeBSD|Darwin)
|
||||||
|
# These systems typically use Clang.
|
||||||
|
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=clang"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Use default compiler which is required to be present on
|
||||||
|
# all POSIX-compliant systems.
|
||||||
|
SCRIPT_ARGS="${SCRIPT_ARGS} --cc=c99"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Append any additional args specified by user
|
||||||
|
SCRIPT_ARGS="${SCRIPT_ARGS} $@"
|
||||||
|
|
||||||
echo "Processing options..."
|
echo "Processing options..."
|
||||||
echo "Ran with arguments: $SCRIPT_ARGS"
|
echo "Ran with arguments: $SCRIPT_ARGS"
|
||||||
|
@ -29,16 +56,18 @@ for arg in $SCRIPT_ARGS; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--cc=*)
|
--cc=*)
|
||||||
CC=$(echo "$arg" | cut -d '=' -f 2-)
|
CC=$(echo "$arg" | cut -d '=' -f 2-)
|
||||||
|
case "${CC}" in
|
||||||
|
gcc*|clang*)
|
||||||
|
# "Fancy" compilers that support a plethora of additional flags we
|
||||||
|
# want to enable if present.
|
||||||
|
CFLAGS="-Wall -Wextra -Werror -pedantic -std=c99 -O3 ${CFLAGS}"
|
||||||
|
LDFLAGS="${LDFLAGS} -flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
;;
|
;;
|
||||||
--prefix=*)
|
--prefix=*)
|
||||||
PREFIX=$(echo "$arg" | cut -d '=' -f 2-)
|
PREFIX=$(echo "$arg" | cut -d '=' -f 2-)
|
||||||
;;
|
;;
|
||||||
--enable-ld-extra)
|
|
||||||
LD_EXTRA="-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections"
|
|
||||||
;;
|
|
||||||
--disable-ld-extra)
|
|
||||||
LD_EXTRA=""
|
|
||||||
;;
|
|
||||||
--bin-name=*)
|
--bin-name=*)
|
||||||
BIN_NAME=$(echo "$arg" | cut -d '=' -f 2-)
|
BIN_NAME=$(echo "$arg" | cut -d '=' -f 2-)
|
||||||
;;
|
;;
|
||||||
|
@ -47,20 +76,22 @@ for arg in $SCRIPT_ARGS; do
|
||||||
;;
|
;;
|
||||||
--enable-debug)
|
--enable-debug)
|
||||||
DEBUG="-O0 -g"
|
DEBUG="-O0 -g"
|
||||||
echo "Notice: --enable-debug implies --disable-ld-extra and --no-static."
|
|
||||||
echo "You must explicitly provide --enable-ld-extra and/or --static after"
|
|
||||||
echo "specifying --enable-debug if you wish to enable these features in debug mode."
|
|
||||||
LD_EXTRA=""
|
|
||||||
STATIC=""
|
|
||||||
;;
|
;;
|
||||||
--disable-debug)
|
--disable-debug)
|
||||||
DEBUG=""
|
DEBUG=""
|
||||||
;;
|
;;
|
||||||
--static)
|
--cytoplasm=*)
|
||||||
STATIC="-static -Wl,-static"
|
CYTOPLASM=$(echo "$arg" | cut -d '=' -f 2-)
|
||||||
;;
|
if [ -n "${CYTOPLASM}" ]; then
|
||||||
--no-static)
|
if [ ! -f "${CYTOPLASM}/configure" ]; then
|
||||||
STATIC=""
|
echo "Path for Cytoplasm does not appear to actually contain Cytoplasm source:"
|
||||||
|
echo "${CYTOPLASM}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CFLAGS="${CFLAGS} -I${CYTOPLASM}/include"
|
||||||
|
LDFLAGS="-L${CYTOPLASM}/out/lib ${LDFLAGS}"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Invalid argument: $arg"
|
echo "Invalid argument: $arg"
|
||||||
|
@ -70,7 +101,7 @@ for arg in $SCRIPT_ARGS; do
|
||||||
done
|
done
|
||||||
|
|
||||||
CFLAGS="${CFLAGS} '-DTELODENDRIA_VERSION=\"${VERSION}\"' ${DEBUG}"
|
CFLAGS="${CFLAGS} '-DTELODENDRIA_VERSION=\"${VERSION}\"' ${DEBUG}"
|
||||||
LDFLAGS="${LIBS} ${LD_EXTRA}"
|
LDFLAGS="${LDFLAGS} ${LIBS}"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Makefile generation
|
# Makefile generation
|
||||||
|
@ -103,6 +134,16 @@ prefix() {
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cytoplasm_tool() {
|
||||||
|
tool="$1"
|
||||||
|
|
||||||
|
if [ -n "${CYTOPLASM}" ]; then
|
||||||
|
echo "LD_LIBRARY_PATH=${CYTOPLASM}/out/lib ${CYTOPLASM}/out/bin/$tool"
|
||||||
|
else
|
||||||
|
echo "$tool"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
print_src() {
|
print_src() {
|
||||||
printf '%s ' "$1"
|
printf '%s ' "$1"
|
||||||
}
|
}
|
||||||
|
@ -111,11 +152,27 @@ print_obj() {
|
||||||
printf '%s ' "$2"
|
printf '%s ' "$2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_deps() {
|
||||||
|
src="$1"
|
||||||
|
|
||||||
|
${CC} -I${INCLUDE} -I${BUILD} $(if [ -n "${CYTOPLASM}" ]; then echo "-I${CYTOPLASM}/include"; fi) -E "$src" \
|
||||||
|
| grep '^#' \
|
||||||
|
| awk '{print $3}' \
|
||||||
|
| cut -d '"' -f 2 \
|
||||||
|
| sort \
|
||||||
|
| uniq \
|
||||||
|
| grep -v '^[/<]' \
|
||||||
|
| grep -e "^${SRC}/" -e "^${BUILD}/" \
|
||||||
|
| while IFS= read -r dep; do
|
||||||
|
printf "%s " "$dep"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
compile_obj() {
|
compile_obj() {
|
||||||
src="$1"
|
src="$1"
|
||||||
obj="$2"
|
obj="$2"
|
||||||
|
|
||||||
pref=$(${CC} -I${INCLUDE} -I${BUILD} -MM -MT "${obj}" "${src}")
|
pref="${obj}: $(get_deps ${src})"
|
||||||
echo "$pref $(collect ${SCHEMA}/ .json .h ${BUILD}/Schema/ print_obj)"
|
echo "$pref $(collect ${SCHEMA}/ .json .h ${BUILD}/Schema/ print_obj)"
|
||||||
echo "${TAB}@mkdir -p $(dirname ${obj})"
|
echo "${TAB}@mkdir -p $(dirname ${obj})"
|
||||||
echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\""
|
echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\""
|
||||||
|
@ -129,7 +186,7 @@ compile_bin() {
|
||||||
|
|
||||||
echo "${out}: ${src}"
|
echo "${out}: ${src}"
|
||||||
echo "${TAB}@mkdir -p ${OUT}/bin"
|
echo "${TAB}@mkdir -p ${OUT}/bin"
|
||||||
echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" $depObjs \$(LDFLAGS) ${STATIC}"
|
echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" $depObjs \$(LDFLAGS)"
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_doc() {
|
compile_doc() {
|
||||||
|
@ -142,7 +199,7 @@ compile_doc() {
|
||||||
|
|
||||||
echo "${out}: ${src}"
|
echo "${out}: ${src}"
|
||||||
echo "${TAB}@mkdir -p ${OUT}/man/man3"
|
echo "${TAB}@mkdir -p ${OUT}/man/man3"
|
||||||
echo "${TAB}hdoc -D \"Os=${BIN_NAME}\" -i \"${src}\" -o \"${out}\""
|
echo "${TAB}$(cytoplasm_tool hdoc) -D \"Os=${BIN_NAME}\" -i \"${src}\" -o \"${out}\""
|
||||||
}
|
}
|
||||||
|
|
||||||
print_doc() {
|
print_doc() {
|
||||||
|
@ -160,11 +217,11 @@ compile_schema() {
|
||||||
|
|
||||||
echo "${BUILD}/Schema/${out}.h:"
|
echo "${BUILD}/Schema/${out}.h:"
|
||||||
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
||||||
echo "${TAB}j2s -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""
|
echo "${TAB}$(cytoplasm_tool j2s) -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""
|
||||||
|
|
||||||
echo "${BUILD}/Schema/${out}.c:"
|
echo "${BUILD}/Schema/${out}.c:"
|
||||||
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
||||||
echo "${TAB}j2s -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""
|
echo "${TAB}$(cytoplasm_tool j2s) -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""
|
||||||
|
|
||||||
echo "${obj}: ${src} ${BUILD}/Schema/${out}.c"
|
echo "${obj}: ${src} ${BUILD}/Schema/${out}.c"
|
||||||
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
echo "${TAB}@mkdir -p ${BUILD}/Schema"
|
||||||
|
@ -174,15 +231,19 @@ compile_schema() {
|
||||||
install_out() {
|
install_out() {
|
||||||
src="$1"
|
src="$1"
|
||||||
out="$2"
|
out="$2"
|
||||||
|
dir=$(dirname "$out")
|
||||||
|
|
||||||
echo "${TAB}install -D \"$src\" \"$out\""
|
echo "${TAB}mkdir -p \"$dir\""
|
||||||
|
echo "${TAB}cp \"$src\" \"$out\""
|
||||||
}
|
}
|
||||||
|
|
||||||
install_man() {
|
install_man() {
|
||||||
src="${OUT}/man/man3/${BIN_NAME}-$(basename $1 .h).3"
|
src="${OUT}/man/man3/${BIN_NAME}-$(basename $1 .h).3"
|
||||||
out="$2"
|
out="$2"
|
||||||
|
dir=$(dirname "$out")
|
||||||
|
|
||||||
echo "${TAB}install -D \"$src\" \"$out\""
|
echo "${TAB}mkdir -p \"$dir\""
|
||||||
|
echo "${TAB}cp \"$src\" \"$out\""
|
||||||
}
|
}
|
||||||
|
|
||||||
uninstall_out() {
|
uninstall_out() {
|
||||||
|
@ -230,7 +291,8 @@ ${TAB}done
|
||||||
${BIN_NAME}: ${OUT}/bin/${BIN_NAME}
|
${BIN_NAME}: ${OUT}/bin/${BIN_NAME}
|
||||||
|
|
||||||
install: ${BIN_NAME}
|
install: ${BIN_NAME}
|
||||||
${TAB}install -D ${OUT}/bin/${BIN_NAME} \$(PREFIX)/bin/${BIN_NAME}
|
${TAB}mkdir -p \$(PREFIX)/bin
|
||||||
|
${TAB}cp ${OUT}/bin/${BIN_NAME} \$(PREFIX)/bin/${BIN_NAME}
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
${TAB}rm \$(PREFIX)/bin/${BIN_NAME}
|
${TAB}rm \$(PREFIX)/bin/${BIN_NAME}
|
||||||
|
@ -247,6 +309,13 @@ $(collect ${SRC}/ .c .o ${BUILD}/ compile_obj)
|
||||||
$(collect ${TOOLS}/ .c '' ${OUT}/bin/ compile_bin)
|
$(collect ${TOOLS}/ .c '' ${OUT}/bin/ compile_bin)
|
||||||
$(collect ${INCLUDE}/ .h .3 ${OUT}/man/man3/${BIN_NAME}- compile_doc)
|
$(collect ${INCLUDE}/ .h .3 ${OUT}/man/man3/${BIN_NAME}- compile_doc)
|
||||||
|
|
||||||
|
$(
|
||||||
|
if [ -n "${CYTOPLASM}" ]; then
|
||||||
|
echo "cytoplasm:"
|
||||||
|
echo "${TAB}cd ${CYTOPLASM} && ./configure && \$(MAKE)"
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "Done. Run 'make' to build ${BIN_NAME}."
|
echo "Done. Run 'make' to build ${BIN_NAME}."
|
||||||
|
|
94
docs/dev/hosting.md
Normal file
94
docs/dev/hosting.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
# Hosting Telodendria
|
||||||
|
|
||||||
|
These are just my own personal notes for hosting Telodendria's code infrastructure. This document is not intended to be used by normal Telodendria users or developers. It may be useful if you are *forking* Telodendria, but I sincerely hope you'll contribute to the upstream project instead. I'm writing this document solely for my own reference, but I am placing it into Telodendria's code repository in the name of transparency.
|
||||||
|
|
||||||
|
## Runners
|
||||||
|
|
||||||
|
The general sequence of steps required for setting up a CI runner is as follows:
|
||||||
|
|
||||||
|
1. Install the runner OS with all the defaults. I typically install my runners in virtual machines with 1 vcpu and 512mb RAM. Only Debian complained about this configuration, but since I didn't install a desktop environment, it worked out fine.
|
||||||
|
2. Install the packages required to build and execute the runner. These are:
|
||||||
|
- Git for checking out the source code.
|
||||||
|
- NodeJS for running `actions/checkout`, I think. Not really sure, all I know is that the runner will fail all jobs without NodeJS.
|
||||||
|
- Go for compiling the runner itself.
|
||||||
|
|
||||||
|
Run these commands to install the packages:
|
||||||
|
- **OpenBSD:** `pkg_add git go node`
|
||||||
|
- **FreeBSD:** `pkg install git go node`
|
||||||
|
- **NetBSD:** `pkgin install git go nodejs openssl mozilla-rootcerts-openssl`
|
||||||
|
(Note that the `go` executable is `go121` or whatever version was installed. and that NetBSD has no root certificates installed by default)
|
||||||
|
- **Debian:** `apt install git golang nodejs`
|
||||||
|
- **Alpine:** `apk add git go nodejs`
|
||||||
|
|
||||||
|
3. Install any development packages required to build Telodendria. For the BSDs, all development tools are built in so no additional packages are necessary. For the Linux distributions I've messed with, install these additional packages:
|
||||||
|
- **Debian:** `apt install make gcc libssl-dev`
|
||||||
|
- **Alpine:** `apk add make gcc musl-dev openssl-dev`
|
||||||
|
4. Clone `https://git.telodendria.io/Telodendria/act_runner.git`.
|
||||||
|
5. Run `go build` in the `act_runner` directory. On NetBSD, you may have to `umount /tmp` first because `/tmp` is by default very small. Otherwise, make `/tmp` larger during installation. 2GB should be plenty.
|
||||||
|
6. Run `./act_runner register` to register the runner. When prompted for the tags, follow following convention:
|
||||||
|
- **Linux Distros:** `linux`, `<distro>-v<version>`, `<arch>`
|
||||||
|
- **BSD Derivatives:** `bsd`, `<osname>-v<version>`, `<arch>`
|
||||||
|
- **Windows:** `windows`, `windows-v<version>`, `<arch>`
|
||||||
|
- **MacOS:** `macos`, `macos-v<version>`, `<arch>`
|
||||||
|
- **Others:** `other`, `<osname>-v<version>`, `<arch>`
|
||||||
|
|
||||||
|
Where `<arch>` is one of `x86` or `x64` for now. ARM runners will be a future project.
|
||||||
|
7. Run `./act_runner daemon`.
|
||||||
|
|
||||||
|
### Startup Scripts
|
||||||
|
|
||||||
|
We will obviously want `act_runner` to execute on bootup. Here are the start scripts I used:
|
||||||
|
|
||||||
|
#### Alpine
|
||||||
|
|
||||||
|
In `/etc/init.d/act_runner`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
directory="/home/runner/act_runner"
|
||||||
|
command="/home/runner/act_runner/act_runner"
|
||||||
|
command_args="daemon"
|
||||||
|
command_user="runner:runner"
|
||||||
|
command_background="true"
|
||||||
|
pidfile="/run/act_runner.pid"
|
||||||
|
```
|
||||||
|
|
||||||
|
Don't forget to `chmod +x /etc/init.d/act_runner`.
|
||||||
|
|
||||||
|
Then just `rc-update add act_runner` and `rc-service act_runner start`.
|
||||||
|
|
||||||
|
#### Debian
|
||||||
|
|
||||||
|
In `/etc/systemd/system/act_runner.service`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=Gitea Actions runner
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/home/runner/act_runner/act_runner daemon
|
||||||
|
ExecReload=/bin/kill -s HUP $MAINPID
|
||||||
|
WorkingDirectory=/home/runner/act_runner
|
||||||
|
TimeoutSec=0
|
||||||
|
RestartSec=10
|
||||||
|
Restart=always
|
||||||
|
User=runner
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Then just `systemctl enable act_runner` and `systemctl start act_runner`.
|
||||||
|
|
||||||
|
#### Other
|
||||||
|
|
||||||
|
Eventually I got sick of writing init scripts for all the various operating systems.
|
||||||
|
|
||||||
|
Just put this in `runner`'s `crontab`:
|
||||||
|
|
||||||
|
```
|
||||||
|
@reboot cd /home/runner/act_runner && ./act_runner daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
That seems to do the job good enough, and it's cross platform.
|
21
src/Config.c
21
src/Config.c
|
@ -30,7 +30,6 @@
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
#include <Cytoplasm/Int64.h>
|
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -59,7 +58,7 @@ ConfigParse(HashMap * config, Config *tConfig)
|
||||||
|
|
||||||
memset(tConfig, 0, sizeof(Config));
|
memset(tConfig, 0, sizeof(Config));
|
||||||
|
|
||||||
tConfig->maxCache = Int64Create(0, 0);
|
tConfig->maxCache = 0;
|
||||||
|
|
||||||
if (!ConfigFromJson(config, tConfig, &tConfig->err))
|
if (!ConfigFromJson(config, tConfig, &tConfig->err))
|
||||||
{
|
{
|
||||||
|
@ -85,17 +84,17 @@ ConfigParse(HashMap * config, Config *tConfig)
|
||||||
for (i = 0; i < ArraySize(tConfig->listen); i++)
|
for (i = 0; i < ArraySize(tConfig->listen); i++)
|
||||||
{
|
{
|
||||||
ConfigListener *listener = ArrayGet(tConfig->listen, i);
|
ConfigListener *listener = ArrayGet(tConfig->listen, i);
|
||||||
if (Int64Eq(listener->maxConnections, Int64Create(0, 0)))
|
if (!listener->maxConnections)
|
||||||
{
|
{
|
||||||
listener->maxConnections = Int64Create(0, 32);
|
listener->maxConnections = 32;
|
||||||
}
|
}
|
||||||
if (Int64Eq(listener->threads, Int64Create(0, 0)))
|
if (!listener->threads)
|
||||||
{
|
{
|
||||||
listener->threads = Int64Create(0, 4);
|
listener->threads = 4;
|
||||||
}
|
}
|
||||||
if (Int64Eq(listener->port, Int64Create(0, 0)))
|
if (!listener->port)
|
||||||
{
|
{
|
||||||
listener->port = Int64Create(0, 8008);
|
listener->port = 8008;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tConfig->ok = 1;
|
tConfig->ok = 1;
|
||||||
|
@ -148,9 +147,9 @@ ConfigCreateDefault(Db * db)
|
||||||
/* Add simple listener without TLS. */
|
/* Add simple listener without TLS. */
|
||||||
config.listen = ArrayCreate();
|
config.listen = ArrayCreate();
|
||||||
listener = Malloc(sizeof(ConfigListener));
|
listener = Malloc(sizeof(ConfigListener));
|
||||||
listener->maxConnections = Int64Create(0, 32);
|
listener->maxConnections = 32;
|
||||||
listener->port = Int64Create(0, 8008);
|
listener->port = 8008;
|
||||||
listener->threads = Int64Create(0, 4);
|
listener->threads = 4;
|
||||||
|
|
||||||
ArrayAdd(config.listen, listener);
|
ArrayAdd(config.listen, listener);
|
||||||
|
|
||||||
|
|
11
src/Main.c
11
src/Main.c
|
@ -36,14 +36,12 @@
|
||||||
|
|
||||||
#include <Cytoplasm/Args.h>
|
#include <Cytoplasm/Args.h>
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Config.h>
|
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
#include <Cytoplasm/HashMap.h>
|
#include <Cytoplasm/HashMap.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/HttpServer.h>
|
#include <Cytoplasm/HttpServer.h>
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
#include <Cytoplasm/Cron.h>
|
#include <Cytoplasm/Cron.h>
|
||||||
#include <Uia.h>
|
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
@ -52,6 +50,9 @@
|
||||||
#include <User.h>
|
#include <User.h>
|
||||||
#include <RegToken.h>
|
#include <RegToken.h>
|
||||||
#include <Routes.h>
|
#include <Routes.h>
|
||||||
|
#include <Uia.h>
|
||||||
|
#include <Config.h>
|
||||||
|
|
||||||
|
|
||||||
static Array *httpServers;
|
static Array *httpServers;
|
||||||
static volatile int restart;
|
static volatile int restart;
|
||||||
|
@ -248,7 +249,7 @@ start:
|
||||||
}
|
}
|
||||||
|
|
||||||
token = StrRandom(32);
|
token = StrRandom(32);
|
||||||
info = RegTokenCreate(matrixArgs.db, token, NULL, UInt64Create(0, 0), Int64Create(0, 1), USER_ALL);
|
info = RegTokenCreate(matrixArgs.db, token, NULL, /* expires */ 0, /* uses */ 1, USER_ALL);
|
||||||
if (!info)
|
if (!info)
|
||||||
{
|
{
|
||||||
Free(token);
|
Free(token);
|
||||||
|
@ -396,14 +397,14 @@ start:
|
||||||
|
|
||||||
if (args.flags & HTTP_FLAG_TLS)
|
if (args.flags & HTTP_FLAG_TLS)
|
||||||
{
|
{
|
||||||
if (UInt64Eq(UtilLastModified(serverCfg->tls.cert), UInt64Create(0, 0)))
|
if (!UtilLastModified(serverCfg->tls.cert))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.cert);
|
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.cert);
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UInt64Eq(UtilLastModified(serverCfg->tls.key), UInt64Create(0, 0)))
|
if (UtilLastModified(serverCfg->tls.key))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.key);
|
Log(LOG_ERR, "%s: %s", strerror(errno), serverCfg->tls.key);
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
|
|
109
src/Parser.c
109
src/Parser.c
|
@ -27,7 +27,6 @@
|
||||||
|
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Int.h>
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -38,7 +37,7 @@
|
||||||
#define Iterate(s) (*(*s)++)
|
#define Iterate(s) (*(*s)++)
|
||||||
|
|
||||||
/* Parse an extended localpart */
|
/* Parse an extended localpart */
|
||||||
static int
|
static bool
|
||||||
ParseUserLocalpart(char **str, char **out)
|
ParseUserLocalpart(char **str, char **out)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
|
@ -47,7 +46,7 @@ ParseUserLocalpart(char **str, char **out)
|
||||||
|
|
||||||
if (!str || !out)
|
if (!str || !out)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
/* An extended localpart contains every ASCII printable character,
|
/* An extended localpart contains every ASCII printable character,
|
||||||
* except an ':'. */
|
* except an ':'. */
|
||||||
|
@ -60,7 +59,7 @@ ParseUserLocalpart(char **str, char **out)
|
||||||
if (length < 1)
|
if (length < 1)
|
||||||
{
|
{
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
if (c == ':')
|
if (c == ':')
|
||||||
{
|
{
|
||||||
|
@ -71,8 +70,9 @@ ParseUserLocalpart(char **str, char **out)
|
||||||
memcpy(*out, start, length);
|
memcpy(*out, start, length);
|
||||||
(*out)[length] = '\0';
|
(*out)[length] = '\0';
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parses an IPv4 address. */
|
/* Parses an IPv4 address. */
|
||||||
static int
|
static int
|
||||||
ParseIPv4(char **str, char **out)
|
ParseIPv4(char **str, char **out)
|
||||||
|
@ -86,7 +86,7 @@ ParseIPv4(char **str, char **out)
|
||||||
int digit = 0;
|
int digit = 0;
|
||||||
int digits = 0;
|
int digits = 0;
|
||||||
|
|
||||||
memset(buffer, '\0', 4);
|
memset(buffer, 0, sizeof(buffer));
|
||||||
start = *str;
|
start = *str;
|
||||||
|
|
||||||
/* An IPv4 address is made of 4 blocks between 1-3 digits, like so:
|
/* An IPv4 address is made of 4 blocks between 1-3 digits, like so:
|
||||||
|
@ -102,36 +102,38 @@ ParseIPv4(char **str, char **out)
|
||||||
{
|
{
|
||||||
/* Current digit is too long for the spec! */
|
/* Current digit is too long for the spec! */
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(buffer, *str - digit - 1, digit);
|
memcpy(buffer, *str - digit - 1, digit);
|
||||||
if (atoi(buffer) > 255)
|
if (atoi(buffer) > 255)
|
||||||
{
|
{
|
||||||
/* Current digit is too large for the spec! */
|
/* Current digit is too large for the spec! */
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
memset(buffer, '\0', 4);
|
memset(buffer, 0, sizeof(buffer));
|
||||||
digit = 0;
|
digit = 0;
|
||||||
digits++; /* We have parsed a digit. */
|
digits++; /* We have parsed a digit. */
|
||||||
}
|
}
|
||||||
if (c == '.' || digits != 3)
|
if (c == '.' || digits != 3)
|
||||||
{
|
{
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
length = (size_t) (*str - start) - 1;
|
length = (size_t) (*str - start) - 1;
|
||||||
*out = Malloc(length + 1);
|
*out = Malloc(length + 1);
|
||||||
memcpy(*out, start, length);
|
memcpy(*out, start, length);
|
||||||
(*str)--;
|
(*str)--;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
static int
|
|
||||||
|
static bool
|
||||||
IsIPv6Char(char c)
|
IsIPv6Char(char c)
|
||||||
{
|
{
|
||||||
return isxdigit(c) || c == ':' || c == '.';
|
return (isxdigit(c) || c == ':' || c == '.');
|
||||||
}
|
}
|
||||||
static int
|
|
||||||
|
static bool
|
||||||
ParseIPv6(char **str, char **out)
|
ParseIPv6(char **str, char **out)
|
||||||
{
|
{
|
||||||
char *start;
|
char *start;
|
||||||
|
@ -150,7 +152,7 @@ ParseIPv6(char **str, char **out)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((c = Iterate(str)) && IsIPv6Char(c))
|
while ((c = Iterate(str)) && IsIPv6Char(c) && digits < 8)
|
||||||
{
|
{
|
||||||
char *ipv4;
|
char *ipv4;
|
||||||
if (isxdigit(c))
|
if (isxdigit(c))
|
||||||
|
@ -174,7 +176,7 @@ ParseIPv6(char **str, char **out)
|
||||||
/* RFC3513 says the following:
|
/* RFC3513 says the following:
|
||||||
* > 'The "::" can only appear once in an address.' */
|
* > 'The "::" can only appear once in an address.' */
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
if (digit < 1 || digit > 4)
|
if (digit < 1 || digit > 4)
|
||||||
{
|
{
|
||||||
|
@ -217,12 +219,13 @@ end:
|
||||||
memset(*out, '\0', length + 1);
|
memset(*out, '\0', length + 1);
|
||||||
memcpy(*out, start, length);
|
memcpy(*out, start, length);
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
fail:
|
fail:
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
static int
|
|
||||||
|
static bool
|
||||||
ParseHostname(char **str, char **out)
|
ParseHostname(char **str, char **out)
|
||||||
{
|
{
|
||||||
char *start;
|
char *start;
|
||||||
|
@ -239,16 +242,16 @@ ParseHostname(char **str, char **out)
|
||||||
if (length < 1 || length > 255)
|
if (length < 1 || length > 255)
|
||||||
{
|
{
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
length = (size_t) (*str - start) - 1;
|
length = (size_t) (*str - start) - 1;
|
||||||
*out = Malloc(length + 1);
|
*out = Malloc(length + 1);
|
||||||
memcpy(*out, start, length);
|
memcpy(*out, start, length);
|
||||||
(*str)--;
|
(*str)--;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static bool
|
||||||
ParseServerName(char **str, ServerPart *out)
|
ParseServerName(char **str, ServerPart *out)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
|
@ -261,7 +264,7 @@ ParseServerName(char **str, ServerPart *out)
|
||||||
|
|
||||||
if (!str || !out)
|
if (!str || !out)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
start = *str;
|
start = *str;
|
||||||
|
@ -284,7 +287,7 @@ ParseServerName(char **str, ServerPart *out)
|
||||||
if (!host)
|
if (!host)
|
||||||
{
|
{
|
||||||
/* Can't parse a valid server name. */
|
/* Can't parse a valid server name. */
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
/* Now, there's only 2 options: a ':', or the end(everything else.) */
|
/* Now, there's only 2 options: a ':', or the end(everything else.) */
|
||||||
if (**str != ':')
|
if (**str != ':')
|
||||||
|
@ -292,7 +295,7 @@ ParseServerName(char **str, ServerPart *out)
|
||||||
/* We're done. */
|
/* We're done. */
|
||||||
out->hostname = host;
|
out->hostname = host;
|
||||||
out->port = NULL;
|
out->port = NULL;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
/* TODO: Separate this out */
|
/* TODO: Separate this out */
|
||||||
startPort = ++(*str);
|
startPort = ++(*str);
|
||||||
|
@ -305,7 +308,7 @@ ParseServerName(char **str, ServerPart *out)
|
||||||
*str = start;
|
*str = start;
|
||||||
Free(host);
|
Free(host);
|
||||||
host = NULL;
|
host = NULL;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
port = Malloc(chars + 1);
|
port = Malloc(chars + 1);
|
||||||
|
@ -316,24 +319,26 @@ ParseServerName(char **str, ServerPart *out)
|
||||||
Free(port);
|
Free(port);
|
||||||
Free(host);
|
Free(host);
|
||||||
*str = start;
|
*str = start;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out->hostname = host;
|
out->hostname = host;
|
||||||
out->port = port;
|
out->port = port;
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
int
|
|
||||||
|
bool
|
||||||
ParseServerPart(char *str, ServerPart *part)
|
ParseServerPart(char *str, ServerPart *part)
|
||||||
{
|
{
|
||||||
/* This is a wrapper behind the internal ParseServerName. */
|
/* This is a wrapper behind the internal ParseServerName. */
|
||||||
if (!str || !part)
|
if (!str || !part)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
return ParseServerName(&str, part);
|
return ParseServerName(&str, part);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ServerPartFree(ServerPart part)
|
ServerPartFree(ServerPart part)
|
||||||
{
|
{
|
||||||
|
@ -347,20 +352,20 @@ ServerPartFree(ServerPart part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
ParseCommonID(char *str, CommonID *id)
|
ParseCommonID(char *str, CommonID *id)
|
||||||
{
|
{
|
||||||
char sigil;
|
char sigil;
|
||||||
|
|
||||||
if (!str || !id)
|
if (!str || !id)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There must at least be 2 chararacters: the sigil and a string.*/
|
/* There must at least be 2 chararacters: the sigil and a string.*/
|
||||||
if (strlen(str) < 2)
|
if (strlen(str) < 2)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sigil = *str++;
|
sigil = *str++;
|
||||||
|
@ -369,7 +374,7 @@ ParseCommonID(char *str, CommonID *id)
|
||||||
*/
|
*/
|
||||||
if ((sigil == '#' || sigil == '@') && strlen(str) > 255)
|
if ((sigil == '#' || sigil == '@') && strlen(str) > 255)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
id->sigil = sigil;
|
id->sigil = sigil;
|
||||||
id->local = NULL;
|
id->local = NULL;
|
||||||
|
@ -383,7 +388,7 @@ ParseCommonID(char *str, CommonID *id)
|
||||||
* accepting it all. */
|
* accepting it all. */
|
||||||
if (!ParseUserLocalpart(&str, &id->local))
|
if (!ParseUserLocalpart(&str, &id->local))
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
if (*str == ':')
|
if (*str == ':')
|
||||||
{
|
{
|
||||||
|
@ -392,9 +397,9 @@ ParseCommonID(char *str, CommonID *id)
|
||||||
{
|
{
|
||||||
Free(id->local);
|
Free(id->local);
|
||||||
id->local = NULL;
|
id->local = NULL;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '!':
|
case '!':
|
||||||
|
@ -403,23 +408,23 @@ ParseCommonID(char *str, CommonID *id)
|
||||||
case '@':
|
case '@':
|
||||||
if (!ParseUserLocalpart(&str, &id->local))
|
if (!ParseUserLocalpart(&str, &id->local))
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
if (*str++ != ':')
|
if (*str++ != ':')
|
||||||
{
|
{
|
||||||
Free(id->local);
|
Free(id->local);
|
||||||
id->local = NULL;
|
id->local = NULL;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ParseServerName(&str, &id->server))
|
if (!ParseServerName(&str, &id->server))
|
||||||
{
|
{
|
||||||
Free(id->local);
|
Free(id->local);
|
||||||
id->local = NULL;
|
id->local = NULL;
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -431,15 +436,18 @@ CommonIDFree(CommonID id)
|
||||||
}
|
}
|
||||||
ServerPartFree(id.server);
|
ServerPartFree(id.server);
|
||||||
}
|
}
|
||||||
int
|
|
||||||
|
bool
|
||||||
ValidCommonID(char *str, char sigil)
|
ValidCommonID(char *str, char sigil)
|
||||||
{
|
{
|
||||||
CommonID id;
|
CommonID id;
|
||||||
int ret;
|
bool ret;
|
||||||
|
|
||||||
memset(&id, 0, sizeof(CommonID));
|
memset(&id, 0, sizeof(CommonID));
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ParseCommonID(str, &id) && id.sigil == sigil;
|
ret = ParseCommonID(str, &id) && id.sigil == sigil;
|
||||||
|
@ -447,6 +455,7 @@ ValidCommonID(char *str, char sigil)
|
||||||
CommonIDFree(id);
|
CommonIDFree(id);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
ParserRecomposeServerPart(ServerPart serverPart)
|
ParserRecomposeServerPart(ServerPart serverPart)
|
||||||
{
|
{
|
||||||
|
@ -460,10 +469,11 @@ ParserRecomposeServerPart(ServerPart serverPart)
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
ParserRecomposeCommonID(CommonID id)
|
ParserRecomposeCommonID(CommonID id)
|
||||||
{
|
{
|
||||||
char *ret = Malloc(2);
|
char *ret = Malloc(2 * sizeof(char));
|
||||||
ret[0] = id.sigil;
|
ret[0] = id.sigil;
|
||||||
ret[1] = '\0';
|
ret[1] = '\0';
|
||||||
|
|
||||||
|
@ -485,15 +495,18 @@ ParserRecomposeCommonID(CommonID id)
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
int
|
|
||||||
|
bool
|
||||||
ParserServerNameEquals(ServerPart serverPart, char *str)
|
ParserServerNameEquals(ServerPart serverPart, char *str)
|
||||||
{
|
{
|
||||||
char *idServer;
|
char *idServer;
|
||||||
int ret;
|
bool ret;
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
idServer = ParserRecomposeServerPart(serverPart);
|
idServer = ParserRecomposeServerPart(serverPart);
|
||||||
|
|
||||||
ret = StrEquals(idServer, str);
|
ret = StrEquals(idServer, str);
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Int64.h>
|
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
#include <User.h>
|
#include <User.h>
|
||||||
|
@ -40,9 +39,9 @@ int
|
||||||
RegTokenValid(RegTokenInfo * token)
|
RegTokenValid(RegTokenInfo * token)
|
||||||
{
|
{
|
||||||
HashMap *tokenJson;
|
HashMap *tokenJson;
|
||||||
Int64 uses, used;
|
int64_t uses, used;
|
||||||
|
|
||||||
UInt64 expiration;
|
uint64_t expiration;
|
||||||
|
|
||||||
if (!token || !RegTokenExists(token->db, token->name))
|
if (!token || !RegTokenExists(token->db, token->name))
|
||||||
{
|
{
|
||||||
|
@ -54,9 +53,7 @@ RegTokenValid(RegTokenInfo * token)
|
||||||
used = JsonValueAsInteger(HashMapGet(tokenJson, "used"));
|
used = JsonValueAsInteger(HashMapGet(tokenJson, "used"));
|
||||||
expiration = JsonValueAsInteger(HashMapGet(tokenJson, "expires_on"));
|
expiration = JsonValueAsInteger(HashMapGet(tokenJson, "expires_on"));
|
||||||
|
|
||||||
return (UInt64Eq(expiration, UInt64Create(0, 0)) ||
|
return (!expiration || (UtilTsMillis() < expiration)) && (uses == -1 || used < uses);
|
||||||
UInt64Geq(UtilServerTs(), expiration)) &&
|
|
||||||
(Int64Eq(uses, Int64Neg(Int64Create(0, 1))) || Int64Lt(used, uses));
|
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
RegTokenUse(RegTokenInfo * token)
|
RegTokenUse(RegTokenInfo * token)
|
||||||
|
@ -68,13 +65,12 @@ RegTokenUse(RegTokenInfo * token)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Int64Geq(token->uses, Int64Create(0, 0)) &&
|
if (token->uses >= 0 && token->used >= token->uses)
|
||||||
Int64Geq(token->used, token->uses))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
token->used = Int64Add(token->used, Int64Create(0, 1));
|
token->used++;
|
||||||
|
|
||||||
/* Write the information to the hashmap */
|
/* Write the information to the hashmap */
|
||||||
tokenJson = DbJson(token->ref);
|
tokenJson = DbJson(token->ref);
|
||||||
|
@ -199,11 +195,11 @@ RegTokenVerify(char *token)
|
||||||
}
|
}
|
||||||
|
|
||||||
RegTokenInfo *
|
RegTokenInfo *
|
||||||
RegTokenCreate(Db * db, char *name, char *owner, UInt64 expires, Int64 uses, int privileges)
|
RegTokenCreate(Db * db, char *name, char *owner, uint64_t expires, int64_t uses, int privileges)
|
||||||
{
|
{
|
||||||
RegTokenInfo *ret;
|
RegTokenInfo *ret;
|
||||||
|
|
||||||
UInt64 timestamp = UtilServerTs();
|
uint64_t timestamp = UtilTsMillis();
|
||||||
|
|
||||||
if (!db || !name)
|
if (!db || !name)
|
||||||
{
|
{
|
||||||
|
@ -213,13 +209,13 @@ RegTokenCreate(Db * db, char *name, char *owner, UInt64 expires, Int64 uses, int
|
||||||
/* -1 indicates infinite uses; zero and all positive values are a
|
/* -1 indicates infinite uses; zero and all positive values are a
|
||||||
* valid number of uses; althought zero would be rather useless.
|
* valid number of uses; althought zero would be rather useless.
|
||||||
* Anything less than -1 doesn't make sense. */
|
* Anything less than -1 doesn't make sense. */
|
||||||
if (Int64Lt(uses, Int64Neg(Int64Create(0, 1))))
|
if (uses < -1)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verify the token */
|
/* Verify the token */
|
||||||
if (!RegTokenVerify(name) || (UInt64Gt(expires, UInt64Create(0, 0)) && UInt64Lt(expires, timestamp)))
|
if (!RegTokenVerify(name) || ((expires > 0) && (expires < timestamp)))
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -235,7 +231,7 @@ RegTokenCreate(Db * db, char *name, char *owner, UInt64 expires, Int64 uses, int
|
||||||
}
|
}
|
||||||
ret->name = StrDuplicate(name);
|
ret->name = StrDuplicate(name);
|
||||||
ret->created_by = StrDuplicate(owner);
|
ret->created_by = StrDuplicate(owner);
|
||||||
ret->used = Int64Create(0, 0);
|
ret->used = 0;
|
||||||
ret->uses = uses;
|
ret->uses = uses;
|
||||||
ret->created_on = timestamp;
|
ret->created_on = timestamp;
|
||||||
ret->expires_on = expires;
|
ret->expires_on = expires;
|
||||||
|
|
|
@ -72,6 +72,7 @@ RouterBuild(void)
|
||||||
|
|
||||||
R("/_matrix/client/v3/profile/(.*)", RouteUserProfile);
|
R("/_matrix/client/v3/profile/(.*)", RouteUserProfile);
|
||||||
R("/_matrix/client/v3/profile/(.*)/(avatar_url|displayname)", RouteUserProfile);
|
R("/_matrix/client/v3/profile/(.*)/(avatar_url|displayname)", RouteUserProfile);
|
||||||
|
R("/_matrix/client/v3/user_directory/search", RouteUserDirectory);
|
||||||
|
|
||||||
R("/_matrix/client/v3/user/(.*)/filter", RouteFilter);
|
R("/_matrix/client/v3/user/(.*)/filter", RouteFilter);
|
||||||
R("/_matrix/client/v3/user/(.*)/filter/(.*)", RouteFilter);
|
R("/_matrix/client/v3/user/(.*)/filter/(.*)", RouteFilter);
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
*/
|
*/
|
||||||
#include <Routes.h>
|
#include <Routes.h>
|
||||||
|
|
||||||
#include <Cytoplasm/Int64.h>
|
|
||||||
#include <User.h>
|
#include <User.h>
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
@ -86,24 +85,11 @@ ROUTE_IMPL(RouteProcControl, path, argp)
|
||||||
if (StrEquals(op, "stats"))
|
if (StrEquals(op, "stats"))
|
||||||
{
|
{
|
||||||
size_t allocated = MemoryAllocated();
|
size_t allocated = MemoryAllocated();
|
||||||
Int64 a;
|
|
||||||
|
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
|
|
||||||
if (sizeof(size_t) == sizeof(Int64))
|
|
||||||
{
|
|
||||||
UInt32 high = (UInt32) (allocated >> 32);
|
|
||||||
UInt32 low = (UInt32) (allocated);
|
|
||||||
|
|
||||||
a = Int64Create(high, low);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
a = Int64Create(0, allocated);
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMapSet(response, "version", JsonValueString(TELODENDRIA_VERSION));
|
HashMapSet(response, "version", JsonValueString(TELODENDRIA_VERSION));
|
||||||
HashMapSet(response, "memory_allocated", JsonValueInteger(a));
|
HashMapSet(response, "memory_allocated", JsonValueInteger(allocated));
|
||||||
|
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,6 @@ ROUTE_IMPL(RouteRequestToken, path, argp)
|
||||||
|
|
||||||
RequestToken reqTok;
|
RequestToken reqTok;
|
||||||
|
|
||||||
Int64 minusOne = Int64Neg(Int64Create(0, 1));
|
|
||||||
|
|
||||||
reqTok.client_secret = NULL;
|
reqTok.client_secret = NULL;
|
||||||
reqTok.next_link = NULL;
|
reqTok.next_link = NULL;
|
||||||
reqTok.id_access_token = NULL;
|
reqTok.id_access_token = NULL;
|
||||||
|
@ -51,7 +49,7 @@ ROUTE_IMPL(RouteRequestToken, path, argp)
|
||||||
reqTok.country = NULL;
|
reqTok.country = NULL;
|
||||||
reqTok.phone_number = NULL;
|
reqTok.phone_number = NULL;
|
||||||
|
|
||||||
reqTok.send_attempt = minusOne;
|
reqTok.send_attempt = -1;
|
||||||
|
|
||||||
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
||||||
{
|
{
|
||||||
|
@ -90,7 +88,7 @@ ROUTE_IMPL(RouteRequestToken, path, argp)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Int64Eq(reqTok.send_attempt, minusOne))
|
if (reqTok.send_attempt == -1)
|
||||||
{
|
{
|
||||||
msg = "Invalid or inexistent 'send_attempt'";
|
msg = "Invalid or inexistent 'send_attempt'";
|
||||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
|
199
src/Routes/RouteUserDirectory.c
Normal file
199
src/Routes/RouteUserDirectory.c
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* 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 dirRequest.limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <Routes.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Array.h>
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Db.h>
|
||||||
|
|
||||||
|
#include <Schema/UserDirectoryRequest.h>
|
||||||
|
|
||||||
|
#include <User.h>
|
||||||
|
|
||||||
|
ROUTE_IMPL(RouteUserDirectory, path, argp)
|
||||||
|
{
|
||||||
|
RouteArgs *args = argp;
|
||||||
|
HashMap *response = NULL;
|
||||||
|
HashMap *request = NULL;
|
||||||
|
|
||||||
|
Array *users = NULL;
|
||||||
|
Array *results = NULL;
|
||||||
|
|
||||||
|
Db *db = args->matrixArgs->db;
|
||||||
|
|
||||||
|
Config config = { .ok = 0 };
|
||||||
|
|
||||||
|
User *user = NULL;
|
||||||
|
|
||||||
|
char *token = NULL;
|
||||||
|
char *requesterName = NULL;
|
||||||
|
char *msg = NULL;
|
||||||
|
|
||||||
|
UserDirectoryRequest dirRequest;
|
||||||
|
|
||||||
|
size_t i, included;
|
||||||
|
|
||||||
|
(void) path;
|
||||||
|
|
||||||
|
dirRequest.search_term = NULL;
|
||||||
|
dirRequest.limit = 10;
|
||||||
|
|
||||||
|
|
||||||
|
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
||||||
|
{
|
||||||
|
msg = "Request supports only POST.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_UNRECOGNIZED, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
request = JsonDecode(HttpServerStream(args->context));
|
||||||
|
if (!request)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_NOT_JSON, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!UserDirectoryRequestFromJson(request, &dirRequest, &msg))
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_BAD_JSON, msg);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (!dirRequest.search_term)
|
||||||
|
{
|
||||||
|
msg = "Field 'search_term' not set.";
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_BAD_JSON, msg);
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
response = MatrixGetAccessToken(args->context, &token);
|
||||||
|
if (response)
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Actually use information related to the user. */
|
||||||
|
user = UserAuthenticate(db, token);
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
response = MatrixErrorCreate(M_UNKNOWN_TOKEN, NULL);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
requesterName = UserGetName(user);
|
||||||
|
|
||||||
|
response = HashMapCreate();
|
||||||
|
results = ArrayCreate();
|
||||||
|
|
||||||
|
/* TODO: Check for users matching search term and users outside our
|
||||||
|
* local server. */
|
||||||
|
users = DbList(db, 1, "users");
|
||||||
|
|
||||||
|
ConfigLock(db, &config);
|
||||||
|
if (!config.ok)
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "Directory endpoint failed to lock configuration.");
|
||||||
|
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
response = MatrixErrorCreate(M_UNKNOWN, config.err);
|
||||||
|
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IncludedLtLimit ((int64_t) included < dirRequest.limit)
|
||||||
|
for (i = 0, included = 0; i < ArraySize(users) && IncludedLtLimit; i++)
|
||||||
|
#undef IncludedLtLimit
|
||||||
|
{
|
||||||
|
HashMap *obj;
|
||||||
|
User *currentUser;
|
||||||
|
char *name = ArrayGet(users, i);
|
||||||
|
char *displayName;
|
||||||
|
char *lowerDisplayName;
|
||||||
|
char *avatarUrl;
|
||||||
|
|
||||||
|
if (!StrEquals(name, requesterName))
|
||||||
|
{
|
||||||
|
currentUser = UserLock(db, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentUser = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayName = UserGetProfile(currentUser, "displayname");
|
||||||
|
lowerDisplayName = StrLower(displayName);
|
||||||
|
avatarUrl = UserGetProfile(currentUser, "avatar_url");
|
||||||
|
|
||||||
|
/* Check for the user ID and display name. */
|
||||||
|
if (strstr(name, dirRequest.search_term) ||
|
||||||
|
(lowerDisplayName &&
|
||||||
|
strstr(lowerDisplayName, dirRequest.search_term)))
|
||||||
|
{
|
||||||
|
included++;
|
||||||
|
|
||||||
|
obj = HashMapCreate();
|
||||||
|
if (displayName)
|
||||||
|
{
|
||||||
|
JsonSet(obj, JsonValueString(displayName), 1, "display_name");
|
||||||
|
}
|
||||||
|
if (avatarUrl)
|
||||||
|
{
|
||||||
|
JsonSet(obj, JsonValueString(displayName), 1, "avatar_url");
|
||||||
|
}
|
||||||
|
if (name)
|
||||||
|
{
|
||||||
|
char *uID = StrConcat(4, "@", name, ":", config.serverName);
|
||||||
|
JsonSet(obj, JsonValueString(uID), 1, "user_id");
|
||||||
|
Free(uID);
|
||||||
|
}
|
||||||
|
ArrayAdd(results, JsonValueObject(obj));
|
||||||
|
}
|
||||||
|
if (lowerDisplayName)
|
||||||
|
{
|
||||||
|
Free(lowerDisplayName);
|
||||||
|
}
|
||||||
|
if (!StrEquals(name, requesterName))
|
||||||
|
{
|
||||||
|
UserUnlock(currentUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonSet(response, JsonValueArray(results), 1, "results");
|
||||||
|
JsonSet(response,
|
||||||
|
JsonValueBoolean((int64_t) included == dirRequest.limit),
|
||||||
|
1, "limited"
|
||||||
|
);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
UserUnlock(user);
|
||||||
|
JsonFree(request);
|
||||||
|
DbListFree(users);
|
||||||
|
ConfigUnlock(&config);
|
||||||
|
UserDirectoryRequestFree(&dirRequest);
|
||||||
|
return response;
|
||||||
|
}
|
|
@ -185,8 +185,6 @@ ROUTE_IMPL(RouteUserProfile, path, argp)
|
||||||
finish:
|
finish:
|
||||||
ConfigUnlock(&config);
|
ConfigUnlock(&config);
|
||||||
|
|
||||||
/* Username is handled by the router, freeing it would cause issues. */
|
|
||||||
Free(entry);
|
|
||||||
UserIdFree(userId);
|
UserIdFree(userId);
|
||||||
UserUnlock(user);
|
UserUnlock(user);
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ TelodendriaPrintHeader(void)
|
||||||
Log(LOG_INFO, "%s", TelodendriaHeader[i]);
|
Log(LOG_INFO, "%s", TelodendriaHeader[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(LOG_INFO, "Telodendria v" TELODENDRIA_VERSION " (%s v%s)", CytoplasmGetName(), CytoplasmGetVersion());
|
Log(LOG_INFO, "Telodendria v" TELODENDRIA_VERSION " (Cytoplasm v%s)", CytoplasmGetVersionStr());
|
||||||
Log(LOG_INFO, "");
|
Log(LOG_INFO, "");
|
||||||
Log(LOG_INFO,
|
Log(LOG_INFO,
|
||||||
"Copyright (C) 2024 Jordan Bancino <@jordan:bancino.net>");
|
"Copyright (C) 2024 Jordan Bancino <@jordan:bancino.net>");
|
||||||
|
|
|
@ -135,7 +135,7 @@ BuildResponse(Array * flows, Db * db, HashMap ** response, char *session, DbRef
|
||||||
|
|
||||||
json = DbJson(ref);
|
json = DbJson(ref);
|
||||||
HashMapSet(json, "completed", JsonValueArray(ArrayCreate()));
|
HashMapSet(json, "completed", JsonValueArray(ArrayCreate()));
|
||||||
HashMapSet(json, "last_access", JsonValueInteger(UtilServerTs()));
|
HashMapSet(json, "last_access", JsonValueInteger(UtilTsMillis()));
|
||||||
DbUnlock(db, ref);
|
DbUnlock(db, ref);
|
||||||
|
|
||||||
HashMapSet(*response, "completed", JsonValueArray(ArrayCreate()));
|
HashMapSet(*response, "completed", JsonValueArray(ArrayCreate()));
|
||||||
|
@ -452,7 +452,7 @@ UiaComplete(Array * flows, HttpServerContext * context, Db * db,
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
ArrayFree(possibleNext);
|
ArrayFree(possibleNext);
|
||||||
JsonValueFree(HashMapSet(dbJson, "last_access", JsonValueInteger(UtilServerTs())));
|
JsonValueFree(HashMapSet(dbJson, "last_access", JsonValueInteger(UtilTsMillis())));
|
||||||
DbUnlock(db, dbRef);
|
DbUnlock(db, dbRef);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -498,7 +498,7 @@ UiaCleanup(MatrixHttpHandlerArgs * args)
|
||||||
char *session = ArrayGet(sessions, i);
|
char *session = ArrayGet(sessions, i);
|
||||||
DbRef *ref = DbLock(args->db, 2, "user_interactive", session);
|
DbRef *ref = DbLock(args->db, 2, "user_interactive", session);
|
||||||
|
|
||||||
UInt64 lastAccess;
|
uint64_t lastAccess;
|
||||||
|
|
||||||
if (!ref)
|
if (!ref)
|
||||||
{
|
{
|
||||||
|
@ -513,7 +513,7 @@ UiaCleanup(MatrixHttpHandlerArgs * args)
|
||||||
|
|
||||||
/* If last access was greater than 15 minutes ago, remove this
|
/* If last access was greater than 15 minutes ago, remove this
|
||||||
* session */
|
* session */
|
||||||
if (UInt64Gt(UInt64Sub(UtilServerTs(), lastAccess), UInt64Create(0, 1000 * 60 * 15)))
|
if ((UtilTsMillis() - lastAccess) > (1000 * 60 * 15))
|
||||||
{
|
{
|
||||||
DbDelete(args->db, 2, "user_interactive", session);
|
DbDelete(args->db, 2, "user_interactive", session);
|
||||||
Log(LOG_DEBUG, "Deleted session %s", session);
|
Log(LOG_DEBUG, "Deleted session %s", session);
|
||||||
|
|
123
src/User.c
123
src/User.c
|
@ -28,8 +28,6 @@
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Sha.h>
|
#include <Cytoplasm/Sha.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/Int64.h>
|
|
||||||
#include <Cytoplasm/UInt64.h>
|
|
||||||
|
|
||||||
#include <Parser.h>
|
#include <Parser.h>
|
||||||
|
|
||||||
|
@ -44,7 +42,7 @@ struct User
|
||||||
char *deviceId;
|
char *deviceId;
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserValidate(char *localpart, char *domain)
|
UserValidate(char *localpart, char *domain)
|
||||||
{
|
{
|
||||||
size_t maxLen = 255 - strlen(domain) - 1;
|
size_t maxLen = 255 - strlen(domain) - 1;
|
||||||
|
@ -56,23 +54,23 @@ UserValidate(char *localpart, char *domain)
|
||||||
|
|
||||||
if (i > maxLen)
|
if (i > maxLen)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
||||||
(c == '.') || (c == '_') || (c == '=') || (c == '-') ||
|
(c == '.') || (c == '_') || (c == '=') || (c == '-') ||
|
||||||
(c == '/')))
|
(c == '/')))
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserHistoricalValidate(char *localpart, char *domain)
|
UserHistoricalValidate(char *localpart, char *domain)
|
||||||
{
|
{
|
||||||
size_t maxLen = 255 - strlen(domain) - 1;
|
size_t maxLen = 255 - strlen(domain) - 1;
|
||||||
|
@ -84,21 +82,21 @@ UserHistoricalValidate(char *localpart, char *domain)
|
||||||
|
|
||||||
if (i > maxLen)
|
if (i > maxLen)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!((c >= 0x21 && c <= 0x39) || (c >= 0x3B && c <= 0x7E)))
|
if (!((c >= 0x21 && c <= 0x39) || (c >= 0x3B && c <= 0x7E)))
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserExists(Db * db, char *name)
|
UserExists(Db * db, char *name)
|
||||||
{
|
{
|
||||||
return DbExists(db, 2, "users", name);
|
return DbExists(db, 2, "users", name);
|
||||||
|
@ -133,7 +131,7 @@ UserAuthenticate(Db * db, char *accessToken)
|
||||||
|
|
||||||
char *userName;
|
char *userName;
|
||||||
char *deviceId;
|
char *deviceId;
|
||||||
UInt64 expires;
|
uint64_t expires;
|
||||||
|
|
||||||
if (!db || !accessToken)
|
if (!db || !accessToken)
|
||||||
{
|
{
|
||||||
|
@ -157,8 +155,7 @@ UserAuthenticate(Db * db, char *accessToken)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UInt64Neq(expires, UInt64Create(0, 0)) &&
|
if (expires && UtilTsMillis() >= expires)
|
||||||
UInt64Geq(UtilServerTs(), expires))
|
|
||||||
{
|
{
|
||||||
UserUnlock(user);
|
UserUnlock(user);
|
||||||
DbUnlock(db, atRef);
|
DbUnlock(db, atRef);
|
||||||
|
@ -171,14 +168,14 @@ UserAuthenticate(Db * db, char *accessToken)
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserUnlock(User * user)
|
UserUnlock(User * user)
|
||||||
{
|
{
|
||||||
int ret;
|
bool ret;
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Free(user->name);
|
Free(user->name);
|
||||||
|
@ -196,7 +193,7 @@ UserCreate(Db * db, char *name, char *password)
|
||||||
User *user = NULL;
|
User *user = NULL;
|
||||||
HashMap *json = NULL;
|
HashMap *json = NULL;
|
||||||
|
|
||||||
UInt64 ts = UtilServerTs();
|
uint64_t ts = UtilTsMillis();
|
||||||
|
|
||||||
/* TODO: Put some sort of password policy(like for example at least
|
/* TODO: Put some sort of password policy(like for example at least
|
||||||
* 8 chars, or maybe check it's entropy)? */
|
* 8 chars, or maybe check it's entropy)? */
|
||||||
|
@ -233,7 +230,7 @@ UserCreate(Db * db, char *name, char *password)
|
||||||
|
|
||||||
json = DbJson(user->ref);
|
json = DbJson(user->ref);
|
||||||
HashMapSet(json, "createdOn", JsonValueInteger(ts));
|
HashMapSet(json, "createdOn", JsonValueInteger(ts));
|
||||||
HashMapSet(json, "deactivated", JsonValueBoolean(0));
|
HashMapSet(json, "deactivated", JsonValueBoolean(false));
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -356,7 +353,7 @@ UserGetDeviceId(User * user)
|
||||||
return user ? user->deviceId : NULL;
|
return user ? user->deviceId : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserCheckPassword(User * user, char *password)
|
UserCheckPassword(User * user, char *password)
|
||||||
{
|
{
|
||||||
HashMap *json;
|
HashMap *json;
|
||||||
|
@ -368,11 +365,11 @@ UserCheckPassword(User * user, char *password)
|
||||||
char *hashedPwd;
|
char *hashedPwd;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
|
||||||
int result;
|
bool result;
|
||||||
|
|
||||||
if (!user || !password)
|
if (!user || !password)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
json = DbJson(user->ref);
|
json = DbJson(user->ref);
|
||||||
|
@ -382,12 +379,12 @@ UserCheckPassword(User * user, char *password)
|
||||||
|
|
||||||
if (!storedHash || !salt)
|
if (!storedHash || !salt)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp = StrConcat(2, password, salt);
|
tmp = StrConcat(2, password, salt);
|
||||||
hashBytes = Sha256(tmp);
|
hashBytes = Sha256(tmp);
|
||||||
hashedPwd = ShaToHex(hashBytes);
|
hashedPwd = ShaToHex(hashBytes, HASH_SHA256);
|
||||||
Free(tmp);
|
Free(tmp);
|
||||||
Free(hashBytes);
|
Free(hashBytes);
|
||||||
|
|
||||||
|
@ -398,7 +395,7 @@ UserCheckPassword(User * user, char *password)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserSetPassword(User * user, char *password)
|
UserSetPassword(User * user, char *password)
|
||||||
{
|
{
|
||||||
HashMap *json;
|
HashMap *json;
|
||||||
|
@ -410,7 +407,7 @@ UserSetPassword(User * user, char *password)
|
||||||
|
|
||||||
if (!user || !password)
|
if (!user || !password)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
json = DbJson(user->ref);
|
json = DbJson(user->ref);
|
||||||
|
@ -418,7 +415,7 @@ UserSetPassword(User * user, char *password)
|
||||||
salt = StrRandom(16);
|
salt = StrRandom(16);
|
||||||
tmpstr = StrConcat(2, password, salt);
|
tmpstr = StrConcat(2, password, salt);
|
||||||
hashBytes = Sha256(tmpstr);
|
hashBytes = Sha256(tmpstr);
|
||||||
hash = ShaToHex(hashBytes);
|
hash = ShaToHex(hashBytes, HASH_SHA256);
|
||||||
|
|
||||||
JsonValueFree(HashMapSet(json, "salt", JsonValueString(salt)));
|
JsonValueFree(HashMapSet(json, "salt", JsonValueString(salt)));
|
||||||
JsonValueFree(HashMapSet(json, "password", JsonValueString(hash)));
|
JsonValueFree(HashMapSet(json, "password", JsonValueString(hash)));
|
||||||
|
@ -428,10 +425,10 @@ UserSetPassword(User * user, char *password)
|
||||||
Free(hashBytes);
|
Free(hashBytes);
|
||||||
Free(tmpstr);
|
Free(tmpstr);
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserDeactivate(User * user, char * from, char * reason)
|
UserDeactivate(User * user, char * from, char * reason)
|
||||||
{
|
{
|
||||||
HashMap *json;
|
HashMap *json;
|
||||||
|
@ -439,7 +436,7 @@ UserDeactivate(User * user, char * from, char * reason)
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* By default, it's the target's username */
|
/* By default, it's the target's username */
|
||||||
|
@ -450,7 +447,7 @@ UserDeactivate(User * user, char * from, char * reason)
|
||||||
|
|
||||||
json = DbJson(user->ref);
|
json = DbJson(user->ref);
|
||||||
|
|
||||||
JsonValueFree(HashMapSet(json, "deactivated", JsonValueBoolean(1)));
|
JsonValueFree(HashMapSet(json, "deactivated", JsonValueBoolean(true)));
|
||||||
|
|
||||||
val = JsonValueString(from);
|
val = JsonValueString(from);
|
||||||
JsonValueFree(JsonSet(json, val, 2, "deactivate", "by"));
|
JsonValueFree(JsonSet(json, val, 2, "deactivate", "by"));
|
||||||
|
@ -460,38 +457,38 @@ UserDeactivate(User * user, char * from, char * reason)
|
||||||
JsonValueFree(JsonSet(json, val, 2, "deactivate", "reason"));
|
JsonValueFree(JsonSet(json, val, 2, "deactivate", "reason"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserReactivate(User * user)
|
UserReactivate(User * user)
|
||||||
{
|
{
|
||||||
HashMap *json;
|
HashMap *json;
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
json = DbJson(user->ref);
|
json = DbJson(user->ref);
|
||||||
|
|
||||||
|
|
||||||
JsonValueFree(HashMapSet(json, "deactivated", JsonValueBoolean(0)));
|
JsonValueFree(HashMapSet(json, "deactivated", JsonValueBoolean(false)));
|
||||||
|
|
||||||
JsonValueFree(HashMapDelete(json, "deactivate"));
|
JsonValueFree(HashMapDelete(json, "deactivate"));
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserDeactivated(User * user)
|
UserDeactivated(User * user)
|
||||||
{
|
{
|
||||||
HashMap *json;
|
HashMap *json;
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
json = DbJson(user->ref);
|
json = DbJson(user->ref);
|
||||||
|
@ -537,17 +534,17 @@ UserAccessTokenGenerate(User * user, char *deviceId, int withRefresh)
|
||||||
|
|
||||||
if (withRefresh)
|
if (withRefresh)
|
||||||
{
|
{
|
||||||
token->lifetime = Int64Create(0, 1000 * 60 * 60 * 24 * 7); /* 1 Week */
|
token->lifetime = 1000 * 60 * 60 * 24 * 7; /* 1 Week */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
token->lifetime = Int64Create(0, 0);
|
token->lifetime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserAccessTokenSave(Db * db, UserAccessToken * token)
|
UserAccessTokenSave(Db * db, UserAccessToken * token)
|
||||||
{
|
{
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
|
@ -555,14 +552,14 @@ UserAccessTokenSave(Db * db, UserAccessToken * token)
|
||||||
|
|
||||||
if (!token)
|
if (!token)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref = DbCreate(db, 3, "tokens", "access", token->string);
|
ref = DbCreate(db, 3, "tokens", "access", token->string);
|
||||||
|
|
||||||
if (!ref)
|
if (!ref)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
json = DbJson(ref);
|
json = DbJson(ref);
|
||||||
|
@ -570,9 +567,9 @@ UserAccessTokenSave(Db * db, UserAccessToken * token)
|
||||||
HashMapSet(json, "user", JsonValueString(token->user));
|
HashMapSet(json, "user", JsonValueString(token->user));
|
||||||
HashMapSet(json, "device", JsonValueString(token->deviceId));
|
HashMapSet(json, "device", JsonValueString(token->deviceId));
|
||||||
|
|
||||||
if (Int64Neq(token->lifetime, Int64Create(0, 0)))
|
if (token->lifetime)
|
||||||
{
|
{
|
||||||
HashMapSet(json, "expires", JsonValueInteger(UInt64Add(UtilServerTs(), token->lifetime)));
|
HashMapSet(json, "expires", JsonValueInteger(UtilTsMillis() + token->lifetime));
|
||||||
}
|
}
|
||||||
|
|
||||||
return DbUnlock(db, ref);
|
return DbUnlock(db, ref);
|
||||||
|
@ -592,7 +589,7 @@ UserAccessTokenFree(UserAccessToken * token)
|
||||||
Free(token);
|
Free(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserDeleteToken(User * user, char *token)
|
UserDeleteToken(User * user, char *token)
|
||||||
{
|
{
|
||||||
char *username;
|
char *username;
|
||||||
|
@ -610,14 +607,14 @@ UserDeleteToken(User * user, char *token)
|
||||||
|
|
||||||
if (!user || !token)
|
if (!user || !token)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
db = user->db;
|
db = user->db;
|
||||||
/* First check if the token even exists */
|
/* First check if the token even exists */
|
||||||
if (!DbExists(db, 3, "tokens", "access", token))
|
if (!DbExists(db, 3, "tokens", "access", token))
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If it does, get it's username. */
|
/* If it does, get it's username. */
|
||||||
|
@ -625,7 +622,7 @@ UserDeleteToken(User * user, char *token)
|
||||||
|
|
||||||
if (!tokenRef)
|
if (!tokenRef)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
tokenJson = DbJson(tokenRef);
|
tokenJson = DbJson(tokenRef);
|
||||||
username = JsonValueAsString(HashMapGet(tokenJson, "user"));
|
username = JsonValueAsString(HashMapGet(tokenJson, "user"));
|
||||||
|
@ -635,7 +632,7 @@ UserDeleteToken(User * user, char *token)
|
||||||
{
|
{
|
||||||
/* Token does not match user, do not delete it */
|
/* Token does not match user, do not delete it */
|
||||||
DbUnlock(db, tokenRef);
|
DbUnlock(db, tokenRef);
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
userJson = DbJson(user->ref);
|
userJson = DbJson(user->ref);
|
||||||
|
@ -643,7 +640,7 @@ UserDeleteToken(User * user, char *token)
|
||||||
|
|
||||||
if (!deviceObj)
|
if (!deviceObj)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete refresh token, if present */
|
/* Delete refresh token, if present */
|
||||||
|
@ -657,17 +654,17 @@ UserDeleteToken(User * user, char *token)
|
||||||
deletedVal = HashMapDelete(deviceObj, deviceId);
|
deletedVal = HashMapDelete(deviceObj, deviceId);
|
||||||
if (!deletedVal)
|
if (!deletedVal)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
JsonValueFree(deletedVal);
|
JsonValueFree(deletedVal);
|
||||||
|
|
||||||
/* Delete the access token. */
|
/* Delete the access token. */
|
||||||
if (!DbUnlock(db, tokenRef) || !DbDelete(db, 3, "tokens", "access", token))
|
if (!DbUnlock(db, tokenRef) || !DbDelete(db, 3, "tokens", "access", token))
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
|
@ -699,7 +696,7 @@ UserSetProfile(User * user, char *name, char *val)
|
||||||
JsonValueFree(JsonSet(json, JsonValueString(val), 2, "profile", name));
|
JsonValueFree(JsonSet(json, JsonValueString(val), 2, "profile", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserDeleteTokens(User * user, char *exempt)
|
UserDeleteTokens(User * user, char *exempt)
|
||||||
{
|
{
|
||||||
HashMap *devices;
|
HashMap *devices;
|
||||||
|
@ -708,13 +705,13 @@ UserDeleteTokens(User * user, char *exempt)
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
devices = JsonValueAsObject(HashMapGet(DbJson(user->ref), "devices"));
|
devices = JsonValueAsObject(HashMapGet(DbJson(user->ref), "devices"));
|
||||||
if (!devices)
|
if (!devices)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (HashMapIterate(devices, &deviceId, (void **) &deviceObj))
|
while (HashMapIterate(devices, &deviceId, (void **) &deviceObj))
|
||||||
|
@ -741,7 +738,7 @@ UserDeleteTokens(User * user, char *exempt)
|
||||||
JsonValueFree(HashMapDelete(devices, deviceId));
|
JsonValueFree(HashMapDelete(devices, deviceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -755,30 +752,30 @@ UserGetPrivileges(User * user)
|
||||||
return UserDecodePrivileges(JsonValueAsArray(HashMapGet(DbJson(user->ref), "privileges")));
|
return UserDecodePrivileges(JsonValueAsArray(HashMapGet(DbJson(user->ref), "privileges")));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
bool
|
||||||
UserSetPrivileges(User * user, int privileges)
|
UserSetPrivileges(User * user, int privileges)
|
||||||
{
|
{
|
||||||
JsonValue *val;
|
JsonValue *val;
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!privileges)
|
if (!privileges)
|
||||||
{
|
{
|
||||||
JsonValueFree(HashMapDelete(DbJson(user->ref), "privileges"));
|
JsonValueFree(HashMapDelete(DbJson(user->ref), "privileges"));
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
val = JsonValueArray(UserEncodePrivileges(privileges));
|
val = JsonValueArray(UserEncodePrivileges(privileges));
|
||||||
if (!val)
|
if (!val)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonValueFree(HashMapSet(DbJson(user->ref), "privileges", val));
|
JsonValueFree(HashMapSet(DbJson(user->ref), "privileges", val));
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#ifndef TELODENDRIA_PARSER_H
|
#ifndef TELODENDRIA_PARSER_H
|
||||||
#define TELODENDRIA_PARSER_H
|
#define TELODENDRIA_PARSER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* @Nm Parser
|
* @Nm Parser
|
||||||
* @Nd Functions for dealing with grammars found in Matrix
|
* @Nd Functions for dealing with grammars found in Matrix
|
||||||
|
@ -58,17 +60,17 @@ typedef struct CommonID {
|
||||||
* Parses a common identifier, as per the Common Identifier Format as defined
|
* Parses a common identifier, as per the Common Identifier Format as defined
|
||||||
* by the [matrix] specification.
|
* by the [matrix] specification.
|
||||||
*/
|
*/
|
||||||
extern int ParseCommonID(char *, CommonID *);
|
extern bool ParseCommonID(char *, CommonID *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the server part in a common identifier.
|
* Parses the server part in a common identifier.
|
||||||
*/
|
*/
|
||||||
extern int ParseServerPart(char *, ServerPart *);
|
extern bool ParseServerPart(char *, ServerPart *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whenever the string is a valid common ID with the correct sigil.
|
* Checks whenever the string is a valid common ID with the correct sigil.
|
||||||
*/
|
*/
|
||||||
extern int ValidCommonID(char *, char);
|
extern bool ValidCommonID(char *, char);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees a CommonID's values. Note that it doesn't free the CommonID itself.
|
* Frees a CommonID's values. Note that it doesn't free the CommonID itself.
|
||||||
|
@ -100,7 +102,7 @@ extern char * ParserRecomposeServerPart(ServerPart);
|
||||||
/**
|
/**
|
||||||
* Compares whenever a ServerName is equivalent to a server name string.
|
* Compares whenever a ServerName is equivalent to a server name string.
|
||||||
*/
|
*/
|
||||||
extern int ParserServerNameEquals(ServerPart, char *);
|
extern bool ParserServerNameEquals(ServerPart, char *);
|
||||||
|
|
||||||
|
|
||||||
#endif /* TELODENDRIA_PARSER_H */
|
#endif /* TELODENDRIA_PARSER_H */
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
#include <Cytoplasm/Int64.h>
|
|
||||||
|
|
||||||
#include <Schema/RegToken.h>
|
#include <Schema/RegToken.h>
|
||||||
|
|
||||||
|
@ -78,7 +77,7 @@ extern RegTokenInfo * RegTokenGetInfo(Db *, char *);
|
||||||
* structure will be returned. Otherwise, NULL will be returned.
|
* structure will be returned. Otherwise, NULL will be returned.
|
||||||
*/
|
*/
|
||||||
extern RegTokenInfo *
|
extern RegTokenInfo *
|
||||||
RegTokenCreate(Db *, char *, char *, UInt64, Int64, int);
|
RegTokenCreate(Db *, char *, char *, uint64_t, int64_t, int);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free the memory associated with the registration token. This should
|
* Free the memory associated with the registration token. This should
|
||||||
|
|
|
@ -86,6 +86,7 @@ ROUTE(RouteChangePwd);
|
||||||
ROUTE(RouteDeactivate);
|
ROUTE(RouteDeactivate);
|
||||||
ROUTE(RouteTokenValid);
|
ROUTE(RouteTokenValid);
|
||||||
ROUTE(RouteUserProfile);
|
ROUTE(RouteUserProfile);
|
||||||
|
ROUTE(RouteUserDirectory);
|
||||||
ROUTE(RouteRequestToken);
|
ROUTE(RouteRequestToken);
|
||||||
|
|
||||||
ROUTE(RouteUiaFallback);
|
ROUTE(RouteUiaFallback);
|
||||||
|
|
|
@ -39,12 +39,13 @@
|
||||||
* users, among many other tasks.
|
* users, among many other tasks.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Cytoplasm/Int64.h>
|
|
||||||
#include <Cytoplasm/Db.h>
|
#include <Cytoplasm/Db.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
|
|
||||||
#include <Parser.h>
|
#include <Parser.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Many functions here operate on an opaque user structure.
|
* Many functions here operate on an opaque user structure.
|
||||||
*/
|
*/
|
||||||
|
@ -77,7 +78,7 @@ typedef struct UserAccessToken
|
||||||
char *user;
|
char *user;
|
||||||
char *string;
|
char *string;
|
||||||
char *deviceId;
|
char *deviceId;
|
||||||
Int64 lifetime;
|
uint64_t lifetime;
|
||||||
} UserAccessToken;
|
} UserAccessToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +99,7 @@ typedef struct UserLoginInfo
|
||||||
* the local part is allowed to be. This function is used to ensure
|
* the local part is allowed to be. This function is used to ensure
|
||||||
* that client-provided Matrix IDs are valid on this server.
|
* that client-provided Matrix IDs are valid on this server.
|
||||||
*/
|
*/
|
||||||
extern int UserValidate(char *, char *);
|
extern bool UserValidate(char *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function behaves just like
|
* This function behaves just like
|
||||||
|
@ -109,13 +110,13 @@ extern int UserValidate(char *, char *);
|
||||||
* spec compliant but remain in use since before the new restrictions
|
* spec compliant but remain in use since before the new restrictions
|
||||||
* were put in place.
|
* were put in place.
|
||||||
*/
|
*/
|
||||||
extern int UserHistoricalValidate(char *, char *);
|
extern bool UserHistoricalValidate(char *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the user identified by the specified localpart
|
* Determine whether the user identified by the specified localpart
|
||||||
* exists in the database.
|
* exists in the database.
|
||||||
*/
|
*/
|
||||||
extern int UserExists(Db *, char *);
|
extern bool UserExists(Db *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new user with the specified localpart and password, in
|
* Create a new user with the specified localpart and password, in
|
||||||
|
@ -146,7 +147,7 @@ extern User * UserAuthenticate(Db *, char *);
|
||||||
* .Fn DbUnlock
|
* .Fn DbUnlock
|
||||||
* under the hood.
|
* under the hood.
|
||||||
*/
|
*/
|
||||||
extern int UserUnlock(User *);
|
extern bool UserUnlock(User *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log in a user. This function takes the user's password, desired
|
* Log in a user. This function takes the user's password, desired
|
||||||
|
@ -179,13 +180,13 @@ extern char * UserGetDeviceId(User *);
|
||||||
* does not store passwords in plain text, so this function hashes the
|
* does not store passwords in plain text, so this function hashes the
|
||||||
* password and checks it against what is stored in the database.
|
* password and checks it against what is stored in the database.
|
||||||
*/
|
*/
|
||||||
extern int UserCheckPassword(User *, char *);
|
extern bool UserCheckPassword(User *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the given user's password by hashing a plain text password and
|
* Reset the given user's password by hashing a plain text password and
|
||||||
* storing it in the database.
|
* storing it in the database.
|
||||||
*/
|
*/
|
||||||
extern int UserSetPassword(User *, char *);
|
extern bool UserSetPassword(User *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immediately deactivate the given user account such that it can no
|
* Immediately deactivate the given user account such that it can no
|
||||||
|
@ -198,21 +199,21 @@ extern int UserSetPassword(User *, char *);
|
||||||
* responsible for deactivating the target user is NULL, then it is
|
* responsible for deactivating the target user is NULL, then it is
|
||||||
* set to the target's own name.
|
* set to the target's own name.
|
||||||
*/
|
*/
|
||||||
extern int UserDeactivate(User *, char *, char *);
|
extern bool UserDeactivate(User *, char *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reactivates the given user account if it has been deactvated with
|
* Reactivates the given user account if it has been deactvated with
|
||||||
* .Fn UserDeactivate ,
|
* .Fn UserDeactivate ,
|
||||||
* otherwise, it simply doesn't do anything.
|
* otherwise, it simply doesn't do anything.
|
||||||
*/
|
*/
|
||||||
extern int UserReactivate(User *);
|
extern bool UserReactivate(User *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a boolean value indicating whether or not the user was
|
* Return a boolean value indicating whether or not the user was
|
||||||
* deactivated using
|
* deactivated using
|
||||||
* .Fn UserDeactivate .
|
* .Fn UserDeactivate .
|
||||||
*/
|
*/
|
||||||
extern int UserDeactivated(User *);
|
extern bool UserDeactivated(User *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the devices that belong to the user, in JSON format,
|
* Fetches the devices that belong to the user, in JSON format,
|
||||||
|
@ -233,7 +234,7 @@ extern UserAccessToken * UserAccessTokenGenerate(User *, char *, int);
|
||||||
* Write the specified access token to the database, returning a
|
* Write the specified access token to the database, returning a
|
||||||
* boolean value indicating success.
|
* boolean value indicating success.
|
||||||
*/
|
*/
|
||||||
extern int UserAccessTokenSave(Db *, UserAccessToken *);
|
extern bool UserAccessTokenSave(Db *, UserAccessToken *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free the memory associated with the given access token.
|
* Free the memory associated with the given access token.
|
||||||
|
@ -243,7 +244,7 @@ extern void UserAccessTokenFree(UserAccessToken *);
|
||||||
/**
|
/**
|
||||||
* Delete a specific access token by name.
|
* Delete a specific access token by name.
|
||||||
*/
|
*/
|
||||||
extern int UserDeleteToken(User *, char *);
|
extern bool UserDeleteToken(User *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a string property from the user's profile given the specified
|
* Get a string property from the user's profile given the specified
|
||||||
|
@ -262,7 +263,7 @@ extern void UserSetProfile(User *, char *, char *);
|
||||||
* except for the one provided by name, unless NULL is provided for
|
* except for the one provided by name, unless NULL is provided for
|
||||||
* the name.
|
* the name.
|
||||||
*/
|
*/
|
||||||
extern int UserDeleteTokens(User *, char *);
|
extern bool UserDeleteTokens(User *, char *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current privileges of the user as a packed bit field. Use
|
* Get the current privileges of the user as a packed bit field. Use
|
||||||
|
@ -274,7 +275,7 @@ extern int UserGetPrivileges(User *);
|
||||||
/**
|
/**
|
||||||
* Set the privileges of the user.
|
* Set the privileges of the user.
|
||||||
*/
|
*/
|
||||||
extern int UserSetPrivileges(User *, int);
|
extern bool UserSetPrivileges(User *, int);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode the JSON that represents the user privileges into a packed
|
* Decode the JSON that represents the user privileges into a packed
|
||||||
|
|
|
@ -64,47 +64,23 @@ query(char *select, HashMap * json, int canonical)
|
||||||
int expectArr = 0;
|
int expectArr = 0;
|
||||||
int func = 0;
|
int func = 0;
|
||||||
|
|
||||||
expectArr = (sscanf(key, "%127[^[][%lu]", keyName, &arrInd) == 2);
|
expectArr = (sscanf(key, "%127[^[][%zu]", keyName, &arrInd) == 2);
|
||||||
|
|
||||||
if (keyName[0] == '@')
|
if (keyName[0] == '@')
|
||||||
{
|
{
|
||||||
if (StrEquals(keyName + 1, "length"))
|
if (StrEquals(keyName + 1, "length"))
|
||||||
{
|
{
|
||||||
UInt64 len;
|
uint64_t len;
|
||||||
|
|
||||||
switch (JsonValueType(val))
|
switch (JsonValueType(val))
|
||||||
{
|
{
|
||||||
case JSON_ARRAY:
|
case JSON_ARRAY:
|
||||||
if (sizeof(size_t) == sizeof(UInt64))
|
len = ArraySize(JsonValueAsArray(val));
|
||||||
{
|
|
||||||
size_t slen = ArraySize(JsonValueAsArray(val));
|
|
||||||
UInt32 high = slen >> 32;
|
|
||||||
UInt32 low = slen;
|
|
||||||
|
|
||||||
len = UInt64Create(high, low);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
len = UInt64Create(0, ArraySize(JsonValueAsArray(val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
val = JsonValueInteger(len);
|
val = JsonValueInteger(len);
|
||||||
ArrayAdd(cleanUp, val);
|
ArrayAdd(cleanUp, val);
|
||||||
break;
|
break;
|
||||||
case JSON_STRING:
|
case JSON_STRING:
|
||||||
if (sizeof(size_t) == sizeof(UInt64))
|
len = strlen(JsonValueAsString(val));
|
||||||
{
|
|
||||||
size_t slen = strlen(JsonValueAsString(val));
|
|
||||||
UInt32 high = slen >> 32;
|
|
||||||
UInt32 low = slen;
|
|
||||||
|
|
||||||
len = UInt64Create(high, low);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
len = UInt64Create(0, strlen(JsonValueAsString(val)));
|
|
||||||
}
|
|
||||||
|
|
||||||
val = JsonValueInteger(len);
|
val = JsonValueInteger(len);
|
||||||
ArrayAdd(cleanUp, val);
|
ArrayAdd(cleanUp, val);
|
||||||
break;
|
break;
|
||||||
|
@ -154,7 +130,7 @@ query(char *select, HashMap * json, int canonical)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (sscanf(keyName + 1, "%lu", &i) == 1)
|
if (sscanf(keyName + 1, "%zu", &i) == 1)
|
||||||
{
|
{
|
||||||
JsonValueFree(ArrayDelete(JsonValueAsArray(val), i));
|
JsonValueFree(ArrayDelete(JsonValueAsArray(val), i));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue