mirror of
https://github.com/matrix-construct/construct
synced 2024-06-01 01:28:54 +02:00
Compare commits
123 commits
Author | SHA1 | Date | |
---|---|---|---|
0624b69246 | |||
e5ae70c521 | |||
759871ce8e | |||
3c9a4f8c57 | |||
f6b3b8b758 | |||
4fe85805bf | |||
b6cb1180f7 | |||
741304271e | |||
cd2b92b8fe | |||
feb4ac1fd3 | |||
d4ba215a3b | |||
8a705f77a9 | |||
f86fddc3a3 | |||
8eb10c5d1a | |||
a7a3275817 | |||
82feeb5274 | |||
8af781c5d7 | |||
29f3ddfc3e | |||
0b888b662a | |||
bbed809975 | |||
f9aeae5516 | |||
9301980f9d | |||
ca80d66e85 | |||
e7ad503f8c | |||
f70d837258 | |||
6f5121dc6a | |||
91a8fcbe43 | |||
b4b26484ec | |||
faf796a56b | |||
48e0755a79 | |||
5f5be52fa9 | |||
5f0b98e5d1 | |||
62a1c850cc | |||
e874e28473 | |||
55f332c821 | |||
44a4f33e45 | |||
04025af961 | |||
d1b0722169 | |||
0943cfd69c | |||
8b0cf48578 | |||
8eb4de920c | |||
5a06c0066e | |||
4ebc9fefaf | |||
5f3398bf52 | |||
dabc8b4304 | |||
d80f29b65a | |||
6c3420afbc | |||
6072229dcc | |||
956aa7682f | |||
238cc10489 | |||
1a032b28b7 | |||
f08b9e85cd | |||
9ce44aadd5 | |||
78d6e4ce03 | |||
882629ab53 | |||
8ade7c814e | |||
4d5d99ab2c | |||
502a03a8cf | |||
1e42fa16a7 | |||
0a151a3a90 | |||
fa46231e3a | |||
336026dde6 | |||
d5df04a183 | |||
080748f0af | |||
321ea3d641 | |||
d41926bc6f | |||
58476f59c3 | |||
8dc50db2dc | |||
2122412c6d | |||
15cb6bfdca | |||
fa0365fa31 | |||
02e09728a5 | |||
dc13381822 | |||
22b9cf515c | |||
a10992b813 | |||
c77df219b5 | |||
55a73624d2 | |||
cab2b4c822 | |||
79f5e4fd8d | |||
4c06793980 | |||
ef27ae50dc | |||
6e7d63ce6d | |||
73a3a4cb35 | |||
b40c21545a | |||
b4e75dfdf0 | |||
ba6030f4ce | |||
dd6d17433e | |||
7904fa0563 | |||
1f35421cec | |||
af783c8dfe | |||
e30c77283c | |||
8c629f20a6 | |||
b2d3ae4d0d | |||
c8ff5bbc37 | |||
715f21564e | |||
50481585b0 | |||
5d40121d53 | |||
b906650946 | |||
11d0ab9eaf | |||
24cb1b804f | |||
13d21ca83c | |||
7501f983a6 | |||
193d87ef76 | |||
922db35dd5 | |||
02dd645748 | |||
a67227f28f | |||
b21558fada | |||
97692b7330 | |||
fc91ace4f2 | |||
219571e69c | |||
d2c47b0d9e | |||
8cbeb98b59 | |||
25ed05429b | |||
4518cf104a | |||
c02b3d845f | |||
32d4d44662 | |||
b260bd85a7 | |||
d4d8063dee | |||
7be2582714 | |||
6264071248 | |||
602833d0ef | |||
1b933f8b8f | |||
2db42a7e70 |
|
@ -5,6 +5,10 @@ skip_tags: true
|
|||
services:
|
||||
- docker
|
||||
|
||||
branches:
|
||||
only:
|
||||
- appveyor
|
||||
|
||||
environment:
|
||||
COMMAND: if [[ "${APPVEYOR_REPO_COMMIT_MESSAGE@Q}" == *"[ci debug]"* ]]; then export DEBUG_FLAGS="--enable-debug"; fi; if [[ "${APPVEYOR_REPO_COMMIT_MESSAGE@Q}" == *"[ci gdb]"* ]]; then apt-get update && apt-get -y install gdb && export CONSTRUCT="gdb --batch -ex r -ex bt --return-child-result --args construct"; else export CONSTRUCT="construct"; fi && rmdir -v deps/rocksdb && ln -sv /usr/src/rocksdb deps && ./autogen.sh && ./configure --enable-assert ${DEBUG_FLAGS} && make install && ${CONSTRUCT} -smoketest -debug localhost
|
||||
|
||||
|
|
98
.github/workflows/docker.yml
vendored
Normal file
98
.github/workflows/docker.yml
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
#
|
||||
# Build docker images
|
||||
#
|
||||
name: Docker Images
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
features:
|
||||
type: string
|
||||
description: JSON array of feature-set names to build images for.
|
||||
distros:
|
||||
type: string
|
||||
description: JSON array of operating system distros to build for.
|
||||
machines:
|
||||
type: string
|
||||
description: JSON array of machines to build for.
|
||||
toolchains:
|
||||
type: string
|
||||
description: JSON array of compiler toolchains to build for.
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
ctor_id: ${{vars.DOCKER_ID}}
|
||||
ctor_url: https://github.com/${{github.repository}}
|
||||
|
||||
jobs:
|
||||
# Build the base-feature intermediate images (cached and not shipped).
|
||||
base:
|
||||
uses: ./.github/workflows/docker_prime.yml
|
||||
with:
|
||||
id: ${{github.env.ctor_id}}
|
||||
url: ${{github.env.ctor_url}}
|
||||
features: '["base"]'
|
||||
distros: ${{github.event.inputs.distros || vars.DOCKER_DISTROS}}
|
||||
machines: ${{github.event.inputs.machines || vars.DOCKER_MACHINES}}
|
||||
test: ${{contains(github.events.push.commits[0].message, '[ci test]')}}
|
||||
|
||||
# Build the full-feature intermediate images (cached and not shipped).
|
||||
full:
|
||||
uses: ./.github/workflows/docker_prime.yml
|
||||
needs: [base]
|
||||
with:
|
||||
id: ${{github.env.ctor_id}}
|
||||
url: ${{github.env.ctor_url}}
|
||||
features: '["full"]'
|
||||
distros: ${{github.event.inputs.distros || vars.DOCKER_DISTROS}}
|
||||
machines: ${{github.event.inputs.machines || vars.DOCKER_MACHINES}}
|
||||
test: ${{contains(github.events.push.commits[0].message, '[ci test]')}}
|
||||
|
||||
# Build the leaf images (shipped and not cached)
|
||||
built:
|
||||
needs: [base, full]
|
||||
runs-on: ${{matrix.machine}}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
feature: ${{fromJSON(github.event.inputs.features || vars.DOCKER_FEATURES)}}
|
||||
distro: ${{fromJSON(github.event.inputs.distros || vars.DOCKER_DISTROS)}}
|
||||
machine: ${{fromJSON(github.event.inputs.machines || vars.DOCKER_MACHINES)}}
|
||||
toolchain: ${{fromJSON(github.event.inputs.toolchains || vars.DOCKER_TOOLCHAINS)}}
|
||||
exclude:
|
||||
- distro: alpine-3.17
|
||||
toolchain: gcc-10 # n/a on distro version
|
||||
- distro: alpine-3.17
|
||||
toolchain: gcc-11 # n/a on distro version
|
||||
- distro: alpine-3.17
|
||||
toolchain: clang-14 # n/a on distro version
|
||||
- distro: ubuntu-22.04
|
||||
toolchain: clang-15 # n/a on distro version
|
||||
- machine: arm64
|
||||
toolchain: gcc-12 # build hangs
|
||||
- machine: arm64
|
||||
toolchain: gcc-11 # build hangs
|
||||
- machine: arm64
|
||||
toolchain: gcc-10 # build hangs
|
||||
- machine: arm64
|
||||
distro: alpine-3.17
|
||||
toolchain: clang-14 # smoketest crash
|
||||
- machine: arm64
|
||||
distro: alpine-3.17
|
||||
toolchain: clang-15 # smoketest crash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: build
|
||||
env:
|
||||
ctor_features: ${{matrix.feature}}
|
||||
ctor_distros: ${{matrix.distro}}
|
||||
ctor_machines: ${{matrix.machine}}
|
||||
ctor_toolchains: ${{matrix.toolchain}}
|
||||
ctor_test: ${{contains(github.events.push.commits[0].message, '[ci test]')}}
|
||||
|
||||
run: |
|
||||
docker/build-and-push-images.sh
|
62
.github/workflows/docker_prime.yml
vendored
Normal file
62
.github/workflows/docker_prime.yml
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
# Build intermediate images
|
||||
#
|
||||
# Called to build lower-layer images which other images depend on. These are
|
||||
# cached for use by the next layer but not shipped to users.
|
||||
#
|
||||
name: Docker Images Prime
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
id:
|
||||
type: string
|
||||
description: Dockerhub acct/repo identity.
|
||||
url:
|
||||
type: string
|
||||
description: Git repository for checkout.
|
||||
features:
|
||||
type: string
|
||||
description: JSON array of feature-set names to build images for.
|
||||
distros:
|
||||
type: string
|
||||
description: JSON array of operating system distros to build for.
|
||||
machines:
|
||||
type: string
|
||||
description: JSON array of machines to build for.
|
||||
test:
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
description: Echo all docker commands rather than invoking them.
|
||||
|
||||
concurrency:
|
||||
group: ${{github.workflow}}-${{inputs.features}}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
ctor_id: ${{inputs.id}}
|
||||
ctor_url: ${{inputs.url}}
|
||||
|
||||
jobs:
|
||||
prime:
|
||||
runs-on: ${{matrix.machine}}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
feature: ${{fromJSON(inputs.features)}}
|
||||
distro: ${{fromJSON(inputs.distros)}}
|
||||
machine: ${{fromJSON(inputs.machines)}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: build
|
||||
env:
|
||||
ctor_features: ${{matrix.feature}}
|
||||
ctor_distros: ${{matrix.distro}}
|
||||
ctor_machines: ${{matrix.machine}}
|
||||
ctor_toolchains: false
|
||||
ctor_test: ${{inputs.test}}
|
||||
|
||||
run: |
|
||||
docker/build-and-push-images.sh ${{matrix.feature}}
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -19,6 +19,7 @@ Makefile
|
|||
*.gch
|
||||
*.cache
|
||||
*.tmp
|
||||
*.save
|
||||
*.profdata
|
||||
.deps
|
||||
.dirstamp
|
||||
|
|
64
configure.ac
64
configure.ac
|
@ -83,6 +83,16 @@ dnl
|
|||
dnl Platform
|
||||
dnl
|
||||
|
||||
dnl
|
||||
dnl Hardwares
|
||||
dnl
|
||||
|
||||
AM_CONDITIONAL([AMD64], [[[[ $host_cpu = *x86_64* ]]]])
|
||||
AM_CONDITIONAL([X86_64], [[[[ $host_cpu = *x86_64* ]]]])
|
||||
|
||||
AM_CONDITIONAL([ARM64], [[[[ $host_cpu = *aarch64* ]]]])
|
||||
AM_CONDITIONAL([AARCH64], [[[[ $host_cpu = *aarch64* ]]]])
|
||||
|
||||
dnl
|
||||
dnl Compiler brand
|
||||
dnl
|
||||
|
@ -769,16 +779,20 @@ AM_COND_IF([GCC],
|
|||
[
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["-fstack-protector-explicit"])
|
||||
|
||||
dnl These flags should not be used on Intel-CET capable platforms
|
||||
dnl TODO: XXX
|
||||
AS_IF([test "$CXX_EPOCH" -ge "9"],
|
||||
AS_CASE([$host_cpu],
|
||||
[x86_64],
|
||||
[
|
||||
AM_COND_IF([GENERIC],
|
||||
dnl These flags should not be used on Intel-CET capable platforms
|
||||
dnl TODO: XXX
|
||||
AS_IF([test "$CXX_EPOCH" -ge "9"],
|
||||
[
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["-fcf-protection=full"])
|
||||
], [
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["-fcf-protection=none"])
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["-mmanual-endbr"])
|
||||
AM_COND_IF([GENERIC],
|
||||
[
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["-fcf-protection=return"])
|
||||
], [
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["-fcf-protection=none"])
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["-mmanual-endbr"])
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
|
@ -797,10 +811,22 @@ MACHINE_FLAGS=""
|
|||
dnl Initial machine
|
||||
machine_arch="native"
|
||||
machine_tune="native"
|
||||
|
||||
AM_COND_IF([CLANG],
|
||||
[
|
||||
AS_IF([test "$CXX_EPOCH" -lt "15"],
|
||||
[
|
||||
AS_IF([test "$host_cpu" = "aarch64"],
|
||||
[
|
||||
machine_arch=""
|
||||
])
|
||||
])
|
||||
])
|
||||
|
||||
AM_COND_IF([GENERIC],
|
||||
[
|
||||
machine_tune="generic"
|
||||
AS_CASE([$target_cpu],
|
||||
AS_CASE([$host_cpu],
|
||||
[x86_64],
|
||||
[
|
||||
machine_arch="x86-64"
|
||||
|
@ -816,7 +842,7 @@ for feature in $machine; do
|
|||
machine_tune=$(echo $feature | cut -d"=" -f2)
|
||||
else
|
||||
machine_feat="$feature $machine_feat"
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], ["-m${feature}"])
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], [-m${feature}])
|
||||
fi
|
||||
done
|
||||
|
||||
|
@ -834,18 +860,23 @@ AM_COND_IF_NOT([GENERIC],
|
|||
])
|
||||
|
||||
dnl Specific extension underrides
|
||||
AS_CASE([$target_cpu],
|
||||
AS_CASE([$host_cpu],
|
||||
[x86_64],
|
||||
[
|
||||
dnl AMD K10's SSE4a doesn't work with valgrind
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], ["-mno-sse4a"])
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], [-mno-sse4a])
|
||||
])
|
||||
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], ["-mtune=$machine_tune"])
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], ["-march=$machine_arch"])
|
||||
if [[ ! -z "$machine_tune" ]]; then
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], [-mtune=$machine_tune])
|
||||
fi
|
||||
|
||||
machine_tuning="${target_cpu} arch=$machine_arch tune=$machine_tune :$machine_feat"
|
||||
RB_VAR_PREPEND([CXXFLAGS], ["$MACHINE_FLAGS"])
|
||||
if [[ ! -z "$machine_arch" ]]; then
|
||||
RB_VAR_PREPEND([MACHINE_FLAGS], [-march=$machine_arch])
|
||||
fi
|
||||
|
||||
machine_tuning="${host_cpu} arch=$machine_arch tune=$machine_tune :$machine_feat"
|
||||
RB_VAR_PREPEND([CXXFLAGS], [$MACHINE_FLAGS])
|
||||
|
||||
dnl Get the target features for this build from gcc into a readable string
|
||||
AM_COND_IF([GCC],
|
||||
|
@ -957,6 +988,7 @@ dnl Compiler specific
|
|||
AM_COND_IF([CLANG],
|
||||
[
|
||||
RB_MAYBE_CWARN([-Werror=return-stack-address], charybdis_cv_c_gcc_w_error_return_stack_address)
|
||||
RB_MAYBE_CWARN([-Werror=abstract-final-class], charybdis_cv_c_gcc_w_error_abstract_final_class)
|
||||
|
||||
RB_MAYBE_CWARN([-Wundefined-reinterpret-cast], charybdis_cv_c_gcc_w_undefined_reinterpret_cast)
|
||||
RB_MAYBE_CWARN([-Wconditional-uninitialized], charybdis_cv_c_gcc_w_conditional_uninitialized)
|
||||
|
|
|
@ -10,12 +10,11 @@ FROM ${acct}/${repo}:${dist_name}-${dist_version}-${feature}-${machine}
|
|||
ARG cc
|
||||
ARG cxx
|
||||
ARG extra_packages_dev
|
||||
ARG extra_packages_dev1
|
||||
ARG extra_packages_dev2
|
||||
ARG rocksdb_version 7.4.3
|
||||
ARG rocksdb_url
|
||||
ARG ctor_url https://github.com/matrix-construct/construct
|
||||
ARG machine_spec
|
||||
ARG nprocs
|
||||
|
||||
ENV CC ${cc}
|
||||
ENV CXX ${cxx}
|
||||
|
@ -25,6 +24,7 @@ ENV rocksdb_version ${rocksdb_version}
|
|||
ENV rocksdb_url https://codeload.github.com/facebook/rocksdb/tar.gz/refs/tags/v${rocksdb_version}
|
||||
ENV ctor_url ${ctor_url}
|
||||
ENV machine_spec ${machine_spec}
|
||||
ENV nprocs ${nprocs}
|
||||
|
||||
ENV packages_dev="\
|
||||
${packages_dev} \
|
||||
|
@ -38,8 +38,6 @@ curl \
|
|||
git \
|
||||
libtool \
|
||||
${extra_packages_dev} \
|
||||
${extra_packages_dev1} \
|
||||
${extra_packages_dev2} \
|
||||
"
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
@ -55,7 +53,7 @@ RUN true \
|
|||
&& cd /usr/src/construct \
|
||||
&& ./autogen.sh \
|
||||
&& (./configure --disable-iou --enable-generic --with-machine="${machine_spec}" || (cat config.log; exit 1)) \
|
||||
&& make -j `nproc` EXTRA_LDFLAGS="-Wl,--strip-all" install \
|
||||
&& make -j ${nprocs} EXTRA_LDFLAGS="-Wl,--strip-all" install \
|
||||
&& rm -rf /usr/src/rocksdb \
|
||||
&& rm -rf /usr/src/construct \
|
||||
&& rm -rf /usr/include/ircd \
|
||||
|
|
|
@ -1,32 +1,72 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Sundry configuration
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
ACCT=jevolk
|
||||
REPO=construct
|
||||
CTOR_URL="https://github.com/matrix-construct/construct"
|
||||
stage=$1
|
||||
mode=$2
|
||||
|
||||
export DOCKER_BUILDKIT=1
|
||||
#export BUILDKIT_PROGRESS=plain
|
||||
if test "$CI" = true; then
|
||||
export BUILDKIT_PROGRESS="plain"
|
||||
echo "plain"
|
||||
fi
|
||||
|
||||
features="base full"
|
||||
distros="ubuntu-22.04 ubuntu-22.10 alpine-3.16 alpine-3.17"
|
||||
machines="arm64 amd64 amd64-avx amd64-avx2 amd64-avx512"
|
||||
toolchains="gcc-10 gcc-11 gcc-12 clang-14 clang-15"
|
||||
stages="built test"
|
||||
# The stage argument can be "base" "full" "built" "test" or "push"
|
||||
default_stage="push"
|
||||
stage=${stage:=$default_stage}
|
||||
|
||||
# The mode argument can be "real" or "test"
|
||||
default_mode="real"
|
||||
mode=${mode:=$default_mode}
|
||||
|
||||
if test "$ctor_test" = true; then
|
||||
mode="test"
|
||||
fi
|
||||
|
||||
# Account configuration environment.
|
||||
#
|
||||
# Override these for yourself.
|
||||
|
||||
default_ctor_url="https://github.com/jevolk/charybdis"
|
||||
default_ctor_id="jevolk/construct"
|
||||
|
||||
ctor_url=${ctor_url:=$default_ctor_url}
|
||||
ctor_id=${ctor_id:=$default_ctor_id}
|
||||
ctor_acct=${ctor_acct:=$(echo $ctor_id | cut -d"/" -f1)}
|
||||
ctor_repo=${ctor_repo:=$(echo $ctor_id | cut -d"/" -f2)}
|
||||
|
||||
# Job matrix configuration environment
|
||||
#
|
||||
# All combinations of these arrays will be run by this script. Overriding
|
||||
# these with one element for each variable allows this script to do one thing
|
||||
# at a time.
|
||||
|
||||
default_ctor_features="base full"
|
||||
default_ctor_distros="ubuntu-22.04 ubuntu-22.10 alpine-3.17"
|
||||
default_ctor_machines="amd64 amd64-avx amd64-avx512"
|
||||
default_ctor_toolchains="gcc-10 gcc-11 gcc-12 clang-14"
|
||||
|
||||
ctor_features=${ctor_features:=$default_ctor_features}
|
||||
ctor_distros=${ctor_distros:=$default_ctor_distros}
|
||||
ctor_machines=${ctor_machines:=$default_ctor_machines}
|
||||
ctor_toolchains=${ctor_toolchains:=$default_ctor_toolchains}
|
||||
|
||||
###############################################################################
|
||||
|
||||
matrix()
|
||||
{
|
||||
for feature_ in $features; do
|
||||
for distro_ in $distros; do
|
||||
for machine_ in $machines; do
|
||||
for toolchain_ in $toolchains; do
|
||||
for stage_ in $stages; do
|
||||
build $feature_ $distro_ $machine_ $toolchain_ $stage_
|
||||
done
|
||||
for ctor_feature in $ctor_features; do
|
||||
for ctor_distro in $ctor_distros; do
|
||||
for ctor_machine in $ctor_machines; do
|
||||
for ctor_toolchain in $ctor_toolchains; do
|
||||
build $ctor_feature $ctor_distro $ctor_machine $ctor_toolchain
|
||||
if test $? -ne 0; then return 1; fi
|
||||
done
|
||||
done
|
||||
done
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
build()
|
||||
|
@ -35,42 +75,77 @@ build()
|
|||
distro=$2
|
||||
machine=$3
|
||||
toolchain=$4
|
||||
stage=$5
|
||||
|
||||
dist_name=$(echo $distro | cut -d"-" -f1)
|
||||
dist_version=$(echo $distro | cut -d"-" -f2)
|
||||
runner_name=$(echo $RUNNER_NAME | cut -d"." -f1)
|
||||
runner_num=$(echo $RUNNER_NAME | cut -d"." -f2)
|
||||
|
||||
args=""
|
||||
args="$ctor_docker_build_args"
|
||||
args="$args --compress=true"
|
||||
args="$args --build-arg acct=$ACCT"
|
||||
args="$args --build-arg repo=$REPO"
|
||||
args="$args --build-arg ctor_url=$CTOR_URL"
|
||||
|
||||
if test ! -z "$runner_num"; then
|
||||
cpu_num=$(expr $runner_num % $(nproc))
|
||||
args="$args --cpuset-cpus=${cpu_num}"
|
||||
args="$args --build-arg nprocs=1"
|
||||
# https://github.com/moby/buildkit/issues/1276
|
||||
else
|
||||
nprocs=$(nproc)
|
||||
args="$args --build-arg nprocs=${nprocs}"
|
||||
fi
|
||||
|
||||
args="$args --build-arg acct=${ctor_acct}"
|
||||
args="$args --build-arg repo=${ctor_repo}"
|
||||
args="$args --build-arg ctor_url=${ctor_url}"
|
||||
args="$args --build-arg dist_name=${dist_name}"
|
||||
args="$args --build-arg dist_version=${dist_version}"
|
||||
args="$args --build-arg feature=${feature}"
|
||||
args="$args --build-arg machine=${machine}"
|
||||
args="$args --build-arg feature=${feature}"
|
||||
|
||||
args_dist $dist_name $dist_version
|
||||
if test $? -ne 0; then return 1; fi
|
||||
|
||||
args_toolchain $toolchain $dist_name $dist_version
|
||||
if test $? -ne 0; then return 1; fi
|
||||
|
||||
args_machine $machine
|
||||
if test $? -ne 0; then return 1; fi
|
||||
|
||||
args_platform $machine
|
||||
if test $? -ne 0; then return 1; fi
|
||||
|
||||
# Intermediate stage build; usually cached from prior iteration.
|
||||
tag="$ACCT/$REPO:${distro}-${feature}-${machine}"
|
||||
cmd="$args -t $tag $BASEDIR/${dist_name}/${feature}"
|
||||
docker build $cmd
|
||||
if test $toolchain != "false"; then
|
||||
args_toolchain $toolchain $dist_name $dist_version
|
||||
if test $? -ne 0; then return 1; fi
|
||||
fi
|
||||
|
||||
if test $mode = "test"; then
|
||||
cmd=$(which echo)
|
||||
else
|
||||
cmd=$(which docker)
|
||||
fi
|
||||
|
||||
# Intermediate stage build; usually cached from prior iteration.
|
||||
tag="$ctor_acct/$ctor_repo:${distro}-${feature}-${machine}"
|
||||
arg="$args -t $tag $BASEDIR/${dist_name}/${feature}"
|
||||
eval "$cmd build $arg"
|
||||
if test $? -ne 0; then return 1; fi
|
||||
if test $stage = $feature; then return 0; fi
|
||||
if test $toolchain = "false"; then return 0; fi
|
||||
|
||||
# Leaf build; unique to each iteration.
|
||||
tag="$ctor_acct/$ctor_repo:${distro}-${feature}-built-${toolchain}-${machine}"
|
||||
arg="$args --no-cache -t $tag $BASEDIR/${dist_name}/built"
|
||||
eval "$cmd build $arg"
|
||||
if test $? -ne 0; then return 1; fi
|
||||
if test $stage = "built"; then return 0; fi
|
||||
|
||||
# Test built;
|
||||
arg="$args $BASEDIR/${dist_name}/test"
|
||||
eval "$cmd build $arg"
|
||||
if test $? -ne 0; then return 1; fi
|
||||
if test $stage = "test"; then return 0; fi
|
||||
|
||||
# Push built
|
||||
eval "$cmd push $tag"
|
||||
if test $? -ne 0; then return 1; fi
|
||||
|
||||
# Leaf stage build; unique to each iteration.
|
||||
tag="$ACCT/$REPO:${distro}-${feature}-${stage}-${toolchain}-${machine}"
|
||||
cmd="$args -t $tag $BASEDIR/${dist_name}/${stage}"
|
||||
docker build $cmd
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -112,22 +187,24 @@ args_dist()
|
|||
args_toolchain()
|
||||
{
|
||||
_name=$(echo $1 | cut -d"-" -f1)
|
||||
_version=$(echo $1 | cut -d"-" -f2)
|
||||
_epoch=$(echo $1 | cut -d"-" -f2)
|
||||
|
||||
case $2 in
|
||||
alpine)
|
||||
toolchain=$_name
|
||||
case $1 in
|
||||
gcc*)
|
||||
args="$args --build-arg extra_packages_dev=gcc"
|
||||
args="$args --build-arg extra_packages_dev1=g++"
|
||||
extra_dev="gcc"
|
||||
extra_dev="${extra_dev} g++"
|
||||
args="$args --build-arg extra_packages_dev=\"${extra_dev}\""
|
||||
args="$args --build-arg cc=gcc --build-arg cxx=g++"
|
||||
return 0
|
||||
;;
|
||||
clang*)
|
||||
args="$args --build-arg extra_packages_dev=clang"
|
||||
args="$args --build-arg extra_packages_dev1=llvm"
|
||||
args="$args --build-arg extra_packages_dev2=llvm-dev"
|
||||
extra_dev="clang"
|
||||
extra_dev="${extra_dev} llvm"
|
||||
extra_dev="${extra_dev} llvm-dev"
|
||||
args="$args --build-arg extra_packages_dev=\"${extra_dev}\""
|
||||
args="$args --build-arg cc=clang --build-arg cxx=clang++"
|
||||
test $3 != "3.16"
|
||||
return $?
|
||||
|
@ -137,16 +214,25 @@ args_toolchain()
|
|||
ubuntu)
|
||||
case $1 in
|
||||
gcc*)
|
||||
args="$args --build-arg extra_packages_dev=gcc-${_version}"
|
||||
args="$args --build-arg extra_packages_dev1=g++-${_version}"
|
||||
args="$args --build-arg cc=gcc-${_version} --build-arg cxx=g++-${_version}"
|
||||
extra_dev="gcc-${_epoch}"
|
||||
extra_dev="${extra_dev} g++-${_epoch}"
|
||||
args="$args --build-arg extra_packages_dev=\"${extra_dev}\""
|
||||
args="$args --build-arg cc=gcc-${_epoch} --build-arg cxx=g++-${_epoch}"
|
||||
return 0
|
||||
;;
|
||||
clang*)
|
||||
args="$args --build-arg extra_packages_dev=clang-${_version}"
|
||||
args="$args --build-arg extra_packages_dev1=llvm-${_version}-dev"
|
||||
args="$args --build-arg extra_packages_dev2=llvm-spirv-${_version}"
|
||||
args="$args --build-arg cc=clang-${_version} --build-arg cxx=clang++-${_version}"
|
||||
if test "$_epoch" -ge 15; then
|
||||
extra="mesa-opencl-icd"
|
||||
extra="${extra} ocl-icd-opencl-dev"
|
||||
extra="${extra} libclc-${_epoch}-dev"
|
||||
args="$args --build-arg extra_packages=\"${extra}\""
|
||||
fi
|
||||
extra_dev="clang-${_epoch}"
|
||||
extra_dev="${extra_dev} llvm-${_epoch}"
|
||||
extra_dev="${extra_dev} llvm-spirv-${_epoch}"
|
||||
extra_dev="${extra_dev} libclc-${_epoch}-dev"
|
||||
args="$args --build-arg extra_packages_dev=\"${extra_dev}\""
|
||||
args="$args --build-arg cc=clang-${_epoch} --build-arg cxx=clang++-${_epoch}"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -10,6 +10,7 @@ ARG rocksdb_avx 0
|
|||
ARG rocksdb_avx2 0
|
||||
ARG boost_version 1.74
|
||||
ARG icu_version 70
|
||||
ARG nprocs 1
|
||||
|
||||
ENV rocksdb_version ${rocksdb_version}
|
||||
ENV rocksdb_url https://codeload.github.com/facebook/rocksdb/tar.gz/refs/tags/v${rocksdb_version}
|
||||
|
@ -17,6 +18,7 @@ ENV rocksdb_avx ${rocksdb_avx}
|
|||
ENV rocksdb_avx2 ${rocksdb_avx2}
|
||||
ENV boost_version ${boost_version}
|
||||
ENV icu_version ${icu_version}
|
||||
ENV nprocs ${nprocs}
|
||||
|
||||
ENV packages="\
|
||||
ca-certificates \
|
||||
|
@ -71,7 +73,9 @@ ENV rocksdb_cmake="\
|
|||
-DWITH_JNI=0 \
|
||||
-DWITH_TESTS=0 \
|
||||
-DWITH_BENCHMARK_TOOLS=0 \
|
||||
-DWITH_TRACE_TOOLS=0 \
|
||||
-DWITH_CORE_TOOLS=0 \
|
||||
-DWITH_TOOLS=0 \
|
||||
-DWITH_GFLAGS=0 \
|
||||
-DWITH_LIBURING=0 \
|
||||
-DWITH_JEMALLOC=1 \
|
||||
|
@ -99,7 +103,7 @@ ENV do_fetch_rocksdb true \
|
|||
&& eval ${do_install} curl \
|
||||
&& cd /usr/src \
|
||||
&& curl -sL ${rocksdb_url} -o rocksdb-${rocksdb_version}.tar.gz \
|
||||
&& tar xfvz rocksdb-${rocksdb_version}.tar.gz \
|
||||
&& tar xfz rocksdb-${rocksdb_version}.tar.gz \
|
||||
&& rm rocksdb-${rocksdb_version}.tar.gz \
|
||||
&& mv /usr/src/rocksdb-${rocksdb_version} /usr/src/rocksdb \
|
||||
&& cd - \
|
||||
|
@ -125,7 +129,7 @@ fi \
|
|||
CFLAGS="-g0 -ftls-model=initial-exec" \
|
||||
LDFLAGS="-Wl,--strip-all" \
|
||||
cmake -H. -Bbuild ${rocksdb_cmake} \
|
||||
&& cmake --build build --target install --parallel `nproc` \
|
||||
&& cmake --build build --target install --parallel ${nprocs} \
|
||||
&& rm -rf /usr/lib/$(uname -m)-linux-gnu/librocksdb.a \
|
||||
&& rm -rf /usr/src/rocksdb \
|
||||
&& eval ${do_purge} ${packages_rocksdb_dev} \
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
ARG distribution
|
||||
|
||||
FROM --platform=$TARGETPLATFORM $distribution
|
||||
|
||||
ARG skiprocks
|
||||
ARG rocksdb_version 7.4.5
|
||||
ARG rocksdb_url
|
||||
ARG rocksdb_portable 1
|
||||
ARG rocksdb_avx 0
|
||||
ARG rocksdb_avx2 0
|
||||
|
||||
ENV rocksdb_version ${rocksdb_version}
|
||||
ENV rocksdb_url https://codeload.github.com/facebook/rocksdb/tar.gz/refs/tags/v${rocksdb_version}
|
||||
ENV rocksdb_portable ${rocksdb_portable}
|
||||
ENV rocksdb_avx ${rocksdb_avx}
|
||||
ENV rocksdb_avx2 ${rocksdb_avx2}
|
||||
|
||||
ENV packages="\
|
||||
ca-certificates \
|
||||
libatomic1 \
|
||||
libjemalloc2 \
|
||||
libboost-chrono1.74.0 \
|
||||
libboost-context1.74.0 \
|
||||
libboost-coroutine1.74.0 \
|
||||
libboost-system1.74.0 \
|
||||
libboost-thread1.74.0 \
|
||||
libicu70 \
|
||||
libnss-db \
|
||||
libsodium23 \
|
||||
libssl3 \
|
||||
liblz4-1 \
|
||||
libmagic1 \
|
||||
libzstd1 \
|
||||
"
|
||||
|
||||
ENV packages_rocksdb_dev="\
|
||||
build-essential \
|
||||
cmake \
|
||||
libjemalloc-dev \
|
||||
liblz4-dev \
|
||||
libzstd-dev \
|
||||
xz-utils \
|
||||
"
|
||||
|
||||
ENV packages_dev="\
|
||||
${packages_rocksdb_dev} \
|
||||
libboost-chrono1.74-dev \
|
||||
libboost-context1.74-dev \
|
||||
libboost-coroutine1.74-dev \
|
||||
libboost-system1.74-dev \
|
||||
libboost-thread1.74-dev \
|
||||
libicu-dev \
|
||||
libmagic-dev \
|
||||
libsodium-dev \
|
||||
libssl-dev \
|
||||
"
|
||||
|
||||
ENV rocksdb_cmake="\
|
||||
-DCMAKE_RULE_MESSAGES:BOOL=OFF \
|
||||
-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=1 \
|
||||
-DFAIL_ON_WARNINGS=0 \
|
||||
-DUSE_RTTI=1 \
|
||||
-DPORTABLE=${rocksdb_portable} \
|
||||
-DFORCE_AVX=${rocksdb_avx} \
|
||||
-DFORCE_AVX2=${rocksdb_avx2} \
|
||||
-DWITH_JNI=0 \
|
||||
-DWITH_TESTS=0 \
|
||||
-DWITH_BENCHMARK_TOOLS=0 \
|
||||
-DWITH_CORE_TOOLS=0 \
|
||||
-DWITH_GFLAGS=0 \
|
||||
-DWITH_LIBURING=0 \
|
||||
-DWITH_JEMALLOC=1 \
|
||||
-DWITH_LZ4=1 \
|
||||
-DWITH_ZSTD=1 \
|
||||
"
|
||||
|
||||
ENV do_install true \
|
||||
&& export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y -f
|
||||
|
||||
ENV do_purge true \
|
||||
&& export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get purge -y
|
||||
|
||||
ENV do_clean true \
|
||||
&& export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get clean \
|
||||
&& apt-get autoremove --purge -y \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& true
|
||||
|
||||
ENV do_fetch_rocksdb true \
|
||||
&& eval ${do_install} curl \
|
||||
&& cd /usr/src \
|
||||
&& curl -sL ${rocksdb_url} -o rocksdb-${rocksdb_version}.tar.gz \
|
||||
&& tar xfvz rocksdb-${rocksdb_version}.tar.gz \
|
||||
&& rm rocksdb-${rocksdb_version}.tar.gz \
|
||||
&& mv /usr/src/rocksdb-${rocksdb_version} /usr/src/rocksdb \
|
||||
&& cd - \
|
||||
&& eval ${do_purge} curl \
|
||||
&& true
|
||||
|
||||
RUN true \
|
||||
&& eval ${do_install} ${packages} \
|
||||
&& update-ca-certificates \
|
||||
&& eval ${do_clean} \
|
||||
&& mkdir /build \
|
||||
&& true
|
||||
|
||||
WORKDIR /build
|
||||
RUN true \
|
||||
&& if test -n "$skiprocks"; then \
|
||||
exit 0; \
|
||||
fi \
|
||||
&& eval ${do_install} ${packages_rocksdb_dev} \
|
||||
&& eval ${do_fetch_rocksdb} \
|
||||
&& cd /usr/src/rocksdb \
|
||||
&& \
|
||||
CFLAGS="-g0 -ftls-model=initial-exec" \
|
||||
LDFLAGS="-Wl,--strip-all" \
|
||||
cmake -H. -Bbuild ${rocksdb_cmake} \
|
||||
&& cmake --build build --target install --parallel `nproc` \
|
||||
&& rm -rf /usr/lib/$(uname -m)-linux-gnu/librocksdb.a \
|
||||
&& rm -rf /usr/src/rocksdb \
|
||||
&& eval ${do_purge} ${packages_rocksdb_dev} \
|
||||
&& eval ${do_clean} \
|
||||
&& true
|
|
@ -10,15 +10,15 @@ FROM ${acct}/${repo}:${dist_name}-${dist_version}-${feature}-${machine}
|
|||
ARG cc
|
||||
ARG cxx
|
||||
ARG extra_packages_dev
|
||||
ARG extra_packages_dev1
|
||||
ARG extra_packages_dev2
|
||||
ARG ctor_url https://github.com/matrix-construct/construct
|
||||
ARG machine_spec
|
||||
ARG nprocs
|
||||
|
||||
ENV CC ${cc}
|
||||
ENV CXX ${cxx}
|
||||
ENV ctor_url ${ctor_url}
|
||||
ENV machine_spec ${machine_spec}
|
||||
ENV nprocs ${nprocs}
|
||||
|
||||
ENV packages_dev="\
|
||||
${packages_dev} \
|
||||
|
@ -31,8 +31,6 @@ git \
|
|||
libtool \
|
||||
shtool \
|
||||
${extra_packages_dev} \
|
||||
${extra_packages_dev1} \
|
||||
${extra_packages_dev2} \
|
||||
"
|
||||
|
||||
RUN true \
|
||||
|
@ -43,8 +41,8 @@ RUN true \
|
|||
&& rmdir -v deps/rocksdb \
|
||||
&& ln -sv /usr/src/rocksdb deps \
|
||||
&& ./autogen.sh \
|
||||
&& ./configure --enable-generic --with-machine="${machine_spec}" \
|
||||
&& make -j `nproc` \
|
||||
&& (./configure --enable-generic --with-machine="${machine_spec}" || (cat config.log; exit 1)) \
|
||||
&& make -j ${nprocs} \
|
||||
&& make install \
|
||||
&& cd .. \
|
||||
&& rm -rf construct \
|
||||
|
|
|
@ -6,11 +6,12 @@ ARG machine
|
|||
|
||||
FROM ${acct}/${repo}:${dist_name}-${dist_version}-base-${machine}
|
||||
|
||||
ARG extra_packages
|
||||
|
||||
ENV packages="\
|
||||
libgraphicsmagick-q16-3 \
|
||||
libpng16-16 \
|
||||
mesa-opencl-icd \
|
||||
ocl-icd-opencl-dev \
|
||||
${extra_packages} \
|
||||
"
|
||||
|
||||
ENV packages_dev="\
|
||||
|
|
|
@ -32,6 +32,7 @@ struct ircd::buffer::unique_buffer
|
|||
unique_buffer() = default;
|
||||
unique_buffer(const size_t &size, const size_t &align = 0);
|
||||
explicit unique_buffer(const const_buffer &);
|
||||
template<class T> unique_buffer(unique_buffer<T> &&) noexcept;
|
||||
unique_buffer(unique_buffer &&) noexcept;
|
||||
unique_buffer(const unique_buffer &) = delete;
|
||||
unique_buffer &operator=(unique_buffer &&) & noexcept;
|
||||
|
@ -71,6 +72,19 @@ ircd::buffer::unique_buffer<buffer_type>::unique_buffer(const size_t &size,
|
|||
}
|
||||
{}
|
||||
|
||||
template<class buffer_type>
|
||||
template<class other_type>
|
||||
inline
|
||||
ircd::buffer::unique_buffer<buffer_type>::unique_buffer(unique_buffer<other_type> &&other)
|
||||
noexcept
|
||||
:buffer_type
|
||||
{
|
||||
other.release()
|
||||
}
|
||||
{
|
||||
assert(std::get<0>(other) == nullptr);
|
||||
}
|
||||
|
||||
template<class buffer_type>
|
||||
inline
|
||||
ircd::buffer::unique_buffer<buffer_type>::unique_buffer(unique_buffer &&other)
|
||||
|
|
|
@ -46,6 +46,8 @@ struct ircd::ctx::future
|
|||
|
||||
bool valid() const { return !is(state(), future_state::INVALID); }
|
||||
bool operator!() const { return !valid(); }
|
||||
std::exception_ptr eptr() const { return state().eptr; }
|
||||
string_view what() const { return util::what(eptr()); }
|
||||
explicit operator T() { return get(); }
|
||||
|
||||
template<class U, class time_point> friend bool wait_until(const future<U> &, const time_point &, std::nothrow_t);
|
||||
|
@ -75,6 +77,8 @@ struct ircd::ctx::future<void>
|
|||
|
||||
bool valid() const { return !is(state(), future_state::INVALID); }
|
||||
bool operator!() const { return !valid(); }
|
||||
std::exception_ptr eptr() const { return state().eptr; }
|
||||
string_view what() const { return util::what(eptr()); }
|
||||
|
||||
template<class U, class time_point> friend bool wait_until(const future<U> &, const time_point &, std::nothrow_t);
|
||||
template<class U, class time_point> friend void wait_until(const future<U> &, const time_point &);
|
||||
|
|
|
@ -102,3 +102,11 @@ const
|
|||
{
|
||||
return &this->operator*();
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::db::seek(domain::const_iterator_base &it,
|
||||
const string_view &p)
|
||||
{
|
||||
it.opts.prefix = true;
|
||||
return seek(static_cast<column::const_iterator_base &>(it), p);
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
#include "mods/mods.h"
|
||||
#include "net/net.h"
|
||||
#include "server/server.h"
|
||||
#include "rest.h"
|
||||
#include "png.h"
|
||||
#include "beep.h"
|
||||
#include "magick.h"
|
||||
|
|
|
@ -39,20 +39,7 @@ namespace ircd::json
|
|||
#include "strung.h"
|
||||
#include "tuple/tuple.h"
|
||||
#include "stack/stack.h"
|
||||
|
||||
// Convenience toolset for higher level operations.
|
||||
namespace ircd::json
|
||||
{
|
||||
strung append(const array &, const string_view &val);
|
||||
strung prepend(const array &, const string_view &val);
|
||||
|
||||
void merge(stack::object &out, const vector &);
|
||||
strung remove(const object &, const string_view &key);
|
||||
strung remove(const object &, const size_t &index);
|
||||
strung insert(const object &, const member &);
|
||||
strung replace(const object &, const member &);
|
||||
strung replace(const object &, const members &);
|
||||
}
|
||||
#include "tool.h"
|
||||
|
||||
// Exports to ircd::
|
||||
namespace ircd
|
||||
|
|
|
@ -41,6 +41,10 @@ struct ircd::json::stack::member
|
|||
member(stack &s, const string_view &name, const json::value &);
|
||||
template<class... T> member(object &po, const string_view &name, const json::tuple<T...> &t);
|
||||
template<class... T> member(stack &s, const string_view &name, const json::tuple<T...> &t);
|
||||
explicit member(object &, const json::object::member &);
|
||||
explicit member(stack &, const json::object::member &);
|
||||
explicit member(object &, const json::member &);
|
||||
explicit member(stack &, const json::member &);
|
||||
member() = default;
|
||||
member(const member &) = delete;
|
||||
member(member &&) noexcept;
|
||||
|
|
|
@ -105,28 +105,28 @@ template<>
|
|||
inline ircd::json::stack::array &
|
||||
ircd::json::stack::stack::top<ircd::json::stack::array>(stack &s)
|
||||
{
|
||||
return array::top(s);
|
||||
return array::top(s);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline const ircd::json::stack::array &
|
||||
ircd::json::stack::stack::top<ircd::json::stack::array>(const stack &s)
|
||||
{
|
||||
return array::top(s);
|
||||
return array::top(s);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline ircd::json::stack::object &
|
||||
ircd::json::stack::stack::top<ircd::json::stack::object>(stack &s)
|
||||
{
|
||||
return object::top(s);
|
||||
return object::top(s);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline const ircd::json::stack::object &
|
||||
ircd::json::stack::stack::top<ircd::json::stack::object>(const stack &s)
|
||||
{
|
||||
return object::top(s);
|
||||
return object::top(s);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
|
44
include/ircd/json/tool.h
Normal file
44
include/ircd/json/tool.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_JSON_TOOL_H
|
||||
|
||||
// Convenience toolset for higher level operations.
|
||||
namespace ircd::json
|
||||
{
|
||||
strung append(const array &, const string_view &val);
|
||||
strung prepend(const array &, const string_view &val);
|
||||
|
||||
void merge(stack::object &out, const vector &);
|
||||
|
||||
strung remove(const object &, const string_view &key);
|
||||
strung remove(const object &, const size_t &index);
|
||||
|
||||
strung insert(const object &, const members &);
|
||||
strung insert(const object &, const member &);
|
||||
|
||||
strung replace(const object &, const members &);
|
||||
strung replace(const object &, const member &);
|
||||
}
|
||||
|
||||
inline ircd::json::strung
|
||||
ircd::json::replace(const object &s,
|
||||
const json::member &m)
|
||||
{
|
||||
return replace(s, json::members{m});
|
||||
}
|
||||
|
||||
inline ircd::json::strung
|
||||
ircd::json::insert(const object &s,
|
||||
const json::member &m)
|
||||
{
|
||||
return insert(s, json::members{m});
|
||||
}
|
|
@ -29,26 +29,18 @@ namespace ircd::json
|
|||
/// not use the strict overload.
|
||||
IRCD_OVERLOAD(strict)
|
||||
|
||||
// Determine the type
|
||||
enum type type(const string_view &);
|
||||
enum type type(const string_view &, std::nothrow_t);
|
||||
enum type type(const string_view &, strict_t);
|
||||
enum type type(const string_view &, strict_t, std::nothrow_t);
|
||||
|
||||
// Query if type
|
||||
bool type(const string_view &, const enum type &, strict_t);
|
||||
bool type(const string_view &, const enum type &);
|
||||
|
||||
// Utils
|
||||
string_view reflect(const enum type &);
|
||||
[[gnu::pure]] string_view reflect(const enum type) noexcept;
|
||||
|
||||
extern const string_view literal_null;
|
||||
extern const string_view literal_true;
|
||||
extern const string_view literal_false;
|
||||
extern const string_view empty_string;
|
||||
extern const string_view empty_object;
|
||||
extern const string_view empty_array;
|
||||
extern const int64_t undefined_number;
|
||||
// Determine the type w/ strict correctness (full scan)
|
||||
[[gnu::pure]] bool type(const string_view &, const enum type, strict_t) noexcept;
|
||||
[[gnu::pure]] enum type type(const string_view &, strict_t, std::nothrow_t) noexcept;
|
||||
enum type type(const string_view &, strict_t);
|
||||
|
||||
// Determine the type quickly
|
||||
[[gnu::pure]] bool type(const string_view &, const enum type) noexcept;
|
||||
[[gnu::pure]] enum type type(const string_view &, std::nothrow_t) noexcept;
|
||||
enum type type(const string_view &);
|
||||
}
|
||||
|
||||
enum ircd::json::type
|
||||
|
|
|
@ -36,7 +36,15 @@ namespace ircd::json
|
|||
void valid(const string_view &);
|
||||
std::string why(const string_view &);
|
||||
|
||||
struct stats extern stats;
|
||||
extern const string_view literal_null;
|
||||
extern const string_view literal_true;
|
||||
extern const string_view literal_false;
|
||||
extern const string_view empty_string;
|
||||
extern const string_view empty_object;
|
||||
extern const string_view empty_array;
|
||||
extern const int64_t undefined_number;
|
||||
|
||||
extern struct stats stats;
|
||||
}
|
||||
|
||||
/// Statistics counter access; unfortunately these cannot participate as
|
||||
|
|
|
@ -53,11 +53,14 @@ struct ircd::m::signing_key_update
|
|||
/// Required. The user ID whose cross-signing keys have changed.
|
||||
json::property<name::user_id, json::string>,
|
||||
|
||||
/// Cross signing key
|
||||
/// Master signing key
|
||||
json::property<name::master_key, json::object>,
|
||||
|
||||
/// Cross signing key
|
||||
json::property<name::self_signing_key, json::object>
|
||||
/// Self signing key
|
||||
json::property<name::self_signing_key, json::object>,
|
||||
|
||||
/// User signing key (local only)
|
||||
json::property<name::user_signing_key, json::object>
|
||||
>
|
||||
{
|
||||
using super_type::tuple;
|
||||
|
|
|
@ -29,6 +29,29 @@ struct ircd::m::event::append
|
|||
{
|
||||
struct opts;
|
||||
|
||||
private:
|
||||
static const event::keys::exclude exclude_keys;
|
||||
static const event::keys default_keys;
|
||||
static conf::item<std::string> exclude_types;
|
||||
static conf::item<bool> info;
|
||||
static log::log log;
|
||||
|
||||
bool is_ignored(const event &, const opts &) const;
|
||||
bool is_redacted(const event &, const opts &) const;
|
||||
bool is_invisible(const event &, const opts &) const;
|
||||
bool is_excluded(const event &, const opts &) const;
|
||||
|
||||
bool bundle_replace(json::stack::object &, const event &, const opts &);
|
||||
void _relations(json::stack::object &, const event &, const opts &);
|
||||
void _age(json::stack::object &, const event &, const opts &);
|
||||
void _txnid(json::stack::object &, const event &, const opts &);
|
||||
void _prev_state(json::stack::object &, const event &, const opts &);
|
||||
void _unsigned(json::stack::object &, const event &, const opts &);
|
||||
|
||||
bool members(json::stack::object &, const event &, const opts &);
|
||||
bool object(json::stack::array &, const event &, const opts &);
|
||||
|
||||
public:
|
||||
append(json::stack::object &, const event &, const opts &);
|
||||
append(json::stack::object &, const event &);
|
||||
append(json::stack::array &, const event &, const opts &);
|
||||
|
@ -39,11 +62,11 @@ struct ircd::m::event::append
|
|||
/// can provide the best result.
|
||||
struct ircd::m::event::append::opts
|
||||
{
|
||||
const event::idx *event_idx {nullptr};
|
||||
const string_view *client_txnid {nullptr};
|
||||
const id::user *user_id {nullptr};
|
||||
const room *user_room {nullptr};
|
||||
const int64_t *room_depth {nullptr};
|
||||
event::idx event_idx {0};
|
||||
string_view client_txnid;
|
||||
id::user user_id;
|
||||
id::room user_room_id;
|
||||
int64_t room_depth {-1L};
|
||||
const event::keys *keys {nullptr};
|
||||
const m::event_filter *event_filter {nullptr};
|
||||
long age {std::numeric_limits<long>::min()};
|
||||
|
@ -51,6 +74,8 @@ struct ircd::m::event::append::opts
|
|||
bool query_prev_state {true};
|
||||
bool query_redacted {true};
|
||||
bool query_visible {false};
|
||||
bool bundle_all {false};
|
||||
bool bundle_replace {false};
|
||||
};
|
||||
|
||||
inline
|
||||
|
@ -64,3 +89,23 @@ ircd::m::event::append::append(json::stack::object &o,
|
|||
const event &e)
|
||||
:append{o, e, {}}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::event::append::append(json::stack::array &array,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
:returns<bool>{[this, &array, &event, &opts]
|
||||
{
|
||||
return object(array, event, opts);
|
||||
}}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::event::append::append(json::stack::object &object,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
:returns<bool>{[this, &object, &event, &opts]
|
||||
{
|
||||
return members(object, event, opts);
|
||||
}}
|
||||
{}
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace ircd::m
|
|||
#include "direct_to_device.h"
|
||||
#include "visible.h"
|
||||
#include "redacted.h"
|
||||
#include "replaced.h"
|
||||
#include "feds.h"
|
||||
#include "app.h"
|
||||
#include "bridge.h"
|
||||
|
|
|
@ -221,4 +221,5 @@ struct ircd::m::name
|
|||
static constexpr const char *const usage {"usage"};
|
||||
static constexpr const char *const master_key {"master_key"};
|
||||
static constexpr const char *const self_signing_key {"self_signing_key"};
|
||||
static constexpr const char *const user_signing_key {"user_signing_key"};
|
||||
};
|
||||
|
|
|
@ -44,9 +44,11 @@ struct ircd::m::relates
|
|||
const event::idx &, const json::object &, const m::relates_to &
|
||||
>;
|
||||
|
||||
static conf::item<std::string> latest_column;
|
||||
|
||||
event::refs refs;
|
||||
bool match_sender {false};
|
||||
bool prefetch_depth {false};
|
||||
bool prefetch_latest {false};
|
||||
bool prefetch_sender {false};
|
||||
|
||||
private:
|
||||
|
|
80
include/ircd/m/replaced.h
Normal file
80
include/ircd/m/replaced.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_M_REPLACED_H
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
struct replaced;
|
||||
}
|
||||
|
||||
class ircd::m::replaced
|
||||
:public returns<event::idx>
|
||||
{
|
||||
m::relates relates;
|
||||
|
||||
public:
|
||||
IRCD_OVERLOAD(latest);
|
||||
|
||||
replaced(const event::idx &, latest_t);
|
||||
replaced(const event::idx &);
|
||||
|
||||
replaced(const event::id &, latest_t);
|
||||
replaced(const event::id &);
|
||||
|
||||
explicit replaced(const event &, latest_t);
|
||||
explicit replaced(const event &);
|
||||
};
|
||||
|
||||
inline
|
||||
ircd::m::replaced::replaced(const event &event)
|
||||
:replaced{event.event_id}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::replaced::replaced(const event &event,
|
||||
latest_t)
|
||||
:replaced{event.event_id, latest}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::replaced::replaced(const event::id &event_id)
|
||||
:replaced{index(std::nothrow, event_id)}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::replaced::replaced(const event::id &event_id,
|
||||
latest_t)
|
||||
:replaced{index(std::nothrow, event_id), latest}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::replaced::replaced(const event::idx &event_idx)
|
||||
:relates
|
||||
{
|
||||
.refs = event_idx,
|
||||
.match_sender = true,
|
||||
}
|
||||
{
|
||||
this->returns::ret = relates.has("m.replace")? -1UL: 0UL;
|
||||
}
|
||||
|
||||
inline
|
||||
ircd::m::replaced::replaced(const event::idx &event_idx,
|
||||
latest_t)
|
||||
:relates
|
||||
{
|
||||
.refs = event_idx,
|
||||
.match_sender = true,
|
||||
}
|
||||
{
|
||||
this->returns::ret = relates.latest("m.replace");
|
||||
}
|
|
@ -31,6 +31,7 @@ namespace ircd::m
|
|||
bool exists(const id::room_alias &, const bool &remote = false);
|
||||
bool internal(const id::room &);
|
||||
bool federated(const id::room &);
|
||||
bool type(const id::room &, const string_view &);
|
||||
bool creator(const id::room &, const id::user &);
|
||||
bool contains(const id::room &, const event::idx &);
|
||||
bool membership(const room &, const id::user &, const string_view & = "join");
|
||||
|
@ -186,9 +187,11 @@ struct ircd::m::room
|
|||
const vm::copts *const &copts,
|
||||
const event::fetch::opts *const &fopts = nullptr) noexcept;
|
||||
|
||||
room(const id &room_id = {},
|
||||
room(const id &room_id,
|
||||
const event::fetch::opts *const &fopts = nullptr) noexcept;
|
||||
|
||||
room() = default;
|
||||
|
||||
// Index of create event
|
||||
static event::idx index(const id &, std::nothrow_t);
|
||||
static event::idx index(const id &);
|
||||
|
@ -228,7 +231,9 @@ ircd::m::room::room(const id &room_id,
|
|||
,event_id{event_id? event::id{event_id} : event::id{}}
|
||||
,copts{copts}
|
||||
,fopts{fopts}
|
||||
{}
|
||||
{
|
||||
assert(room_id);
|
||||
}
|
||||
|
||||
inline
|
||||
ircd::m::room::room(const id &room_id,
|
||||
|
@ -238,7 +243,9 @@ noexcept
|
|||
:room_id{room_id}
|
||||
,copts{copts}
|
||||
,fopts{fopts}
|
||||
{}
|
||||
{
|
||||
assert(room_id);
|
||||
}
|
||||
|
||||
inline
|
||||
ircd::m::room::room(const id &room_id,
|
||||
|
@ -246,7 +253,9 @@ ircd::m::room::room(const id &room_id,
|
|||
noexcept
|
||||
:room_id{room_id}
|
||||
,fopts{fopts}
|
||||
{}
|
||||
{
|
||||
assert(room_id);
|
||||
}
|
||||
|
||||
inline ircd::m::room::operator
|
||||
const ircd::m::room::id &()
|
||||
|
|
|
@ -48,6 +48,9 @@ struct ircd::m::rooms::opts
|
|||
/// Room alias prefix search
|
||||
string_view room_alias;
|
||||
|
||||
/// Room type search
|
||||
string_view room_type;
|
||||
|
||||
/// user::rooms convenience
|
||||
id::user user_id;
|
||||
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
|
||||
struct ircd::m::user::devices
|
||||
{
|
||||
struct send;
|
||||
|
||||
using closure = std::function<void (const event::idx &, const string_view &)>;
|
||||
using closure_bool = std::function<bool (const event::idx &, const string_view &)>;
|
||||
using closure_bool = util::function_bool<const event::idx &, const string_view &>;
|
||||
|
||||
m::user user;
|
||||
|
||||
|
@ -36,9 +38,17 @@ struct ircd::m::user::devices
|
|||
///TODO: XXX junk
|
||||
static std::map<std::string, long> count_one_time_keys(const m::user &, const string_view &);
|
||||
static bool update(const device_list_update &);
|
||||
static bool send(json::iov &content);
|
||||
|
||||
devices(const m::user &user)
|
||||
:user{user}
|
||||
{}
|
||||
};
|
||||
|
||||
/// Broadcast m.device_list_update.
|
||||
///
|
||||
struct ircd::m::user::devices::send
|
||||
{
|
||||
send(const m::user::devices &,
|
||||
const m::id::device &,
|
||||
const string_view = {});
|
||||
};
|
||||
|
|
128
include/ircd/m/user/keys.h
Normal file
128
include/ircd/m/user/keys.h
Normal file
|
@ -0,0 +1,128 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_M_USER_KEYS_H
|
||||
|
||||
struct ircd::m::user::keys
|
||||
{
|
||||
struct send;
|
||||
|
||||
static string_view make_sigs_state_key(const mutable_buffer &, const string_view &tgt, const string_view &src);
|
||||
static std::tuple<string_view, string_view> unmake_sigs_state_key(const string_view &) noexcept;
|
||||
|
||||
m::user::room user_room;
|
||||
|
||||
void attach_sigs(json::stack::object &, const json::object &, const user::id &) const;
|
||||
bool attach_sigs(json::stack::object &, const event::idx &, const user::id &) const;
|
||||
void append_sigs(json::stack::object &, const json::object &, const user::id &) const;
|
||||
void append_keys(json::stack::object &, const json::object &, const user::id &) const;
|
||||
bool append_keys(json::stack::object &, const event::idx &, const user::id &) const;
|
||||
|
||||
public:
|
||||
bool has_device(const string_view &) const;
|
||||
bool has_cross(const string_view &type) const;
|
||||
bool has_cross_master() const;
|
||||
bool has_cross_self() const;
|
||||
bool has_cross_user() const;
|
||||
|
||||
void device(json::stack::object &, const string_view &device_id) const;
|
||||
void cross(json::stack::object &, const string_view &type) const;
|
||||
void cross_master(json::stack::object &) const;
|
||||
void cross_self(json::stack::object &) const;
|
||||
void cross_user(json::stack::object &) const;
|
||||
|
||||
bool claim(json::stack::object &, const string_view &device_id, const string_view &algo) const;
|
||||
void update(const m::signing_key_update &) const;
|
||||
|
||||
keys(const m::user &user)
|
||||
:user_room{user}
|
||||
{}
|
||||
};
|
||||
|
||||
struct ircd::m::user::keys::send
|
||||
{
|
||||
send(const m::user::keys &,
|
||||
const string_view = {});
|
||||
};
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross_user(json::stack::object &out)
|
||||
const
|
||||
{
|
||||
return cross(out, "ircd.cross_signing.user");
|
||||
}
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross_self(json::stack::object &out)
|
||||
const
|
||||
{
|
||||
return cross(out, "ircd.cross_signing.self");
|
||||
}
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross_master(json::stack::object &out)
|
||||
const
|
||||
{
|
||||
return cross(out, "ircd.cross_signing.master");
|
||||
}
|
||||
|
||||
inline void
|
||||
ircd::m::user::keys::cross(json::stack::object &out,
|
||||
const string_view &type)
|
||||
const
|
||||
{
|
||||
const auto event_idx
|
||||
{
|
||||
user_room.get(std::nothrow, type, "")
|
||||
};
|
||||
|
||||
append_keys(out, event_idx, user_room.user.user_id);
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross_user()
|
||||
const
|
||||
{
|
||||
return has_cross("ircd.cross_signing.user");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross_self()
|
||||
const
|
||||
{
|
||||
return has_cross("ircd.cross_signing.self");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross_master()
|
||||
const
|
||||
{
|
||||
return has_cross("ircd.cross_signing.master");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_cross(const string_view &type)
|
||||
const
|
||||
{
|
||||
return user_room.has(type, "");
|
||||
}
|
||||
|
||||
inline bool
|
||||
ircd::m::user::keys::has_device(const string_view &device_id)
|
||||
const
|
||||
{
|
||||
const m::user::devices devices
|
||||
{
|
||||
user_room.user
|
||||
};
|
||||
|
||||
return devices.has(device_id, "keys");
|
||||
}
|
|
@ -52,6 +52,7 @@ struct ircd::m::user
|
|||
struct tokens;
|
||||
struct devices;
|
||||
struct reading;
|
||||
struct keys;
|
||||
|
||||
using id = m::id::user;
|
||||
using closure = std::function<void (const user &)>;
|
||||
|
@ -73,13 +74,17 @@ struct ircd::m::user
|
|||
event::id::buf deoper();
|
||||
event::id::buf oper();
|
||||
|
||||
user(const id &user_id)
|
||||
:user_id{user_id}
|
||||
{}
|
||||
|
||||
user(const id &user_id);
|
||||
user() = default;
|
||||
};
|
||||
|
||||
inline
|
||||
ircd::m::user::user(const id &user_id)
|
||||
:user_id{user_id}
|
||||
{
|
||||
assert(user_id);
|
||||
}
|
||||
|
||||
inline ircd::m::user::operator
|
||||
const ircd::m::user::id &()
|
||||
const
|
||||
|
@ -105,3 +110,4 @@ const
|
|||
#include "tokens.h"
|
||||
#include "devices.h"
|
||||
#include "reading.h"
|
||||
#include "keys.h"
|
||||
|
|
|
@ -132,11 +132,11 @@ ircd::net::dns::tag::tag(const hostport &hp,
|
|||
{
|
||||
this->hp.host =
|
||||
{
|
||||
hostbuf, copy(hostbuf, hp.host)
|
||||
tolower(hostbuf, hp.host)
|
||||
};
|
||||
|
||||
this->hp.service =
|
||||
{
|
||||
servicebuf, copy(servicebuf, hp.service)
|
||||
tolower(servicebuf, hp.service)
|
||||
};
|
||||
}
|
||||
|
|
211
include/ircd/rest.h
Normal file
211
include/ircd/rest.h
Normal file
|
@ -0,0 +1,211 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
#define HAVE_IRCD_REST_H
|
||||
|
||||
/// Simple highlevel interface for web/http requests.
|
||||
///
|
||||
/// Prior to this it was too difficult to orchestrate all the objects and
|
||||
/// buffers and low-level non-ergonomic procedures split between ircd::http
|
||||
/// and ircd::server. This should instead have some familiarity to the
|
||||
/// browser-js environment which developers can easily commit to their memory.
|
||||
namespace ircd::rest
|
||||
{
|
||||
struct opts;
|
||||
struct request;
|
||||
|
||||
struct get;
|
||||
struct put;
|
||||
struct post;
|
||||
}
|
||||
|
||||
struct ircd::rest::request
|
||||
:returns<string_view>
|
||||
{
|
||||
unique_const_buffer out;
|
||||
|
||||
request(const mutable_buffer &out, const rfc3986::uri &, opts);
|
||||
request(const rfc3986::uri &, opts);
|
||||
};
|
||||
|
||||
struct ircd::rest::get
|
||||
:request
|
||||
{
|
||||
get(const mutable_buffer &out, const rfc3986::uri &, opts);
|
||||
get(const mutable_buffer &out, const rfc3986::uri &);
|
||||
get(const rfc3986::uri &, opts);
|
||||
};
|
||||
|
||||
struct ircd::rest::put
|
||||
:request
|
||||
{
|
||||
put(const mutable_buffer &out, const rfc3986::uri &, const string_view &content, opts);
|
||||
put(const rfc3986::uri &, const string_view &content, opts);
|
||||
};
|
||||
|
||||
struct ircd::rest::post
|
||||
:request
|
||||
{
|
||||
post(const mutable_buffer &out, const rfc3986::uri &, const string_view &content, opts);
|
||||
post(const mutable_buffer &out, const rfc3986::uri &, opts);
|
||||
post(const rfc3986::uri &, const string_view &content, opts);
|
||||
post(const rfc3986::uri &, opts);
|
||||
};
|
||||
|
||||
struct ircd::rest::opts
|
||||
{
|
||||
/// The HTTP method to use. This is overridden and should not be set unless
|
||||
/// using the generic rest::request() call where it must be set.
|
||||
string_view method;
|
||||
|
||||
/// The HTTP request body. This is overridden and should not be set unless
|
||||
/// using the generic rest::request() call where it's set as needed.
|
||||
string_view content;
|
||||
|
||||
/// The HTTP request body content-type. It is a good idea to set this when
|
||||
/// there is request body content.
|
||||
string_view content_type;
|
||||
|
||||
/// Additional request headers to send. These are pairs of string_views.
|
||||
vector_view<const http::header> headers;
|
||||
|
||||
/// This is set automatically from the URI argument's domain and scheme
|
||||
/// (service) by default. Setting it here will override.
|
||||
net::hostport remote;
|
||||
|
||||
/// Managed internally by default and passed to server::request. Setting
|
||||
/// things here will override.
|
||||
server::out sout;
|
||||
|
||||
/// Managed internally by default and passed to server::request. Setting
|
||||
/// things here will override.
|
||||
server::in sin;
|
||||
|
||||
/// Passed to server::request. The http_exceptions option is useful here
|
||||
/// to prevent this suite from throwing on non-2xx codes.
|
||||
server::request::opts sopts;
|
||||
|
||||
/// Allows the HTTP response code to be returned to the caller. This may
|
||||
/// not be initialized if the call throws any error first.
|
||||
http::code *code {nullptr};
|
||||
|
||||
/// Allows the user to override the request::out with their own for
|
||||
/// receiving dynamic content. Supply an empty unique_buffer instance.
|
||||
unique_const_buffer *out {nullptr};
|
||||
|
||||
/// Optionally supply the temporary buffer for headers in/out in lieu of
|
||||
/// any internally allocated.
|
||||
mutable_buffer buf;
|
||||
|
||||
/// Timeout for the yielding/synchronous calls of this interface.
|
||||
seconds timeout {20s};
|
||||
|
||||
/// Internal use
|
||||
opts &&set(const string_view &method, const string_view &content = {});
|
||||
};
|
||||
|
||||
inline
|
||||
ircd::rest::post::post(const rfc3986::uri &uri,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
uri, opts.set("POST")
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::post::post(const rfc3986::uri &uri,
|
||||
const string_view &content,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
uri, opts.set("POST", content)
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::post::post(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
out, uri, opts.set("POST")
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::post::post(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri,
|
||||
const string_view &content,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
out, uri, opts.set("POST", content)
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::put::put(const rfc3986::uri &uri,
|
||||
const string_view &content,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
uri, opts.set("PUT", content)
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::put::put(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri,
|
||||
const string_view &content,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
out, uri, opts.set("PUT", content)
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::get::get(const rfc3986::uri &uri,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
uri, opts.set("GET")
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::get::get(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri)
|
||||
:get
|
||||
{
|
||||
out, uri, opts{}
|
||||
}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::rest::get::get(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri,
|
||||
opts opts)
|
||||
:request
|
||||
{
|
||||
out, uri, opts.set("GET")
|
||||
}
|
||||
{}
|
||||
|
||||
inline ircd::rest::opts &&
|
||||
ircd::rest::opts::set(const string_view &method,
|
||||
const string_view &content)
|
||||
{
|
||||
this->method = method;
|
||||
this->content = content?: this->content;
|
||||
return std::move(*this);
|
||||
}
|
|
@ -70,6 +70,7 @@ struct ircd::rfc3986::uri
|
|||
string_view path;
|
||||
string_view query;
|
||||
string_view fragment;
|
||||
string_view resource() const; // path and query string as one
|
||||
|
||||
uri(const string_view &);
|
||||
uri() = default;
|
||||
|
@ -179,3 +180,12 @@ namespace ircd::rfc3986
|
|||
void valid_remote(const string_view &);
|
||||
bool valid_remote(std::nothrow_t, const string_view &);
|
||||
}
|
||||
|
||||
inline ircd::string_view
|
||||
ircd::rfc3986::uri::resource()
|
||||
const
|
||||
{
|
||||
return query?
|
||||
string_view(begin(path), end(query)):
|
||||
path;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace ircd::simd
|
|||
/// T = inner aligned type
|
||||
template<class T>
|
||||
struct
|
||||
[[using clang: internal_linkage, nodebug]]
|
||||
[[using clang: internal_linkage]]
|
||||
[[using gnu: packed, aligned(1), visibility("internal")]]
|
||||
ircd::simd::unaligned
|
||||
{
|
||||
|
|
|
@ -107,6 +107,7 @@ constexpr void
|
|||
ircd::util::bitset<N>::set(const size_t pos,
|
||||
const bool val)
|
||||
{
|
||||
reset(pos);
|
||||
buf[byte(pos)] |= word(val) << bit(pos);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,16 +73,9 @@ struct ircd::versions
|
|||
~versions() noexcept;
|
||||
};
|
||||
|
||||
namespace ircd
|
||||
{
|
||||
template<>
|
||||
decltype(versions::allocator)
|
||||
instance_list<versions>::allocator;
|
||||
|
||||
template<>
|
||||
decltype(versions::list)
|
||||
instance_list<versions>::list;
|
||||
}
|
||||
template<>
|
||||
decltype(ircd::versions::list)
|
||||
ircd::instance_list<ircd::versions>::list;
|
||||
|
||||
inline ircd::versions::operator
|
||||
const long &()
|
||||
|
|
|
@ -237,6 +237,7 @@ endif
|
|||
libircd_la_SOURCES += server.cc
|
||||
libircd_la_SOURCES += client.cc
|
||||
libircd_la_SOURCES += resource.cc
|
||||
libircd_la_SOURCES += rest.cc
|
||||
if JS
|
||||
libircd_la_SOURCES += js.cc
|
||||
endif
|
||||
|
@ -435,6 +436,8 @@ gpt_gpu.spv.cc: gpt_gpu.spv
|
|||
# GCN-HSA
|
||||
#
|
||||
|
||||
if AMD64
|
||||
|
||||
GCN_HSA_TARGET = amdgcn--amdhsa
|
||||
|
||||
GCN_HSA_CPPFLAGS = $(GPU_CPPFLAGS)
|
||||
|
@ -469,10 +472,14 @@ libircd_la_SOURCES += gpt_gpu.gcn_hsa.bc.cc
|
|||
gpt_gpu.gcn_hsa.bc.cc: gpt_gpu.gcn_hsa.bc
|
||||
xxd -i $^ $@
|
||||
|
||||
endif # AMD64
|
||||
|
||||
#
|
||||
# R600
|
||||
#
|
||||
|
||||
if AMD64
|
||||
|
||||
R600_TARGET = r600--
|
||||
|
||||
#
|
||||
|
@ -510,6 +517,8 @@ libircd_la_SOURCES += gpt_gpu.r600_barts.bc.cc
|
|||
gpt_gpu.r600_barts.bc.cc: gpt_gpu.r600_barts.bc
|
||||
xxd -i $^ $@
|
||||
|
||||
endif # AMD64
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
|
27
ircd/cl.cc
27
ircd/cl.cc
|
@ -192,8 +192,8 @@ ircd::cl::envs
|
|||
{ "default", "true" },
|
||||
},
|
||||
{
|
||||
{ "name", "MESA_GLSL_CACHE_DISABLE" },
|
||||
{ "default", "true" },
|
||||
{ "name", "MESA_SHADER_CACHE_DISABLE" },
|
||||
{ "default", "true" },
|
||||
},
|
||||
{
|
||||
{ "name", "AMD_DEBUG" },
|
||||
|
@ -355,6 +355,7 @@ ircd::cl::init::fini_libs()
|
|||
|
||||
size_t
|
||||
ircd::cl::init::init_platforms()
|
||||
try
|
||||
{
|
||||
// OpenCL sez platform=null is implementation defined.
|
||||
constexpr auto ignore(CL_INVALID_PLATFORM);
|
||||
|
@ -365,9 +366,21 @@ ircd::cl::init::init_platforms()
|
|||
|
||||
return platforms;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::logf
|
||||
{
|
||||
log, log::level::DERROR,
|
||||
"OpenCL platforms initialization :%s",
|
||||
e.what(),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::cl::init::init_devices()
|
||||
try
|
||||
{
|
||||
// Get the devices.
|
||||
size_t devices_total(0);
|
||||
|
@ -414,6 +427,16 @@ ircd::cl::init::init_devices()
|
|||
|
||||
return devices_total;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
{
|
||||
log, "OpenCL devices initialization :%s",
|
||||
e.what(),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::cl::init::init_ctxs()
|
||||
|
|
122
ircd/db.cc
122
ircd/db.cc
|
@ -2358,15 +2358,20 @@ ircd::db::seek(domain::const_iterator_base &it,
|
|||
{
|
||||
switch(p)
|
||||
{
|
||||
// This is inefficient as per RocksDB's prefix impl.
|
||||
case pos::BACK:
|
||||
{
|
||||
// This is inefficient as per RocksDB's prefix impl. unknown why
|
||||
// a seek to NEXT is still needed after walking back one.
|
||||
char buf[512];
|
||||
string_view key;
|
||||
assert(bool(it)); do
|
||||
{
|
||||
assert(size(it.it->key()) <= sizeof(buf));
|
||||
key = string_view(buf, copy(buf, slice(it.it->key())));
|
||||
}
|
||||
while(seek(it, pos::NEXT));
|
||||
if(seek(it, pos::PREV))
|
||||
seek(it, pos::NEXT);
|
||||
|
||||
return bool(it);
|
||||
assert(key);
|
||||
return seek(it, key);
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -2377,14 +2382,6 @@ ircd::db::seek(domain::const_iterator_base &it,
|
|||
return seek(static_cast<column::const_iterator_base &>(it), p);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::db::seek(domain::const_iterator_base &it,
|
||||
const string_view &p)
|
||||
{
|
||||
it.opts.prefix = true;
|
||||
return seek(static_cast<column::const_iterator_base &>(it), p);
|
||||
}
|
||||
|
||||
ircd::db::domain::const_iterator
|
||||
ircd::db::domain::begin(const string_view &key,
|
||||
gopts opts)
|
||||
|
@ -5011,24 +5008,23 @@ ircd::db::_seek(const vector_view<_read_op> &op,
|
|||
namespace ircd::db
|
||||
{
|
||||
static rocksdb::Iterator &_seek_(rocksdb::Iterator &, const pos &);
|
||||
static rocksdb::Iterator &_seek_(rocksdb::Iterator &, const string_view &);
|
||||
static rocksdb::Iterator &_seek_lower_(rocksdb::Iterator &, const string_view &);
|
||||
static rocksdb::Iterator &_seek_upper_(rocksdb::Iterator &, const string_view &);
|
||||
static bool _seek(database::column &, const pos &, const rocksdb::ReadOptions &, rocksdb::Iterator &it);
|
||||
static bool _seek(database::column &, const string_view &, const rocksdb::ReadOptions &, rocksdb::Iterator &it);
|
||||
static rocksdb::Iterator &_seek_(rocksdb::Iterator &, const string_view &, const bool lte);
|
||||
static bool _seek(database::column &, const pos &, const rocksdb::ReadOptions &, rocksdb::Iterator &it, const bool lte);
|
||||
static bool _seek(database::column &, const string_view &, const rocksdb::ReadOptions &, rocksdb::Iterator &it, const bool lte);
|
||||
}
|
||||
|
||||
std::unique_ptr<rocksdb::Iterator>
|
||||
ircd::db::seek(column &column,
|
||||
const string_view &key,
|
||||
const gopts &opts)
|
||||
const gopts &opts,
|
||||
const bool lte)
|
||||
{
|
||||
database &d(column);
|
||||
database::column &c(column);
|
||||
|
||||
std::unique_ptr<rocksdb::Iterator> ret;
|
||||
const auto ropts(make_opts(opts));
|
||||
seek(c, key, ropts, ret);
|
||||
seek(c, key, ropts, ret, lte);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -5037,7 +5033,8 @@ bool
|
|||
ircd::db::seek(database::column &c,
|
||||
const pos &p,
|
||||
const rocksdb::ReadOptions &opts,
|
||||
std::unique_ptr<rocksdb::Iterator> &it)
|
||||
std::unique_ptr<rocksdb::Iterator> &it,
|
||||
const bool lte)
|
||||
{
|
||||
const ctx::uninterruptible ui;
|
||||
const ctx::stack_usage_assertion sua;
|
||||
|
@ -5049,31 +5046,33 @@ ircd::db::seek(database::column &c,
|
|||
it.reset(d.d->NewIterator(opts, cf));
|
||||
}
|
||||
|
||||
return _seek(c, p, opts, *it);
|
||||
return _seek(c, p, opts, *it, lte);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::db::_seek(database::column &c,
|
||||
const string_view &p,
|
||||
const rocksdb::ReadOptions &opts,
|
||||
rocksdb::Iterator &it)
|
||||
rocksdb::Iterator &it,
|
||||
const bool lte)
|
||||
try
|
||||
{
|
||||
util::timer timer{util::timer::nostart};
|
||||
if constexpr(RB_DEBUG_DB_SEEK)
|
||||
timer = util::timer{};
|
||||
|
||||
_seek_(it, p);
|
||||
_seek_(it, p, lte);
|
||||
|
||||
database &d(*c.d);
|
||||
if constexpr(RB_DEBUG_DB_SEEK)
|
||||
log::debug
|
||||
{
|
||||
log, "[%s] %lu:%lu SEEK %s %s in %ld$us '%s'",
|
||||
log, "[%s] %lu:%lu SEEK[%s] %s %s in %ld$us '%s'",
|
||||
name(d),
|
||||
sequence(d),
|
||||
sequence(opts.snapshot),
|
||||
valid(it)? "VALID" : "INVALID",
|
||||
lte? "LTE"_sv: "GTE"_sv,
|
||||
valid(it)? "VALID"_sv: "INVALID"_sv,
|
||||
it.status().ok()? "OK"s: it.status().ToString(),
|
||||
timer.at<microseconds>().count(),
|
||||
name(c)
|
||||
|
@ -5086,11 +5085,12 @@ catch(const error &e)
|
|||
const database &d(*c.d);
|
||||
log::critical
|
||||
{
|
||||
log, "[%s][%s] %lu:%lu SEEK key :%s",
|
||||
log, "[%s][%s] %lu:%lu SEEK[%s] key :%s",
|
||||
name(d),
|
||||
name(c),
|
||||
sequence(d),
|
||||
sequence(opts.snapshot),
|
||||
lte? "LTE"_sv: "GTE"_sv,
|
||||
e.what(),
|
||||
};
|
||||
|
||||
|
@ -5101,7 +5101,8 @@ bool
|
|||
ircd::db::_seek(database::column &c,
|
||||
const pos &p,
|
||||
const rocksdb::ReadOptions &opts,
|
||||
rocksdb::Iterator &it)
|
||||
rocksdb::Iterator &it,
|
||||
const bool)
|
||||
try
|
||||
{
|
||||
bool valid_it;
|
||||
|
@ -5123,7 +5124,7 @@ try
|
|||
sequence(d),
|
||||
sequence(opts.snapshot),
|
||||
reflect(p),
|
||||
valid_it? "VALID" : "INVALID",
|
||||
valid_it? "VALID"_sv: "INVALID"_sv,
|
||||
it.status().ok()? "OK"s: it.status().ToString(),
|
||||
timer.at<microseconds>().count(),
|
||||
name(c)
|
||||
|
@ -5142,41 +5143,27 @@ catch(const error &e)
|
|||
sequence(d),
|
||||
sequence(opts.snapshot),
|
||||
reflect(p),
|
||||
it.Valid()? "VALID" : "INVALID",
|
||||
it.Valid()? "VALID"_sv: "INVALID"_sv,
|
||||
e.what(),
|
||||
};
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
/// Seek to entry NOT GREATER THAN key. That is, equal to or less than key
|
||||
rocksdb::Iterator &
|
||||
ircd::db::_seek_lower_(rocksdb::Iterator &it,
|
||||
const string_view &sv)
|
||||
{
|
||||
assert(!ctx::interruptible());
|
||||
|
||||
it.SeekForPrev(slice(sv));
|
||||
return it;
|
||||
}
|
||||
|
||||
/// Seek to entry NOT LESS THAN key. That is, equal to or greater than key
|
||||
rocksdb::Iterator &
|
||||
ircd::db::_seek_upper_(rocksdb::Iterator &it,
|
||||
const string_view &sv)
|
||||
{
|
||||
assert(!ctx::interruptible());
|
||||
|
||||
it.Seek(slice(sv));
|
||||
return it;
|
||||
}
|
||||
|
||||
/// Defaults to _seek_upper_ because it has better support from RocksDB.
|
||||
rocksdb::Iterator &
|
||||
ircd::db::_seek_(rocksdb::Iterator &it,
|
||||
const string_view &sv)
|
||||
const string_view &sv,
|
||||
const bool lte)
|
||||
{
|
||||
return _seek_upper_(it, sv);
|
||||
assert(!ctx::interruptible());
|
||||
|
||||
if(lte)
|
||||
it.SeekForPrev(slice(sv));
|
||||
else
|
||||
it.Seek(slice(sv));
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
rocksdb::Iterator &
|
||||
|
@ -5187,11 +5174,24 @@ ircd::db::_seek_(rocksdb::Iterator &it,
|
|||
|
||||
switch(p)
|
||||
{
|
||||
case pos::NEXT: it.Next(); break;
|
||||
case pos::PREV: it.Prev(); break;
|
||||
case pos::FRONT: it.SeekToFirst(); break;
|
||||
case pos::BACK: it.SeekToLast(); break;
|
||||
default:
|
||||
case pos::NEXT:
|
||||
assert(valid(it));
|
||||
it.Next();
|
||||
break;
|
||||
|
||||
case pos::PREV:
|
||||
assert(valid(it));
|
||||
it.Prev();
|
||||
break;
|
||||
|
||||
case pos::FRONT:
|
||||
it.SeekToFirst();
|
||||
break;
|
||||
|
||||
case pos::BACK:
|
||||
it.SeekToLast();
|
||||
break;
|
||||
|
||||
case pos::END:
|
||||
{
|
||||
it.SeekToLast();
|
||||
|
@ -5200,6 +5200,10 @@ ircd::db::_seek_(rocksdb::Iterator &it,
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return it;
|
||||
|
|
13
ircd/db.h
13
ircd/db.h
|
@ -134,10 +134,8 @@ namespace ircd::db
|
|||
static void valid_eq_or_throw(const rocksdb::Iterator &, const string_view &);
|
||||
|
||||
// [GET] iterator seek suite
|
||||
template<class pos> static bool seek(database::column &, const pos &, const rocksdb::ReadOptions &, std::unique_ptr<rocksdb::Iterator> &it);
|
||||
static std::unique_ptr<rocksdb::Iterator> seek(column &, const gopts &);
|
||||
static std::unique_ptr<rocksdb::Iterator> seek(column &, const string_view &key, const gopts &);
|
||||
static std::vector<row::value_type> seek(database &, const gopts &);
|
||||
template<class pos> static bool seek(database::column &, const pos &, const rocksdb::ReadOptions &, std::unique_ptr<rocksdb::Iterator> &it, const bool lte = false);
|
||||
static std::unique_ptr<rocksdb::Iterator> seek(column &, const string_view &key, const gopts &, const bool lte = false);
|
||||
static std::pair<string_view, string_view> operator*(const rocksdb::Iterator &);
|
||||
|
||||
// [GET] read suite
|
||||
|
@ -226,7 +224,9 @@ ircd::db::database::cache final
|
|||
#else
|
||||
Status Insert(const Slice &key, void *value, size_t charge, deleter, Handle **, Priority) noexcept override;
|
||||
#endif
|
||||
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER
|
||||
#if defined(IRCD_DB_HAS_CACHE_ASYNC)
|
||||
Handle *Lookup(const Slice &key, const CacheItemHelper *, CreateContext *, Priority, Statistics *) noexcept override;
|
||||
#elif defined(IRCD_DB_HAS_CACHE_ITEMHELPER)
|
||||
Handle *Lookup(const Slice &key, const CacheItemHelper *, CreateContext *, Priority, bool, Statistics *) noexcept override;
|
||||
#else
|
||||
Handle *Lookup(const Slice &key, Statistics *) noexcept override;
|
||||
|
@ -265,6 +265,9 @@ ircd::db::database::cache final
|
|||
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER
|
||||
const CacheItemHelper *GetCacheItemHelper(Handle *) const noexcept override;
|
||||
#endif
|
||||
#ifdef IRCD_DB_HAS_CACHE_ASYNC
|
||||
Handle *CreateStandalone(const Slice &, ObjectPtr, const CacheItemHelper *, size_t, bool) noexcept override;
|
||||
#endif
|
||||
|
||||
cache(database *const &,
|
||||
std::shared_ptr<struct database::stats>,
|
||||
|
|
|
@ -3321,7 +3321,14 @@ noexcept
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER
|
||||
#if defined(IRCD_DB_HAS_CACHE_ASYNC)
|
||||
rocksdb::Cache::Handle *
|
||||
ircd::db::database::cache::Lookup(const Slice &key,
|
||||
const CacheItemHelper *const helper,
|
||||
CreateContext *const cc,
|
||||
Priority pri,
|
||||
Statistics *const statistics)
|
||||
#elif defined(IRCD_DB_HAS_CACHE_ITEMHELPER)
|
||||
rocksdb::Cache::Handle *
|
||||
ircd::db::database::cache::Lookup(const Slice &key,
|
||||
const CacheItemHelper *const helper,
|
||||
|
@ -3355,7 +3362,9 @@ noexcept
|
|||
|
||||
auto *const &ret
|
||||
{
|
||||
#ifdef IRCD_DB_HAS_CACHE_ITEMHELPER
|
||||
#if defined(IRCD_DB_HAS_CACHE_ASYNC)
|
||||
c->Lookup(key, helper, cc, pri, statistics)
|
||||
#elif defined(IRCD_DB_HAS_CACHE_ITEMHELPER)
|
||||
c->Lookup(key, helper, cc, pri, wait, statistics)
|
||||
#else
|
||||
c->Lookup(key, s)
|
||||
|
@ -3559,6 +3568,20 @@ const noexcept
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(IRCD_DB_HAS_CACHE_ASYNC)
|
||||
rocksdb::Cache::Handle *
|
||||
ircd::db::database::cache::CreateStandalone(const Slice &key,
|
||||
ObjectPtr ptr,
|
||||
const CacheItemHelper *const helper,
|
||||
size_t charge,
|
||||
bool allow_uncharged)
|
||||
noexcept
|
||||
{
|
||||
assert(bool(c));
|
||||
return c->CreateStandalone(key, ptr, helper, charge, allow_uncharged);
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// database::compaction_filter
|
||||
|
|
|
@ -193,3 +193,9 @@
|
|||
|| (ROCKSDB_MAJOR == 8 && ROCKSDB_MINOR == 0 && ROCKSDB_PATCH >= 0)
|
||||
#define IRCD_DB_HAS_CACHE_WRAPPER
|
||||
#endif
|
||||
|
||||
#if ROCKSDB_MAJOR > 8 \
|
||||
|| (ROCKSDB_MAJOR == 8 && ROCKSDB_MINOR > 1) \
|
||||
|| (ROCKSDB_MAJOR == 8 && ROCKSDB_MINOR == 1 && ROCKSDB_PATCH >= 1)
|
||||
#define IRCD_DB_HAS_CACHE_ASYNC
|
||||
#endif
|
||||
|
|
|
@ -151,7 +151,7 @@ namespace ircd::http::parser
|
|||
const rule<string_view> reason { str ,"reason" };
|
||||
|
||||
const rule<string_view> head_key { raw[+(char_ - (illegal | ws | colon))] ,"head key" };
|
||||
const rule<string_view> head_val { str ,"head value" };
|
||||
const rule<string_view> head_val { -str ,"head value" };
|
||||
const rule<http::header> header { head_key >> *ws >> colon >> *ws >> head_val ,"header" };
|
||||
const rule<> headers { header % (*ws >> CRLF) ,"headers" };
|
||||
|
||||
|
|
93
ircd/json.cc
93
ircd/json.cc
|
@ -604,11 +604,11 @@ ircd::json::replace(const object &s,
|
|||
{
|
||||
[](const json::members &r, const object::member &m)
|
||||
{
|
||||
return std::any_of(begin(r), end(r), [&m]
|
||||
(const json::member &r)
|
||||
{
|
||||
return string_view{r.first} == m.first;
|
||||
});
|
||||
for(const auto &[k, v] : r)
|
||||
if(string_view{k} == m.first)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -634,33 +634,9 @@ ircd::json::replace(const object &s,
|
|||
};
|
||||
}
|
||||
|
||||
ircd::json::strung
|
||||
ircd::json::replace(const object &s,
|
||||
const json::member &m_)
|
||||
{
|
||||
if(unlikely(!empty(s) && type(s) != type::OBJECT))
|
||||
throw type_error
|
||||
{
|
||||
"Cannot replace member into JSON of type %s",
|
||||
reflect(type(s))
|
||||
};
|
||||
|
||||
size_t mctr {0};
|
||||
auto &mb(member_buffer);
|
||||
for(const object::member &m : object{s})
|
||||
if(m.first != string_view{m_.first})
|
||||
mb.at(mctr++) = member{m};
|
||||
|
||||
mb.at(mctr++) = m_;
|
||||
return strung
|
||||
{
|
||||
mb.data(), mb.data() + mctr
|
||||
};
|
||||
}
|
||||
|
||||
ircd::json::strung
|
||||
ircd::json::insert(const object &s,
|
||||
const json::member &m)
|
||||
const json::members &m)
|
||||
{
|
||||
if(unlikely(!empty(s) && type(s) != type::OBJECT))
|
||||
throw type_error
|
||||
|
@ -674,7 +650,9 @@ ircd::json::insert(const object &s,
|
|||
for(const object::member &m : object{s})
|
||||
mb.at(mctr++) = member{m};
|
||||
|
||||
mb.at(mctr++) = m;
|
||||
for(const auto &_m : m)
|
||||
mb.at(mctr++) = _m;
|
||||
|
||||
return strung
|
||||
{
|
||||
mb.data(), mb.data() + mctr
|
||||
|
@ -1734,6 +1712,42 @@ ircd::json::stack::member::member(object &po,
|
|||
s->append(string_view{tmp, size_t(data(buf) - tmp)});
|
||||
}
|
||||
|
||||
ircd::json::stack::member::member(stack &s,
|
||||
const json::member &m)
|
||||
:member
|
||||
{
|
||||
stack::top<object>(s), m
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::json::stack::member::member(object &po,
|
||||
const json::member &m)
|
||||
:member
|
||||
{
|
||||
po, string_view{m.first}, m.second
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::json::stack::member::member(stack &s,
|
||||
const json::object::member &om)
|
||||
:member
|
||||
{
|
||||
stack::top<object>(s), om
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::json::stack::member::member(object &po,
|
||||
const json::object::member &om)
|
||||
:member
|
||||
{
|
||||
po, om.first, om.second
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::json::stack::member::member(stack &s,
|
||||
const string_view &name,
|
||||
const json::value &value)
|
||||
|
@ -4754,7 +4768,8 @@ namespace ircd::json::parser
|
|||
|
||||
bool
|
||||
ircd::json::type(const string_view &buf,
|
||||
const enum type &type)
|
||||
const enum type type)
|
||||
noexcept
|
||||
{
|
||||
const bool ret
|
||||
{
|
||||
|
@ -4780,6 +4795,7 @@ ircd::json::type(const string_view &buf)
|
|||
enum ircd::json::type
|
||||
ircd::json::type(const string_view &buf,
|
||||
std::nothrow_t)
|
||||
noexcept
|
||||
{
|
||||
enum type ret;
|
||||
if(!parser::parse(begin(buf), end(buf), parser::type_parse, ret))
|
||||
|
@ -4842,8 +4858,9 @@ namespace ircd::json::parser
|
|||
|
||||
bool
|
||||
ircd::json::type(const string_view &buf,
|
||||
const enum type &type,
|
||||
const enum type type,
|
||||
strict_t)
|
||||
noexcept
|
||||
{
|
||||
const bool ret
|
||||
{
|
||||
|
@ -4871,6 +4888,7 @@ enum ircd::json::type
|
|||
ircd::json::type(const string_view &buf,
|
||||
strict_t,
|
||||
std::nothrow_t)
|
||||
noexcept
|
||||
{
|
||||
enum type ret;
|
||||
if(!parser::parse(begin(buf), end(buf), parser::type_parse_strict, ret))
|
||||
|
@ -4880,7 +4898,8 @@ ircd::json::type(const string_view &buf,
|
|||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::json::reflect(const enum type &type)
|
||||
ircd::json::reflect(const enum type type)
|
||||
noexcept
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
@ -4891,8 +4910,6 @@ ircd::json::reflect(const enum type &type)
|
|||
case STRING: return "STRING";
|
||||
}
|
||||
|
||||
throw type_error
|
||||
{
|
||||
"Unknown type %x", uint(type)
|
||||
};
|
||||
assert(false);
|
||||
return "STRING";
|
||||
}
|
||||
|
|
|
@ -191,10 +191,10 @@ bool
|
|||
ircd::net::dns::cache::operator==(const waiter &a, const waiter &b)
|
||||
noexcept
|
||||
{
|
||||
return
|
||||
a.opts.qtype == b.opts.qtype &&
|
||||
a.key && b.key &&
|
||||
a.key == b.key;
|
||||
return true
|
||||
&& a.opts.qtype == b.opts.qtype
|
||||
&& a.key && b.key
|
||||
&& a.key == b.key;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -227,7 +227,7 @@ ircd::net::dns::cache::waiter::waiter(const hostport &hp,
|
|||
{
|
||||
opts.qtype == 33?
|
||||
make_SRV_key(keybuf, hp, opts):
|
||||
strlcpy(keybuf, host(hp))
|
||||
tolower(keybuf, host(hp))
|
||||
}
|
||||
{
|
||||
this->opts.srv = {};
|
||||
|
|
162
ircd/rest.cc
Normal file
162
ircd/rest.cc
Normal file
|
@ -0,0 +1,162 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
ircd::rest::request::request(const rfc3986::uri &uri,
|
||||
opts opts)
|
||||
{
|
||||
if(!opts.remote)
|
||||
opts.remote = net::hostport{uri};
|
||||
|
||||
const bool need_alloc
|
||||
{
|
||||
empty(opts.buf)
|
||||
&& (empty(opts.sout.head) || empty(opts.sin.head))
|
||||
};
|
||||
|
||||
const unique_mutable_buffer _buf
|
||||
{
|
||||
need_alloc? 16_KiB: 0_KiB
|
||||
};
|
||||
|
||||
if(!empty(_buf))
|
||||
opts.buf = _buf;
|
||||
|
||||
window_buffer window
|
||||
{
|
||||
opts.buf
|
||||
};
|
||||
|
||||
if(empty(opts.sout.head))
|
||||
{
|
||||
assert(opts.remote);
|
||||
assert(opts.method);
|
||||
http::request
|
||||
{
|
||||
window,
|
||||
host(opts.remote),
|
||||
opts.method,
|
||||
uri.resource(),
|
||||
size(opts.content),
|
||||
opts.content_type,
|
||||
opts.headers
|
||||
};
|
||||
|
||||
opts.sout.head = window.completed();
|
||||
}
|
||||
|
||||
if(empty(opts.sout.content))
|
||||
opts.sout.content = opts.content;
|
||||
|
||||
if(empty(opts.sin.head))
|
||||
opts.sin.head = mutable_buffer{window};
|
||||
|
||||
// server::request will allocate dynamic content
|
||||
opts.sin.content = mutable_buffer{};
|
||||
|
||||
assert(opts.remote);
|
||||
server::request request
|
||||
{
|
||||
opts.remote,
|
||||
std::move(opts.sout),
|
||||
std::move(opts.sin),
|
||||
&opts.sopts,
|
||||
};
|
||||
|
||||
const auto code
|
||||
{
|
||||
request.get(opts.timeout)
|
||||
};
|
||||
|
||||
if(opts.code)
|
||||
*opts.code = code;
|
||||
|
||||
if(opts.out)
|
||||
*opts.out = std::move(request.in.dynamic);
|
||||
else
|
||||
this->out = std::move(request.in.dynamic);
|
||||
|
||||
ret = request.in.content;
|
||||
}
|
||||
|
||||
ircd::rest::request::request(const mutable_buffer &out,
|
||||
const rfc3986::uri &uri,
|
||||
opts opts)
|
||||
{
|
||||
if(!opts.remote)
|
||||
opts.remote = net::hostport{uri};
|
||||
|
||||
const bool need_alloc
|
||||
{
|
||||
empty(opts.buf)
|
||||
&& (empty(opts.sout.head) || empty(opts.sin.head))
|
||||
};
|
||||
|
||||
const unique_mutable_buffer _buf
|
||||
{
|
||||
need_alloc? 16_KiB: 0_KiB
|
||||
};
|
||||
|
||||
if(!empty(_buf))
|
||||
opts.buf = _buf;
|
||||
|
||||
window_buffer window
|
||||
{
|
||||
opts.buf
|
||||
};
|
||||
|
||||
if(empty(opts.sout.head))
|
||||
{
|
||||
assert(opts.remote);
|
||||
assert(opts.method);
|
||||
http::request
|
||||
{
|
||||
window,
|
||||
host(opts.remote),
|
||||
opts.method,
|
||||
uri.resource(),
|
||||
size(opts.content),
|
||||
opts.content_type,
|
||||
opts.headers
|
||||
};
|
||||
|
||||
opts.sout.head = window.completed();
|
||||
}
|
||||
|
||||
if(empty(opts.sout.content))
|
||||
opts.sout.content = opts.content;
|
||||
|
||||
if(empty(opts.sin.head))
|
||||
opts.sin.head = mutable_buffer{window};
|
||||
|
||||
if(empty(opts.sin.content))
|
||||
opts.sin.content = out;
|
||||
|
||||
if(empty(opts.sin.content))
|
||||
opts.sin.content = opts.sin.head;
|
||||
|
||||
assert(opts.remote);
|
||||
server::request request
|
||||
{
|
||||
opts.remote,
|
||||
std::move(opts.sout),
|
||||
std::move(opts.sin),
|
||||
&opts.sopts,
|
||||
};
|
||||
|
||||
const auto code
|
||||
{
|
||||
request.get(opts.timeout)
|
||||
};
|
||||
|
||||
if(opts.code)
|
||||
*opts.code = code;
|
||||
|
||||
ret = request.in.content;
|
||||
}
|
|
@ -13,12 +13,12 @@
|
|||
//
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::versions>::allocator)
|
||||
decltype(ircd::versions::allocator)
|
||||
ircd::util::instance_list<ircd::versions>::allocator
|
||||
{};
|
||||
|
||||
template<>
|
||||
decltype(ircd::util::instance_list<ircd::versions>::list)
|
||||
decltype(ircd::versions::list)
|
||||
ircd::util::instance_list<ircd::versions>::list
|
||||
{
|
||||
allocator
|
||||
|
|
|
@ -41,7 +41,7 @@ endif
|
|||
endif
|
||||
|
||||
if LTO
|
||||
if CLANG
|
||||
if CLANG15 # trouble with ld.gold <= llvm-14
|
||||
AM_CXXFLAGS += -flto=thin
|
||||
AM_CXXFLAGS += -fstrict-vtable-pointers
|
||||
AM_CXXFLAGS += -fwhole-program-vtables
|
||||
|
@ -137,6 +137,7 @@ libircd_matrix_la_SOURCES += user_devices.cc
|
|||
libircd_matrix_la_SOURCES += user_events.cc
|
||||
libircd_matrix_la_SOURCES += user_filter.cc
|
||||
libircd_matrix_la_SOURCES += user_ignores.cc
|
||||
libircd_matrix_la_SOURCES += user_keys.cc
|
||||
libircd_matrix_la_SOURCES += user_mitsein.cc
|
||||
libircd_matrix_la_SOURCES += user_notifications.cc
|
||||
libircd_matrix_la_SOURCES += user_profile.cc
|
||||
|
|
|
@ -8,31 +8,25 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
extern const event::keys::exclude event_append_exclude_keys;
|
||||
extern const event::keys event_append_default_keys;
|
||||
extern conf::item<std::string> event_append_exclude_types;
|
||||
extern conf::item<bool> event_append_info;
|
||||
extern log::log event_append_log;
|
||||
}
|
||||
|
||||
decltype(ircd::m::event_append_log)
|
||||
ircd::m::event_append_log
|
||||
[[gnu::visibility("hidden")]]
|
||||
decltype(ircd::m::event::append::log)
|
||||
ircd::m::event::append::log
|
||||
{
|
||||
"m.event.append"
|
||||
};
|
||||
|
||||
decltype(ircd::m::event_append_info)
|
||||
ircd::m::event_append_info
|
||||
[[gnu::visibility("hidden")]]
|
||||
decltype(ircd::m::event::append::info)
|
||||
ircd::m::event::append::info
|
||||
{
|
||||
{ "name", "ircd.m.event.append.info" },
|
||||
{ "default", false },
|
||||
{ "persist", false },
|
||||
};
|
||||
|
||||
decltype(ircd::m::event_append_exclude_types)
|
||||
ircd::m::event_append_exclude_types
|
||||
[[gnu::visibility("hidden")]]
|
||||
decltype(ircd::m::event::append::exclude_types)
|
||||
ircd::m::event::append::exclude_types
|
||||
{
|
||||
{ "name", "ircd.m.event.append.exclude.types" },
|
||||
{ "default", "org.matrix.dummy_event" },
|
||||
|
@ -42,8 +36,9 @@ ircd::m::event_append_exclude_types
|
|||
/// to the client. This mask is applied only if the caller of event::append{}
|
||||
/// did not supply their mask to apply. It is also inferior to the user's
|
||||
/// filter if supplied.
|
||||
decltype(ircd::m::event_append_exclude_keys)
|
||||
ircd::m::event_append_exclude_keys
|
||||
[[gnu::visibility("hidden")]]
|
||||
decltype(ircd::m::event::append::exclude_keys)
|
||||
ircd::m::event::append::exclude_keys
|
||||
{
|
||||
"auth_events",
|
||||
"hashes",
|
||||
|
@ -53,16 +48,17 @@ ircd::m::event_append_exclude_keys
|
|||
"signatures",
|
||||
};
|
||||
|
||||
decltype(ircd::m::event_append_default_keys)
|
||||
ircd::m::event_append_default_keys
|
||||
[[gnu::visibility("hidden")]]
|
||||
decltype(ircd::m::event::append::default_keys)
|
||||
ircd::m::event::append::default_keys
|
||||
{
|
||||
event_append_exclude_keys
|
||||
event::append::exclude_keys
|
||||
};
|
||||
|
||||
ircd::m::event::append::append(json::stack::array &array,
|
||||
const event &event_,
|
||||
bool
|
||||
ircd::m::event::append::object(json::stack::array &array,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
:returns<bool>{[&]
|
||||
{
|
||||
assert(array.s);
|
||||
json::stack::checkpoint cp
|
||||
|
@ -70,29 +66,24 @@ ircd::m::event::append::append(json::stack::array &array,
|
|||
*array.s
|
||||
};
|
||||
|
||||
json::stack::object object
|
||||
json::stack::object _object
|
||||
{
|
||||
array
|
||||
};
|
||||
|
||||
const bool ret
|
||||
{
|
||||
append
|
||||
{
|
||||
object, event_, opts
|
||||
}
|
||||
members(_object, event, opts)
|
||||
};
|
||||
|
||||
cp.committing(ret);
|
||||
return ret;
|
||||
}}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::event::append::append(json::stack::object &object,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
:returns<bool>{[&]
|
||||
bool
|
||||
ircd::m::event::append::members(json::stack::object &out,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
{
|
||||
// Assertions that the event being appended has some required fields. This
|
||||
// is a central butt-end test of data coming through the system to here.
|
||||
|
@ -101,150 +92,35 @@ ircd::m::event::append::append(json::stack::object &object,
|
|||
assert(defined(json::get<"sender"_>(event)));
|
||||
//assert(json::get<"origin_server_ts"_>(event));
|
||||
//assert(json::get<"origin_server_ts"_>(event) != json::undefined_number);
|
||||
#if defined(RB_DEBUG)
|
||||
if(unlikely(!defined(json::get<"type"_>(event))))
|
||||
return false;
|
||||
if constexpr(RB_DEBUG_LEVEL)
|
||||
{
|
||||
if(unlikely(!defined(json::get<"type"_>(event))))
|
||||
return false;
|
||||
|
||||
if(unlikely(!defined(json::get<"sender"_>(event))))
|
||||
return false;
|
||||
#endif
|
||||
if(unlikely(!defined(json::get<"sender"_>(event))))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(opts.event_filter && !m::match(*opts.event_filter, event))
|
||||
return false;
|
||||
|
||||
const auto ¬_types
|
||||
{
|
||||
event_append_exclude_types
|
||||
};
|
||||
|
||||
if(!opts.event_filter && token_exists(not_types, ' ', json::get<"type"_>(event)))
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because type '%s' excluded by configuration.",
|
||||
string_view{event.event_id},
|
||||
json::get<"type"_>(event),
|
||||
};
|
||||
|
||||
if(is_excluded(event, opts))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(opts.query_visible && opts.user_id && !visible(event, *opts.user_id))
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because not visible to %s.",
|
||||
string_view{event.event_id},
|
||||
string_view{*opts.user_id},
|
||||
};
|
||||
|
||||
if(is_invisible(event, opts))
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool has_event_idx
|
||||
{
|
||||
opts.event_idx && *opts.event_idx
|
||||
};
|
||||
|
||||
const bool is_state
|
||||
{
|
||||
defined(json::get<"state_key"_>(event))
|
||||
};
|
||||
|
||||
const bool query_redacted
|
||||
{
|
||||
has_event_idx &&
|
||||
opts.query_redacted &&
|
||||
!is_state &&
|
||||
(!opts.room_depth || *opts.room_depth > json::get<"depth"_>(event))
|
||||
};
|
||||
|
||||
if(query_redacted && m::redacted(*opts.event_idx))
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because redacted.",
|
||||
string_view{event.event_id},
|
||||
};
|
||||
|
||||
if(is_redacted(event, opts))
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool has_user
|
||||
{
|
||||
opts.user_id && opts.user_room
|
||||
};
|
||||
|
||||
const bool check_ignores
|
||||
{
|
||||
has_user && !is_state
|
||||
};
|
||||
|
||||
if(check_ignores && *opts.user_id != json::get<"sender"_>(event))
|
||||
{
|
||||
const m::user::ignores ignores
|
||||
{
|
||||
*opts.user_id
|
||||
};
|
||||
|
||||
if(ignores.enforce("events") && ignores.has(json::get<"sender"_>(event)))
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because %s is ignored by %s",
|
||||
string_view{event.event_id},
|
||||
json::get<"sender"_>(event),
|
||||
string_view{*opts.user_id}
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const bool sender_is_user
|
||||
{
|
||||
has_user && json::get<"sender"_>(event) == *opts.user_id
|
||||
};
|
||||
|
||||
const bool has_client_txnid
|
||||
{
|
||||
opts.client_txnid && *opts.client_txnid
|
||||
};
|
||||
|
||||
const auto txnid_idx
|
||||
{
|
||||
!has_client_txnid && sender_is_user && opts.query_txnid?
|
||||
opts.user_room->get(std::nothrow, "ircd.client.txnid", event.event_id):
|
||||
0UL
|
||||
};
|
||||
|
||||
const bool query_prev_state
|
||||
{
|
||||
opts.query_prev_state && has_event_idx && is_state
|
||||
};
|
||||
|
||||
const auto prev_state_idx
|
||||
{
|
||||
query_prev_state?
|
||||
room::state::prev(*opts.event_idx):
|
||||
0UL
|
||||
};
|
||||
|
||||
#if defined(RB_DEBUG) && 0
|
||||
if(!has_client_txnid && !txnid_idx && sender_is_user && opts.query_txnid)
|
||||
log::dwarning
|
||||
{
|
||||
log, "Could not find transaction_id for %s from %s in %s",
|
||||
string_view{event.event_id},
|
||||
json::get<"sender"_>(event),
|
||||
json::get<"room_id"_>(event)
|
||||
};
|
||||
#endif
|
||||
if(is_ignored(event, opts))
|
||||
return false;
|
||||
|
||||
// For v3+ events
|
||||
if(!json::get<"event_id"_>(event))
|
||||
json::stack::member
|
||||
{
|
||||
object, "event_id", event.event_id
|
||||
out, "event_id", event.event_id
|
||||
};
|
||||
|
||||
// Get the list of properties to send to the client so we can strip
|
||||
|
@ -254,11 +130,11 @@ ircd::m::event::append::append(json::stack::object &object,
|
|||
{
|
||||
opts.keys?
|
||||
*opts.keys:
|
||||
event_append_default_keys
|
||||
default_keys
|
||||
};
|
||||
|
||||
// Append the event members
|
||||
for_each(event, [&keys, &object]
|
||||
for_each(event, [&keys, &out]
|
||||
(const auto &key, const auto &val_)
|
||||
{
|
||||
if(!keys.has(key) && key != "redacts"_sv)
|
||||
|
@ -274,70 +150,77 @@ ircd::m::event::append::append(json::stack::object &object,
|
|||
|
||||
json::stack::member
|
||||
{
|
||||
object, key, val
|
||||
out, key, val
|
||||
};
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
json::stack::object unsigned_
|
||||
{
|
||||
object, "unsigned"
|
||||
};
|
||||
_unsigned(out, event, opts);
|
||||
|
||||
const json::value age
|
||||
{
|
||||
// When the opts give an explicit age, use it.
|
||||
opts.age != std::numeric_limits<long>::min()?
|
||||
opts.age:
|
||||
|
||||
// If we have depth information, craft a value based on the
|
||||
// distance to the head depth; if this is 0 in riot the event will
|
||||
// "stick" at the bottom of the timeline. This may be advantageous
|
||||
// in the future but for now we make sure the result is non-zero.
|
||||
json::get<"depth"_>(event) >= 0 && opts.room_depth && *opts.room_depth >= 0L?
|
||||
(*opts.room_depth + 1 - json::get<"depth"_>(event)) + 1:
|
||||
|
||||
// We don't have depth information, so we use the origin_server_ts.
|
||||
// It is bad if it conflicts with other appends in the room which
|
||||
// did have depth information.
|
||||
!opts.room_depth && json::get<"origin_server_ts"_>(event)?
|
||||
ircd::time<milliseconds>() - json::get<"origin_server_ts"_>(event):
|
||||
|
||||
// Finally, this special value will eliminate the age altogether
|
||||
// during serialization.
|
||||
json::undefined_number
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
unsigned_, "age", age
|
||||
};
|
||||
|
||||
if(has_client_txnid)
|
||||
json::stack::member
|
||||
if(unlikely(info))
|
||||
log::info
|
||||
{
|
||||
unsigned_, "transaction_id", *opts.client_txnid
|
||||
log, "%s %s idx:%lu in %s depth:%ld txnid:%s %s,%s",
|
||||
string_view{opts.user_id},
|
||||
string_view{event.event_id},
|
||||
opts.event_idx,
|
||||
json::get<"room_id"_>(event),
|
||||
json::get<"depth"_>(event),
|
||||
opts.client_txnid,
|
||||
json::get<"type"_>(event),
|
||||
json::get<"state_key"_>(event),
|
||||
};
|
||||
|
||||
if(txnid_idx)
|
||||
m::get(std::nothrow, txnid_idx, "content", [&unsigned_]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
unsigned_, "transaction_id", unquote(content.get("transaction_id"))
|
||||
};
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::event::append::_unsigned(json::stack::object &out,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "unsigned"
|
||||
};
|
||||
|
||||
_age(object, event, opts);
|
||||
_txnid(object, event, opts);
|
||||
_relations(object, event, opts);
|
||||
if(defined(json::get<"state_key"_>(event)))
|
||||
_prev_state(object, event, opts);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::event::append::_prev_state(json::stack::object &out,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
{
|
||||
assert(defined(json::get<"state_key"_>(event)));
|
||||
|
||||
const bool query_prev_state
|
||||
{
|
||||
true
|
||||
&& opts.event_idx
|
||||
&& opts.query_prev_state
|
||||
};
|
||||
|
||||
const auto prev_state_idx
|
||||
{
|
||||
query_prev_state?
|
||||
room::state::prev(opts.event_idx):
|
||||
0UL
|
||||
};
|
||||
|
||||
if(prev_state_idx)
|
||||
{
|
||||
m::get(std::nothrow, prev_state_idx, "content", [&unsigned_]
|
||||
m::get(std::nothrow, prev_state_idx, "content", [&out]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
unsigned_, "prev_content", content
|
||||
out, "prev_content", content
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -348,7 +231,7 @@ ircd::m::event::append::append(json::stack::object &object,
|
|||
|
||||
json::stack::member
|
||||
{
|
||||
unsigned_, "replaces_state", json::value
|
||||
out, "replaces_state", json::value
|
||||
{
|
||||
replaces_state_id?
|
||||
string_view{replaces_state_id}:
|
||||
|
@ -356,24 +239,279 @@ ircd::m::event::append::append(json::stack::object &object,
|
|||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if(unlikely(event_append_info))
|
||||
log::info
|
||||
void
|
||||
ircd::m::event::append::_txnid(json::stack::object &out,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
{
|
||||
const bool sender_is_user
|
||||
{
|
||||
json::get<"sender"_>(event) == opts.user_id
|
||||
};
|
||||
|
||||
const bool query_txnid
|
||||
{
|
||||
true
|
||||
&& !opts.client_txnid
|
||||
&& opts.query_txnid
|
||||
&& opts.user_room_id
|
||||
&& sender_is_user
|
||||
};
|
||||
|
||||
const auto txnid_idx
|
||||
{
|
||||
query_txnid?
|
||||
m::room(opts.user_room_id).get(std::nothrow, "ircd.client.txnid", event.event_id):
|
||||
0UL
|
||||
};
|
||||
|
||||
if constexpr(RB_DEBUG_LEVEL)
|
||||
{
|
||||
const bool missing_txnid
|
||||
{
|
||||
event_append_log, "%s %s idx:%lu in %s depth:%ld txnid:%s idx:%lu age:%ld %s,%s",
|
||||
opts.user_id? string_view{*opts.user_id} : string_view{},
|
||||
string_view{event.event_id},
|
||||
opts.event_idx? *opts.event_idx : 0UL,
|
||||
json::get<"room_id"_>(event),
|
||||
json::get<"depth"_>(event),
|
||||
has_client_txnid? *opts.client_txnid : string_view{},
|
||||
txnid_idx,
|
||||
int64_t(age),
|
||||
json::get<"type"_>(event),
|
||||
json::get<"state_key"_>(event),
|
||||
true
|
||||
&& !opts.client_txnid
|
||||
&& !txnid_idx
|
||||
&& sender_is_user
|
||||
&& opts.query_txnid
|
||||
};
|
||||
|
||||
return true;
|
||||
}}
|
||||
{
|
||||
if(unlikely(missing_txnid))
|
||||
log::dwarning
|
||||
{
|
||||
log, "Could not find transaction_id for %s from %s in %s",
|
||||
string_view{event.event_id},
|
||||
json::get<"sender"_>(event),
|
||||
json::get<"room_id"_>(event)
|
||||
};
|
||||
}
|
||||
|
||||
if(opts.client_txnid)
|
||||
json::stack::member
|
||||
{
|
||||
out, "transaction_id", opts.client_txnid
|
||||
};
|
||||
else if(txnid_idx)
|
||||
m::get(std::nothrow, txnid_idx, "content", [&out]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
out, "transaction_id", content.get("transaction_id")
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::event::append::_age(json::stack::object &out,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
{
|
||||
const json::value age
|
||||
{
|
||||
// When the opts give an explicit age, use it.
|
||||
opts.age != std::numeric_limits<long>::min()?
|
||||
opts.age:
|
||||
|
||||
// If we have depth information, craft a value based on the
|
||||
// distance to the head depth; if this is 0 in riot the event will
|
||||
// "stick" at the bottom of the timeline. This may be advantageous
|
||||
// in the future but for now we make sure the result is non-zero.
|
||||
json::get<"depth"_>(event) >= 0 && opts.room_depth >= 0L?
|
||||
(opts.room_depth + 1 - json::get<"depth"_>(event)) + 1:
|
||||
|
||||
// We don't have depth information, so we use the origin_server_ts.
|
||||
// It is bad if it conflicts with other appends in the room which
|
||||
// did have depth information.
|
||||
opts.room_depth < 0 && json::get<"origin_server_ts"_>(event)?
|
||||
ircd::time<milliseconds>() - json::get<"origin_server_ts"_>(event):
|
||||
|
||||
// Finally, this special value will eliminate the age altogether
|
||||
// during serialization.
|
||||
json::undefined_number
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
out, "age", age
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::event::append::_relations(json::stack::object &out,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
{
|
||||
assert(out.s);
|
||||
json::stack::checkpoint cp
|
||||
{
|
||||
*out.s, false
|
||||
};
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
out, "m.relations"
|
||||
};
|
||||
|
||||
bool commit
|
||||
{
|
||||
cp.committing()
|
||||
};
|
||||
|
||||
if(opts.bundle_all || opts.bundle_replace)
|
||||
commit |= bundle_replace(object, event, opts);
|
||||
|
||||
cp.committing(commit);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::event::append::bundle_replace(json::stack::object &out,
|
||||
const event &event,
|
||||
const opts &opts)
|
||||
{
|
||||
const m::replaced replaced
|
||||
{
|
||||
opts.event_idx, m::replaced::latest
|
||||
};
|
||||
|
||||
const event::idx &replace_idx
|
||||
{
|
||||
replaced
|
||||
};
|
||||
|
||||
if(likely(!replace_idx))
|
||||
return false;
|
||||
|
||||
const m::event::fetch replace
|
||||
{
|
||||
std::nothrow, replace_idx
|
||||
};
|
||||
|
||||
if(unlikely(!replace.valid))
|
||||
return false;
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
out, "m.replace"
|
||||
};
|
||||
|
||||
object.append(replace);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::event::append::is_excluded(const event &event,
|
||||
const opts &opts)
|
||||
const
|
||||
{
|
||||
const auto ¬_types
|
||||
{
|
||||
exclude_types
|
||||
};
|
||||
|
||||
const bool ret
|
||||
{
|
||||
true
|
||||
&& !opts.event_filter
|
||||
&& token_exists(not_types, ' ', json::get<"type"_>(event))
|
||||
};
|
||||
|
||||
if(ret)
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because type '%s' excluded by configuration.",
|
||||
string_view{event.event_id},
|
||||
json::get<"type"_>(event),
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::event::append::is_invisible(const event &event,
|
||||
const opts &opts)
|
||||
const
|
||||
{
|
||||
const bool ret
|
||||
{
|
||||
true
|
||||
&& opts.query_visible
|
||||
&& opts.user_id
|
||||
&& !visible(event, opts.user_id)
|
||||
};
|
||||
|
||||
if(ret)
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because not visible to %s.",
|
||||
string_view{event.event_id},
|
||||
string_view{opts.user_id},
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::event::append::is_redacted(const event &event,
|
||||
const opts &opts)
|
||||
const
|
||||
{
|
||||
const bool ret
|
||||
{
|
||||
true
|
||||
&& opts.event_idx
|
||||
&& opts.query_redacted
|
||||
&& !defined(json::get<"state_key"_>(event))
|
||||
&& opts.room_depth > json::get<"depth"_>(event)
|
||||
&& m::redacted(opts.event_idx)
|
||||
};
|
||||
|
||||
if(ret)
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because redacted.",
|
||||
string_view{event.event_id},
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::event::append::is_ignored(const event &event,
|
||||
const opts &opts)
|
||||
const
|
||||
{
|
||||
const bool check_ignores
|
||||
{
|
||||
true
|
||||
&& !defined(json::get<"state_key"_>(event))
|
||||
&& opts.user_id
|
||||
&& opts.user_room_id
|
||||
&& opts.user_id != json::get<"sender"_>(event)
|
||||
};
|
||||
|
||||
if(!check_ignores)
|
||||
return false;
|
||||
|
||||
const m::user::ignores ignores
|
||||
{
|
||||
opts.user_id
|
||||
};
|
||||
|
||||
if(ignores.enforce("events") && ignores.has(json::get<"sender"_>(event)))
|
||||
{
|
||||
log::debug
|
||||
{
|
||||
log, "Not sending event %s because %s is ignored by %s",
|
||||
string_view{event.event_id},
|
||||
json::get<"sender"_>(event),
|
||||
string_view{opts.user_id}
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
15
matrix/id.cc
15
matrix/id.cc
|
@ -132,6 +132,13 @@ ircd::m::id::parser
|
|||
,"event_id version 4"
|
||||
};
|
||||
|
||||
// de-facto device id
|
||||
const rule<> device_id
|
||||
{
|
||||
device_sigil >> localpart
|
||||
,"device_id (de facto)"
|
||||
};
|
||||
|
||||
/// (Appendix 4.1) Server Name
|
||||
/// A homeserver is uniquely identified by its server name. This value
|
||||
/// is used in a number of identifiers, as described below. The server
|
||||
|
@ -159,6 +166,7 @@ ircd::m::id::parser
|
|||
(prefix >> ':' >> server_name)
|
||||
| event_id_v4
|
||||
| event_id_v3
|
||||
| device_id
|
||||
,"mxid"
|
||||
};
|
||||
|
||||
|
@ -584,7 +592,12 @@ ircd::m::id::id(const enum sigil &sigil,
|
|||
static const auto &dict{rand::dict::alnum};
|
||||
const mutable_buffer dst{tmp_buf[0], 10};
|
||||
name = rand::string(dst, dict);
|
||||
break;
|
||||
return fmt::sprintf
|
||||
{
|
||||
buf, "%c%s",
|
||||
char(sigil),
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
|
@ -132,8 +132,7 @@ ircd::m::module_names
|
|||
"client_keys_query",
|
||||
"client_keys_signatures_upload",
|
||||
"client_keys_device_signing_upload",
|
||||
"client_room_keys_version",
|
||||
"client_room_keys_keys",
|
||||
"client_room_keys",
|
||||
"client_presence",
|
||||
"client_groups",
|
||||
"client_joined_groups",
|
||||
|
@ -158,6 +157,7 @@ ircd::m::module_names
|
|||
"client_sync_account_data",
|
||||
"client_sync_device_lists",
|
||||
"client_sync_device_one_time_keys_count",
|
||||
"client_sync_device_unused_fallback_key_types",
|
||||
"client_sync_groups",
|
||||
"client_sync_presence",
|
||||
"client_sync_to_device",
|
||||
|
|
|
@ -200,3 +200,4 @@ constexpr const char *const ircd::m::name::m_in_reply_to;
|
|||
constexpr const char *const ircd::m::name::usage;
|
||||
constexpr const char *const ircd::m::name::master_key;
|
||||
constexpr const char *const ircd::m::name::self_signing_key;
|
||||
constexpr const char *const ircd::m::name::user_signing_key;
|
||||
|
|
|
@ -365,8 +365,8 @@ ircd::m::pretty_stateline(std::ostream &out,
|
|||
<< std::right << " [ "
|
||||
<< std::setw(30) << type
|
||||
<< std::left << " | "
|
||||
<< std::setw(50) << state_key
|
||||
<< std::left << " ]" << flags << " "
|
||||
<< std::setw(50) << trunc(state_key, 50)
|
||||
<< std::left << "]" << flags << " "
|
||||
<< std::setw(10) << event_idx
|
||||
<< std::left << " "
|
||||
<< std::setw(72) << string_view{event.event_id}
|
||||
|
@ -382,10 +382,10 @@ ircd::m::pretty_stateline(std::ostream &out,
|
|||
<< std::right << " "
|
||||
<< std::setw(9) << json::get<"depth"_>(event)
|
||||
<< std::right << " [ "
|
||||
<< std::setw(40) << type
|
||||
<< std::setw(50) << type
|
||||
<< std::left << " | "
|
||||
<< std::setw(56) << state_key
|
||||
<< std::left << " ]" << flags << " "
|
||||
<< std::setw(60) << trunc(state_key, 60)
|
||||
<< std::left << "]" << flags << " "
|
||||
<< std::setw(10) << event_idx
|
||||
<< ' '
|
||||
<< std::left << trunc(content, 80)
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
decltype(ircd::m::relates::latest_column)
|
||||
ircd::m::relates::latest_column
|
||||
{
|
||||
{ "name", "ircd.m.relates.latest_column" },
|
||||
{ "default", "origin_server_ts" },
|
||||
};
|
||||
|
||||
bool
|
||||
ircd::m::relates::prefetch(const string_view &type)
|
||||
const
|
||||
|
@ -21,8 +28,8 @@ const
|
|||
refs.for_each(dbs::ref::M_RELATES, [this, &ret]
|
||||
(const auto &event_idx, const auto &)
|
||||
{
|
||||
if(this->prefetch_depth)
|
||||
ret |= m::prefetch(event_idx, "depth");
|
||||
if(this->prefetch_latest)
|
||||
ret |= m::prefetch(event_idx, string_view{latest_column});
|
||||
|
||||
if(this->prefetch_sender || this->match_sender)
|
||||
ret |= m::prefetch(event_idx, "sender");
|
||||
|
@ -87,6 +94,11 @@ ircd::m::relates::latest(const string_view &type,
|
|||
uint *const at)
|
||||
const
|
||||
{
|
||||
const string_view &column
|
||||
{
|
||||
latest_column
|
||||
};
|
||||
|
||||
if(at)
|
||||
*at = -1;
|
||||
|
||||
|
@ -97,10 +109,10 @@ const
|
|||
(const event::idx &event_idx, const json::object &, const m::relates_to &)
|
||||
noexcept
|
||||
{
|
||||
int64_t depth{0};
|
||||
if((depth = m::get(std::nothrow, event_idx, "depth", depth)) > best)
|
||||
int64_t val{0};
|
||||
if((val = m::get(std::nothrow, event_idx, column, val)) > best)
|
||||
{
|
||||
best = depth;
|
||||
best = val;
|
||||
ret = event_idx;
|
||||
|
||||
if(at)
|
||||
|
|
|
@ -892,6 +892,32 @@ ircd::m::join_rule(const room &room,
|
|||
return join_rule(buf, room) == rule;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::type(const room::id &room_id,
|
||||
const string_view &type_)
|
||||
{
|
||||
const m::room room
|
||||
{
|
||||
room_id
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
{
|
||||
room.get(std::nothrow, "m.room.create", "")
|
||||
};
|
||||
|
||||
return m::query(std::nothrow, event_idx, "content", false, [&type_]
|
||||
(const json::object &content)
|
||||
{
|
||||
const json::string &type
|
||||
{
|
||||
content.get("type")
|
||||
};
|
||||
|
||||
return type == type_;
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::creator(const room::id &room_id,
|
||||
const user::id &user_id)
|
||||
|
|
|
@ -31,7 +31,7 @@ decltype(ircd::m::createroom::version_default)
|
|||
ircd::m::createroom::version_default
|
||||
{
|
||||
{ "name", "ircd.m.createroom.version_default" },
|
||||
{ "default", "5" },
|
||||
{ "default", "6" },
|
||||
};
|
||||
|
||||
decltype(ircd::m::createroom::spec_presets)
|
||||
|
|
|
@ -54,15 +54,14 @@ ircd::m::replace(room::message &msg,
|
|||
const event::idx &event_idx)
|
||||
{
|
||||
// Find the latest edit of this; otherwise stick with the original.
|
||||
const m::relates relates
|
||||
const m::replaced replaced
|
||||
{
|
||||
.refs = event_idx,
|
||||
.match_sender = true,
|
||||
event_idx, m::replaced::latest
|
||||
};
|
||||
|
||||
const event::idx replace_idx
|
||||
const event::idx &replace_idx
|
||||
{
|
||||
relates.latest("m.replace")
|
||||
replaced
|
||||
};
|
||||
|
||||
if(!replace_idx)
|
||||
|
|
|
@ -138,6 +138,10 @@ ircd::m::rooms::for_each(const opts &opts,
|
|||
if(!join_rule(room, opts.join_rule))
|
||||
return;
|
||||
|
||||
if(opts.room_type)
|
||||
if(!m::type(room_id, opts.room_type))
|
||||
return;
|
||||
|
||||
if(opts.server && opts.request_node_id && my_host(opts.server))
|
||||
if(!room::aliases(room_id).has_server(opts.server))
|
||||
return;
|
||||
|
|
|
@ -8,26 +8,81 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
bool
|
||||
ircd::m::user::devices::send(json::iov &content)
|
||||
ircd::m::user::devices::send::send(const m::user::devices &devices,
|
||||
const m::id::device &device_id,
|
||||
const string_view room_id)
|
||||
try
|
||||
{
|
||||
assert(content.has("user_id"));
|
||||
assert(content.has("device_id"));
|
||||
assert(device_id);
|
||||
|
||||
const auto &user_id
|
||||
{
|
||||
devices.user.user_id
|
||||
};
|
||||
|
||||
const bool deleted
|
||||
{
|
||||
devices.has(device_id)
|
||||
};
|
||||
|
||||
const m::user::keys user_keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const bool has_keys
|
||||
{
|
||||
!deleted && user_keys.has_device(device_id)
|
||||
};
|
||||
|
||||
const unique_mutable_buffer keys_buf
|
||||
{
|
||||
has_keys? 4_KiB: 0_KiB
|
||||
};
|
||||
|
||||
json::stack keys{keys_buf};
|
||||
if(has_keys)
|
||||
{
|
||||
json::stack::object top{keys};
|
||||
user_keys.device(top, device_id);
|
||||
}
|
||||
|
||||
// Triggers a devices request from the remote; also see
|
||||
// modules/federation/user_devices.cc
|
||||
const long &stream_id
|
||||
{
|
||||
1L
|
||||
};
|
||||
static const auto stream_id{1L};
|
||||
|
||||
json::iov event;
|
||||
json::iov event, content;
|
||||
const json::iov::push push[]
|
||||
{
|
||||
{ event, { "type", "m.device_list_update" } },
|
||||
{ event, { "sender", content.at("user_id") } },
|
||||
{ event, { "sender", user_id } },
|
||||
{ content, { "deleted", deleted } },
|
||||
{ content, { "device_id", device_id } },
|
||||
{ content, { "stream_id", stream_id } },
|
||||
{ content, { "user_id", user_id } },
|
||||
};
|
||||
|
||||
const json::iov::push push_keys
|
||||
{
|
||||
content, has_keys,
|
||||
{
|
||||
"keys", [&keys]
|
||||
{
|
||||
return keys.completed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// For diagnostic purposes; usually not defined.
|
||||
const json::iov::push push_room_id
|
||||
{
|
||||
event, m::valid(m::id::ROOM, room_id),
|
||||
{
|
||||
"room_id", [&room_id]
|
||||
{
|
||||
return room_id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
m::vm::copts opts;
|
||||
|
@ -39,8 +94,6 @@ try
|
|||
{
|
||||
event, content, opts
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
|
@ -51,12 +104,10 @@ catch(const std::exception &e)
|
|||
log::error
|
||||
{
|
||||
m::log, "Send m.device_list_update for '%s' belonging to %s :%s",
|
||||
content.at("device_id"),
|
||||
content.at("user_id"),
|
||||
string_view{device_id},
|
||||
string_view{devices.user.user_id},
|
||||
e.what(),
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -185,21 +236,11 @@ const
|
|||
m::redact(user_room, user_room.user, event_id, "deleted")
|
||||
};
|
||||
|
||||
if(!my(user))
|
||||
return true;
|
||||
|
||||
json::iov content;
|
||||
const json::iov::push push[]
|
||||
{
|
||||
{ content, { "user_id", user.user_id } },
|
||||
{ content, { "device_id", id } },
|
||||
{ content, { "deleted", true } },
|
||||
};
|
||||
|
||||
const bool broadcasted
|
||||
{
|
||||
user::devices::send(content)
|
||||
};
|
||||
if(my(user))
|
||||
user::devices::send
|
||||
{
|
||||
*this, id
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
429
matrix/user_keys.cc
Normal file
429
matrix/user_keys.cc
Normal file
|
@ -0,0 +1,429 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
ircd::m::user::keys::send::send(const m::user::keys &user_keys,
|
||||
const string_view room_id)
|
||||
try
|
||||
{
|
||||
const auto &user_id
|
||||
{
|
||||
user_keys.user_room.user.user_id
|
||||
};
|
||||
|
||||
const unique_mutable_buffer keys_buf[2]
|
||||
{
|
||||
{ 4_KiB },
|
||||
{ 4_KiB },
|
||||
};
|
||||
|
||||
json::stack keys[2]
|
||||
{
|
||||
{ keys_buf[0] },
|
||||
{ keys_buf[1] },
|
||||
};
|
||||
|
||||
// master
|
||||
{
|
||||
json::stack::object object{keys[0]};
|
||||
user_keys.cross_master(object);
|
||||
}
|
||||
|
||||
// self
|
||||
{
|
||||
json::stack::object object{keys[1]};
|
||||
user_keys.cross_self(object);
|
||||
}
|
||||
|
||||
json::iov event, content;
|
||||
const json::iov::push push[]
|
||||
{
|
||||
{ event, { "type", "m.signing_key_update" } },
|
||||
{ event, { "sender", user_id } },
|
||||
{ content, { "master_key", keys[0].completed() } },
|
||||
{ content, { "self_signing_key", keys[1].completed() } },
|
||||
{ content, { "user_id", user_id } },
|
||||
};
|
||||
|
||||
// For diagnostic purposes; usually not defined.
|
||||
const json::iov::push push_room_id
|
||||
{
|
||||
event, m::valid(m::id::ROOM, room_id),
|
||||
{
|
||||
"room_id", [&room_id]
|
||||
{
|
||||
return room_id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
m::vm::copts opts;
|
||||
opts.edu = true;
|
||||
opts.prop_mask.reset();
|
||||
opts.prop_mask.set("origin");
|
||||
opts.notify_clients = false;
|
||||
m::vm::eval
|
||||
{
|
||||
event, content, opts
|
||||
};
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
{
|
||||
m::log, "Sending m.signing_key_update for %s :%s",
|
||||
string_view{user_keys.user_room.user.user_id},
|
||||
e.what(),
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::update(const m::signing_key_update &sku)
|
||||
const
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
json::get<"user_id"_>(sku)
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const json::object &msk
|
||||
{
|
||||
json::get<"master_key"_>(sku)
|
||||
};
|
||||
|
||||
const auto cross_master_id
|
||||
{
|
||||
json::get<"master_key"_>(sku)?
|
||||
m::send(room, user_id, "ircd.cross_signing.master", "", msk):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
|
||||
const json::object &ssk
|
||||
{
|
||||
json::get<"self_signing_key"_>(sku)
|
||||
};
|
||||
|
||||
const auto cross_self_id
|
||||
{
|
||||
ssk?
|
||||
m::send(room, user_id, "ircd.cross_signing.self", "", ssk):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
|
||||
const json::object &usk
|
||||
{
|
||||
json::get<"user_signing_key"_>(sku)
|
||||
};
|
||||
|
||||
const auto cross_user_id
|
||||
{
|
||||
usk && my(user_id)?
|
||||
m::send(room, user_id, "ircd.cross_signing.user", "", usk):
|
||||
m::event::id::buf{}
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::keys::claim(json::stack::object &object,
|
||||
const string_view &device_id,
|
||||
const string_view &algorithm)
|
||||
const
|
||||
{
|
||||
const fmt::bsprintf<m::event::TYPE_MAX_SIZE> type
|
||||
{
|
||||
"ircd.device.one_time_key|%s",
|
||||
algorithm
|
||||
};
|
||||
|
||||
const m::room::type events
|
||||
{
|
||||
user_room, type, { -1UL, -1L }, true
|
||||
};
|
||||
|
||||
return !events.for_each([this, &object, &device_id]
|
||||
(const string_view &type, const auto &, const m::event::idx &event_idx)
|
||||
{
|
||||
if(m::redacted(event_idx))
|
||||
return true;
|
||||
|
||||
const bool match
|
||||
{
|
||||
m::query(std::nothrow, event_idx, "state_key", [&device_id]
|
||||
(const string_view &state_key) noexcept
|
||||
{
|
||||
return state_key == device_id;
|
||||
})
|
||||
};
|
||||
|
||||
if(!match)
|
||||
return true;
|
||||
|
||||
const auto algorithm
|
||||
{
|
||||
split(type, '|').second
|
||||
};
|
||||
|
||||
const bool fetched
|
||||
{
|
||||
m::get(std::nothrow, event_idx, "content", [&object, &algorithm]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
object, algorithm, json::object
|
||||
{
|
||||
content[""] // ircd.device.* quirk
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
if(!fetched)
|
||||
return true;
|
||||
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(event_idx)
|
||||
};
|
||||
|
||||
const auto redact_id
|
||||
{
|
||||
m::redact(user_room, user_room.user, event_id, "claimed")
|
||||
};
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::device(json::stack::object &out,
|
||||
const string_view &device_id)
|
||||
const
|
||||
{
|
||||
const m::user::devices devices
|
||||
{
|
||||
user_room.user
|
||||
};
|
||||
|
||||
devices.get(std::nothrow, device_id, "keys", [this, &out, &device_id]
|
||||
(const auto &, const json::object &device_keys)
|
||||
{
|
||||
const auto &user_id
|
||||
{
|
||||
user_room.user.user_id
|
||||
};
|
||||
|
||||
for(const auto &member : device_keys)
|
||||
if(member.first != "signatures")
|
||||
json::stack::member
|
||||
{
|
||||
out, member
|
||||
};
|
||||
|
||||
json::stack::object sigs
|
||||
{
|
||||
out, "signatures"
|
||||
};
|
||||
|
||||
json::stack::object user_sigs
|
||||
{
|
||||
sigs, user_id
|
||||
};
|
||||
|
||||
attach_sigs(user_sigs, device_keys, user_id);
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
state.for_each("ircd.keys.signatures", [this, &user_sigs, &user_id, &device_id]
|
||||
(const string_view &, const string_view &state_key, const auto &event_idx)
|
||||
{
|
||||
const auto &[target, source]
|
||||
{
|
||||
unmake_sigs_state_key(state_key)
|
||||
};
|
||||
|
||||
if(target && target != device_id)
|
||||
return true;
|
||||
|
||||
attach_sigs(user_sigs, event_idx, user_id);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::keys::append_keys(json::stack::object &out,
|
||||
const event::idx &event_idx,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
return m::get(std::nothrow, event_idx, "content", [this, &out, &user_id]
|
||||
(const json::object &device_keys)
|
||||
{
|
||||
append_keys(out, device_keys, user_id);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::append_keys(json::stack::object &out,
|
||||
const json::object &device_keys,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
for(const auto &member : device_keys)
|
||||
if(member.first != "signatures")
|
||||
json::stack::member
|
||||
{
|
||||
out, member
|
||||
};
|
||||
|
||||
json::stack::object sigs
|
||||
{
|
||||
out, "signatures"
|
||||
};
|
||||
|
||||
// signatures of the key's owner
|
||||
assert(user_room.user.user_id);
|
||||
append_sigs(sigs, device_keys, user_room.user.user_id);
|
||||
|
||||
// signatures of a cross-signer
|
||||
assert(user_id);
|
||||
if(user_id != user_room.user.user_id)
|
||||
append_sigs(sigs, device_keys, user_id);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::append_sigs(json::stack::object &out,
|
||||
const json::object &device_keys,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
json::stack::object user_sigs
|
||||
{
|
||||
out, user_id
|
||||
};
|
||||
|
||||
attach_sigs(user_sigs, device_keys, user_id);
|
||||
|
||||
const json::object device_keys_keys
|
||||
{
|
||||
device_keys["keys"]
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
state.for_each("ircd.keys.signatures", [this, &user_sigs, &user_id, &device_keys_keys]
|
||||
(const string_view &, const string_view &state_key, const auto &event_idx)
|
||||
{
|
||||
const auto &[target, source]
|
||||
{
|
||||
unmake_sigs_state_key(state_key)
|
||||
};
|
||||
|
||||
for(const auto &[key_id_, key] : device_keys_keys)
|
||||
{
|
||||
const auto &key_id
|
||||
{
|
||||
split(key_id_, ':').second
|
||||
};
|
||||
|
||||
if(target != key_id)
|
||||
continue;
|
||||
|
||||
attach_sigs(user_sigs, event_idx, user_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::user::keys::attach_sigs(json::stack::object &user_sigs,
|
||||
const event::idx &event_idx,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
return m::get(std::nothrow, event_idx, "content", [this, &user_sigs, &user_id]
|
||||
(const json::object &device_sigs)
|
||||
{
|
||||
attach_sigs(user_sigs, device_sigs, user_id);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::user::keys::attach_sigs(json::stack::object &user_sigs,
|
||||
const json::object &device_sigs,
|
||||
const user::id &user_id)
|
||||
const
|
||||
{
|
||||
const json::object device_sigs_sigs
|
||||
{
|
||||
device_sigs["signatures"]
|
||||
};
|
||||
|
||||
const json::object device_sigs_user_sigs
|
||||
{
|
||||
device_sigs_sigs[user_id]
|
||||
};
|
||||
|
||||
for(const auto &member : device_sigs_user_sigs)
|
||||
json::stack::member
|
||||
{
|
||||
user_sigs, member
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// user::keys::sigs
|
||||
//
|
||||
|
||||
std::tuple<ircd::string_view, ircd::string_view>
|
||||
ircd::m::user::keys::unmake_sigs_state_key(const string_view &state_key)
|
||||
noexcept
|
||||
{
|
||||
const auto &[tgt, src]
|
||||
{
|
||||
rsplit(state_key, '%')
|
||||
};
|
||||
|
||||
return std::tuple
|
||||
{
|
||||
tgt, src
|
||||
};
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::user::keys::make_sigs_state_key(const mutable_buffer &buf,
|
||||
const string_view &tgt,
|
||||
const string_view &src)
|
||||
{
|
||||
return fmt::sprintf
|
||||
{
|
||||
buf, "%s%s",
|
||||
string_view{tgt},
|
||||
tgt != src && src?
|
||||
string_view{src}:
|
||||
string_view{},
|
||||
};
|
||||
}
|
|
@ -443,6 +443,7 @@ client_client_sync_groups_la_SOURCES = client/sync/groups.cc
|
|||
client_client_sync_to_device_la_SOURCES = client/sync/to_device.cc
|
||||
client_client_sync_device_lists_la_SOURCES = client/sync/device_lists.cc
|
||||
client_client_sync_device_one_time_keys_count_la_SOURCES = client/sync/device_one_time_keys_count.cc
|
||||
client_client_sync_device_unused_fallback_key_types_la_SOURCES = client/sync/device_unused_fallback_key_types.cc
|
||||
|
||||
client_module_LTLIBRARIES += \
|
||||
client/client_sync_account_data.la \
|
||||
|
@ -452,6 +453,7 @@ client_module_LTLIBRARIES += \
|
|||
client/client_sync_to_device.la \
|
||||
client/client_sync_device_lists.la \
|
||||
client/client_sync_device_one_time_keys_count.la \
|
||||
client/client_sync_device_unused_fallback_key_types.la \
|
||||
###
|
||||
|
||||
# client/sync/rooms/
|
||||
|
@ -506,12 +508,14 @@ client_module_LTLIBRARIES += \
|
|||
# client/room_keys/
|
||||
#
|
||||
|
||||
client_client_room_keys_version_la_SOURCES = client/room_keys/version.cc
|
||||
client_client_room_keys_keys_la_SOURCES = client/room_keys/keys.cc
|
||||
client_client_room_keys_la_SOURCES = \
|
||||
client/room_keys/keys.cc \
|
||||
client/room_keys/version.cc \
|
||||
client/room_keys/room_keys.cc \
|
||||
###
|
||||
|
||||
client_module_LTLIBRARIES += \
|
||||
client/client_room_keys_version.la \
|
||||
client/client_room_keys_keys.la \
|
||||
client/client_room_keys.la \
|
||||
###
|
||||
|
||||
#
|
||||
|
|
|
@ -51,6 +51,16 @@ ircd::m::client_capabilities::get(client &client,
|
|||
mods::loaded("client_account")
|
||||
};
|
||||
|
||||
const bool m_set_displayname__enabled
|
||||
{
|
||||
mods::loaded("client_profile")
|
||||
};
|
||||
|
||||
const bool m_set_avatar_url__enabled
|
||||
{
|
||||
mods::loaded("client_profile")
|
||||
};
|
||||
|
||||
const json::value default_room_version
|
||||
{
|
||||
string_view{m::createroom::version_default}, json::STRING
|
||||
|
@ -66,6 +76,14 @@ ircd::m::client_capabilities::get(client &client,
|
|||
{
|
||||
{ "enabled", m_change_password__enabled },
|
||||
}},
|
||||
{ "m.set_displayname", json::members
|
||||
{
|
||||
{ "enabled", m_set_displayname__enabled },
|
||||
}},
|
||||
{ "m.set_avatar_url", json::members
|
||||
{
|
||||
{ "enabled", m_set_avatar_url__enabled },
|
||||
}},
|
||||
{ "m.room_versions", json::members
|
||||
{
|
||||
{ "default", default_room_version },
|
||||
|
|
|
@ -60,7 +60,7 @@ ircd::m::groups::handle_post(client &client,
|
|||
|
||||
return resource::response
|
||||
{
|
||||
client, http::CREATED, json::members
|
||||
client, http::OK, json::members
|
||||
{
|
||||
{ "group_id", group_id }
|
||||
},
|
||||
|
|
|
@ -103,7 +103,7 @@ post__createroom(client &client,
|
|||
top.~object();
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED, json::object
|
||||
client, http::OK, json::object
|
||||
{
|
||||
out.completed()
|
||||
}
|
||||
|
|
|
@ -274,6 +274,11 @@ _get_device(json::stack::object &obj,
|
|||
obj, "device_id", device_id
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
obj, "user_id", devices.user.user_id
|
||||
};
|
||||
|
||||
devices.get(std::nothrow, device_id, "display_name", [&obj]
|
||||
(const auto &, const string_view &value)
|
||||
{
|
||||
|
|
|
@ -376,10 +376,10 @@ append_event(json::stack::array &out,
|
|||
{
|
||||
out, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &user_room.user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = user_room.user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,10 +42,11 @@ recv_response(const string_view &,
|
|||
const system_point &);
|
||||
|
||||
static void
|
||||
recv_responses(query_map &,
|
||||
recv_responses(const host_users_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &,
|
||||
const milliseconds &);
|
||||
const system_point &);
|
||||
|
||||
static void
|
||||
handle_failures(const failure_map &,
|
||||
|
@ -137,6 +138,11 @@ post__keys_claim(client &client,
|
|||
send_requests(map, buffers, failures)
|
||||
};
|
||||
|
||||
const system_point timedout
|
||||
{
|
||||
ircd::now<system_point>() + timeout
|
||||
};
|
||||
|
||||
m::resource::response::chunked response
|
||||
{
|
||||
client, http::OK
|
||||
|
@ -152,9 +158,9 @@ post__keys_claim(client &client,
|
|||
out
|
||||
};
|
||||
|
||||
recv_responses(queries, failures, top, timeout);
|
||||
recv_responses(map, queries, failures, top, timedout);
|
||||
handle_failures(failures, top);
|
||||
return {};
|
||||
return response;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -174,14 +180,22 @@ handle_failures(const failure_map &failures,
|
|||
}
|
||||
|
||||
void
|
||||
recv_responses(query_map &queries,
|
||||
recv_responses(const host_users_map &map,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out,
|
||||
const milliseconds &timeout)
|
||||
const system_point &timeout)
|
||||
{
|
||||
const system_point timedout
|
||||
static const user_devices_map empty;
|
||||
|
||||
const auto it
|
||||
{
|
||||
ircd::now<system_point>() + timeout
|
||||
map.find(origin(m::my()))
|
||||
};
|
||||
|
||||
const user_devices_map &self
|
||||
{
|
||||
it != end(map)? it->second: empty
|
||||
};
|
||||
|
||||
json::stack::object one_time_keys
|
||||
|
@ -189,13 +203,55 @@ recv_responses(query_map &queries,
|
|||
out, "one_time_keys"
|
||||
};
|
||||
|
||||
for(auto &[remote, request] : queries)
|
||||
// local handle
|
||||
for(const auto &[user_id, reqs] : self)
|
||||
{
|
||||
assert(!failures.count(remote));
|
||||
if(failures.count(remote))
|
||||
continue;
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
recv_response(remote, request, failures, one_time_keys, timedout);
|
||||
json::stack::object user_object
|
||||
{
|
||||
one_time_keys, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id, algorithm] : json::object(reqs))
|
||||
{
|
||||
json::stack::object device_object
|
||||
{
|
||||
user_object, device_id
|
||||
};
|
||||
|
||||
keys.claim(device_object, device_id, json::string(algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
// remote handle
|
||||
while(!queries.empty())
|
||||
{
|
||||
auto next
|
||||
{
|
||||
ctx::when_any(begin(queries), end(queries), []
|
||||
(auto &it) -> m::fed::user::keys::claim &
|
||||
{
|
||||
return it->second;
|
||||
})
|
||||
};
|
||||
|
||||
const bool ok
|
||||
{
|
||||
next.wait_until(timeout, std::nothrow)
|
||||
};
|
||||
|
||||
const auto it(next.get());
|
||||
const unwind remove{[&queries, &it]
|
||||
{
|
||||
queries.erase(it);
|
||||
}};
|
||||
|
||||
auto &[remote, request] {*it};
|
||||
recv_response(remote, request, failures, one_time_keys, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,14 +279,22 @@ try
|
|||
};
|
||||
|
||||
for(const auto &[user_id, keys] : one_time_keys)
|
||||
{
|
||||
if(m::user::id(user_id).host() != remote)
|
||||
continue;
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object{keys}
|
||||
object, user_id, json::object
|
||||
{
|
||||
keys
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
log::derror
|
||||
{
|
||||
m::log, "user keys claim from %s :%s",
|
||||
remote,
|
||||
|
@ -247,7 +311,8 @@ send_requests(const host_users_map &hosts,
|
|||
{
|
||||
query_map ret;
|
||||
for(const auto &[remote, user_devices] : hosts)
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
if(likely(!my_host(remote)))
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -292,7 +357,7 @@ try
|
|||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
log::derror
|
||||
{
|
||||
m::log, "user keys claim to %s for %zu users :%s",
|
||||
remote,
|
||||
|
|
|
@ -66,52 +66,26 @@ ircd::m::post_keys_device_signing_upload(client &client,
|
|||
auth["password"]
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
const m::user user
|
||||
{
|
||||
request.user_id
|
||||
};
|
||||
|
||||
if(!room.user.is_password(password))
|
||||
if(!user.is_password(password))
|
||||
throw m::ACCESS_DENIED
|
||||
{
|
||||
"Incorrect password."
|
||||
};
|
||||
|
||||
const json::object &msk
|
||||
const m::user::keys keys
|
||||
{
|
||||
request["master_key"]
|
||||
user
|
||||
};
|
||||
|
||||
const auto master_id
|
||||
{
|
||||
msk?
|
||||
send(room, request.user_id, "ircd.device.signing.master", "", msk):
|
||||
event::id::buf{}
|
||||
};
|
||||
m::signing_key_update sku{request};
|
||||
json::get<"user_id"_>(sku) = request.user_id;
|
||||
|
||||
const json::object &ssk
|
||||
{
|
||||
request["self_signing_key"]
|
||||
};
|
||||
|
||||
const auto self_signing_id
|
||||
{
|
||||
ssk?
|
||||
send(room, request.user_id, "ircd.device.signing.self", "", ssk):
|
||||
event::id::buf{}
|
||||
};
|
||||
|
||||
const json::object &usk
|
||||
{
|
||||
request["user_signing_key"]
|
||||
};
|
||||
|
||||
const auto user_signing_id
|
||||
{
|
||||
usk?
|
||||
send(room, request.user_id, "ircd.device.signing.user", "", usk):
|
||||
event::id::buf{}
|
||||
};
|
||||
keys.update(sku);
|
||||
|
||||
return resource::response
|
||||
{
|
||||
|
|
|
@ -19,8 +19,32 @@ namespace
|
|||
using buffer_list = std::vector<unique_buffer<mutable_buffer>>;
|
||||
}
|
||||
|
||||
static host_users_map
|
||||
parse_user_request(const json::object &device_keys);
|
||||
static void
|
||||
handle_cross_keys(const m::resource::request &,
|
||||
const user_devices_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &,
|
||||
const string_view &);
|
||||
|
||||
static void
|
||||
handle_device_keys(const m::resource::request &,
|
||||
const user_devices_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &);
|
||||
|
||||
static void
|
||||
handle_responses(const m::resource::request &,
|
||||
const host_users_map &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &);
|
||||
|
||||
static void
|
||||
handle_errors(const m::resource::request &,
|
||||
query_map &,
|
||||
failure_map &);
|
||||
|
||||
static bool
|
||||
send_request(const string_view &,
|
||||
|
@ -34,19 +58,8 @@ send_requests(const host_users_map &,
|
|||
buffer_list &,
|
||||
failure_map &);
|
||||
|
||||
static void
|
||||
recv_response(const m::resource::request &,
|
||||
const string_view &,
|
||||
m::fed::user::keys::query &,
|
||||
failure_map &,
|
||||
json::stack::object &);
|
||||
|
||||
static void
|
||||
recv_responses(const m::resource::request &,
|
||||
query_map &,
|
||||
failure_map &,
|
||||
json::stack::object &,
|
||||
const milliseconds &);
|
||||
static host_users_map
|
||||
parse_user_request(const json::object &device_keys);
|
||||
|
||||
static void
|
||||
handle_failures(const failure_map &,
|
||||
|
@ -148,6 +161,20 @@ post__keys_query(client &client,
|
|||
send_requests(map, buffers, failures)
|
||||
};
|
||||
|
||||
auto responses
|
||||
{
|
||||
ctx::when_all(begin(queries), end(queries), []
|
||||
(auto &it) -> m::fed::user::keys::query &
|
||||
{
|
||||
return it->second;
|
||||
})
|
||||
};
|
||||
|
||||
const bool all_good
|
||||
{
|
||||
responses.wait_until(now<system_point>() + timeout, std::nothrow)
|
||||
};
|
||||
|
||||
m::resource::response::chunked response
|
||||
{
|
||||
client, http::OK
|
||||
|
@ -163,9 +190,9 @@ post__keys_query(client &client,
|
|||
out
|
||||
};
|
||||
|
||||
recv_responses(request, queries, failures, top, timeout);
|
||||
handle_responses(request, map, queries, failures, top);
|
||||
handle_failures(failures, top);
|
||||
return {};
|
||||
return response;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -177,216 +204,39 @@ handle_failures(const failure_map &failures,
|
|||
out, "failures"
|
||||
};
|
||||
|
||||
for(const auto &p : failures)
|
||||
{
|
||||
const string_view &hostname(p.first);
|
||||
const std::exception_ptr &eptr(p.second);
|
||||
for(const auto &[remote, eptr] : failures)
|
||||
json::stack::member
|
||||
{
|
||||
response_failures, hostname, what(eptr)
|
||||
response_failures, remote, what(eptr)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
recv_responses(const m::resource::request &client_request,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out,
|
||||
const milliseconds &timeout)
|
||||
try
|
||||
host_users_map
|
||||
parse_user_request(const json::object &device_keys)
|
||||
{
|
||||
const system_point timedout
|
||||
host_users_map ret;
|
||||
for(const auto &member : device_keys)
|
||||
{
|
||||
ircd::now<system_point>() + timeout
|
||||
};
|
||||
const m::user::id &user_id(member.first);
|
||||
const json::array &device_ids(member.second);
|
||||
const string_view &host(user_id.host());
|
||||
|
||||
while(!queries.empty())
|
||||
{
|
||||
static const auto dereferencer{[]
|
||||
(auto &it) -> m::fed::user::keys::query &
|
||||
auto it(ret.lower_bound(host));
|
||||
if(it == end(ret) || it->first != host)
|
||||
it = ret.emplace_hint(it, host, user_devices_map{});
|
||||
|
||||
user_devices_map &users(it->second);
|
||||
{
|
||||
return it->second;
|
||||
}};
|
||||
auto it(users.lower_bound(user_id));
|
||||
if(it == end(users) || it->first != user_id)
|
||||
it = users.emplace_hint(it, user_id, json::array{});
|
||||
|
||||
auto next
|
||||
{
|
||||
ctx::when_any(begin(queries), end(queries), dereferencer)
|
||||
};
|
||||
|
||||
next.wait_until(timedout); // throws on timeout
|
||||
const auto it{next.get()};
|
||||
const unwind remove{[&queries, &it]
|
||||
{
|
||||
queries.erase(it);
|
||||
}};
|
||||
|
||||
const auto &remote(it->first);
|
||||
auto &request(it->second);
|
||||
|
||||
assert(!failures.count(remote));
|
||||
if(failures.count(remote))
|
||||
continue;
|
||||
|
||||
recv_response(client_request, remote, request, failures, out);
|
||||
}
|
||||
}
|
||||
catch(const std::exception &)
|
||||
{
|
||||
for(const auto &[remote, request] : queries)
|
||||
failures.emplace(remote, std::current_exception());
|
||||
}
|
||||
|
||||
void
|
||||
recv_response(const m::resource::request &client_request,
|
||||
const string_view &remote,
|
||||
m::fed::user::keys::query &request,
|
||||
failure_map &failures,
|
||||
json::stack::object &out)
|
||||
try
|
||||
{
|
||||
const auto code
|
||||
{
|
||||
request.get()
|
||||
};
|
||||
|
||||
const json::object response
|
||||
{
|
||||
request
|
||||
};
|
||||
|
||||
// device_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "device_keys"
|
||||
};
|
||||
|
||||
const json::object &device_keys
|
||||
{
|
||||
response["device_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, device_keys] : device_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
object, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id, keys] : json::object(device_keys))
|
||||
json::stack::member
|
||||
{
|
||||
user_object, device_id, keys
|
||||
};
|
||||
if(!empty(device_ids))
|
||||
it->second = device_ids;
|
||||
}
|
||||
}
|
||||
|
||||
// master_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "master_keys"
|
||||
};
|
||||
|
||||
const json::object &master_keys
|
||||
{
|
||||
response["master_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, master_key] : master_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object
|
||||
{
|
||||
master_key
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// self_signing_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "self_signing_keys"
|
||||
};
|
||||
|
||||
const json::object &self_signing_keys
|
||||
{
|
||||
response["self_signing_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, self_signing_key] : self_signing_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object
|
||||
{
|
||||
self_signing_key
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// user_signing_keys
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "user_signing_keys"
|
||||
};
|
||||
|
||||
const json::object &user_signing_keys
|
||||
{
|
||||
response["user_signing_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, user_signing_key] : user_signing_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
_user_id
|
||||
};
|
||||
|
||||
if(client_request.user_id != _user_id)
|
||||
continue;
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
object, user_id, json::object
|
||||
{
|
||||
user_signing_key
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
{
|
||||
m::log, "user keys query from %s :%s",
|
||||
remote,
|
||||
e.what()
|
||||
};
|
||||
|
||||
failures.emplace(remote, std::current_exception());
|
||||
return ret;
|
||||
}
|
||||
|
||||
query_map
|
||||
|
@ -395,12 +245,9 @@ send_requests(const host_users_map &hosts,
|
|||
failure_map &failures)
|
||||
{
|
||||
query_map ret;
|
||||
for(const auto &pair : hosts)
|
||||
{
|
||||
const string_view &remote(pair.first);
|
||||
const user_devices_map &user_devices(pair.second);
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
}
|
||||
for(const auto &[remote, user_devices] : hosts)
|
||||
if(likely(!my_host(remote)))
|
||||
send_request(remote, user_devices, failures, buffers, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -441,43 +288,281 @@ try
|
|||
|
||||
return true;
|
||||
}
|
||||
catch(const ctx::interrupted &e)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
failures.emplace(remote, std::current_exception());
|
||||
log::derror
|
||||
{
|
||||
m::log, "user keys query to %s :%s",
|
||||
remote,
|
||||
e.what()
|
||||
};
|
||||
|
||||
failures.emplace(remote, std::current_exception());
|
||||
return false;
|
||||
}
|
||||
|
||||
host_users_map
|
||||
parse_user_request(const json::object &device_keys)
|
||||
void
|
||||
handle_responses(const m::resource::request &request,
|
||||
const host_users_map &map,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out)
|
||||
{
|
||||
host_users_map ret;
|
||||
for(const auto &member : device_keys)
|
||||
static const user_devices_map empty;
|
||||
|
||||
const auto it
|
||||
{
|
||||
const m::user::id &user_id(member.first);
|
||||
const json::array &device_ids(member.second);
|
||||
const string_view &host(user_id.host());
|
||||
map.find(origin(m::my()))
|
||||
};
|
||||
|
||||
auto it(ret.lower_bound(host));
|
||||
if(it == end(ret) || it->first != host)
|
||||
it = ret.emplace_hint(it, host, user_devices_map{});
|
||||
const user_devices_map &self
|
||||
{
|
||||
it != end(map)? it->second: empty
|
||||
};
|
||||
|
||||
user_devices_map &users(it->second);
|
||||
handle_errors(request, queries, failures);
|
||||
handle_device_keys(request, self, queries, failures, out);
|
||||
handle_cross_keys(request, self, queries, failures, out, "master_keys");
|
||||
handle_cross_keys(request, self, queries, failures, out, "self_signing_keys");
|
||||
handle_cross_keys(request, self, queries, failures, out, "user_signing_keys");
|
||||
}
|
||||
|
||||
void
|
||||
handle_errors(const m::resource::request &request,
|
||||
query_map &queries,
|
||||
failure_map &failures)
|
||||
{
|
||||
auto it(begin(queries));
|
||||
while(it != end(queries))
|
||||
{
|
||||
const auto &[remote, query] {*it};
|
||||
if(query.eptr())
|
||||
{
|
||||
auto it(users.lower_bound(user_id));
|
||||
if(it == end(users) || it->first != user_id)
|
||||
it = users.emplace_hint(it, user_id, json::array{});
|
||||
failures.emplace(remote, query.eptr());
|
||||
it = queries.erase(it);
|
||||
}
|
||||
else ++it;
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty(device_ids))
|
||||
it->second = device_ids;
|
||||
void
|
||||
handle_device_keys(const m::resource::request &request,
|
||||
const user_devices_map &self,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out)
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
out, "device_keys"
|
||||
};
|
||||
|
||||
// local handle
|
||||
for(const auto &[user_id, device_ids] : self)
|
||||
{
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
object, user_id
|
||||
};
|
||||
|
||||
if(empty(json::array(device_ids)))
|
||||
{
|
||||
const m::user::devices devices
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
devices.for_each([&user_object, &keys]
|
||||
(const auto &, const string_view &device_id)
|
||||
{
|
||||
json::stack::object device_object
|
||||
{
|
||||
user_object, device_id
|
||||
};
|
||||
|
||||
keys.device(device_object, device_id);
|
||||
});
|
||||
}
|
||||
else for(const json::string device_id : json::array(device_ids))
|
||||
{
|
||||
json::stack::object device_object
|
||||
{
|
||||
user_object, device_id
|
||||
};
|
||||
|
||||
keys.device(device_object, device_id);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
// remote handle
|
||||
for(const auto &[remote, query] : queries) try
|
||||
{
|
||||
const json::object response
|
||||
{
|
||||
query.in.content
|
||||
};
|
||||
|
||||
const json::object &device_keys
|
||||
{
|
||||
response["device_keys"]
|
||||
};
|
||||
|
||||
for(const auto &[user_id, device_keys] : device_keys)
|
||||
{
|
||||
if(m::user::id(user_id).host() != remote)
|
||||
continue;
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
object, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id, keys] : json::object(device_keys))
|
||||
json::stack::member
|
||||
{
|
||||
user_object, device_id, keys
|
||||
};
|
||||
}
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
failures.emplace(remote, std::current_exception());
|
||||
log::derror
|
||||
{
|
||||
m::log, "Processing device_keys response from '%s' :%s",
|
||||
remote,
|
||||
e.what(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static std::tuple<string_view, bool>
|
||||
translate_cross_type(const string_view &name)
|
||||
{
|
||||
bool match_user;
|
||||
string_view cross_type;
|
||||
switch(match_user = false; hash(name))
|
||||
{
|
||||
case "master_keys"_:
|
||||
cross_type = "ircd.cross_signing.master";
|
||||
break;
|
||||
|
||||
case "self_signing_keys"_:
|
||||
cross_type = "ircd.cross_signing.self";
|
||||
break;
|
||||
|
||||
case "user_signing_keys"_:
|
||||
cross_type = "ircd.cross_signing.user";
|
||||
match_user = true;
|
||||
break;
|
||||
};
|
||||
|
||||
assert(cross_type);
|
||||
return
|
||||
{
|
||||
cross_type, match_user
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
handle_cross_keys(const m::resource::request &request,
|
||||
const user_devices_map &self,
|
||||
query_map &queries,
|
||||
failure_map &failures,
|
||||
json::stack::object &out_,
|
||||
const string_view &name)
|
||||
{
|
||||
const auto &[cross_type, match_user]
|
||||
{
|
||||
translate_cross_type(name)
|
||||
};
|
||||
|
||||
json::stack::object out
|
||||
{
|
||||
out_, name
|
||||
};
|
||||
|
||||
// local handle
|
||||
for(const auto &[user_id, device_ids] : self)
|
||||
{
|
||||
if(match_user && request.user_id != user_id)
|
||||
continue;
|
||||
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
if(!keys.has_cross(cross_type))
|
||||
continue;
|
||||
|
||||
json::stack::object user_object
|
||||
{
|
||||
out, user_id
|
||||
};
|
||||
|
||||
keys.cross(user_object, cross_type);
|
||||
}
|
||||
|
||||
// remote handle
|
||||
for(auto &[remote, query] : queries) try
|
||||
{
|
||||
if(match_user && request.user_id.host() != remote)
|
||||
continue;
|
||||
|
||||
const json::object response
|
||||
{
|
||||
query.in.content
|
||||
};
|
||||
|
||||
const json::object &object
|
||||
{
|
||||
response[name]
|
||||
};
|
||||
|
||||
for(const auto &[user_id, keys] : object)
|
||||
{
|
||||
if(m::user::id(user_id).host() != remote)
|
||||
continue;
|
||||
|
||||
if(match_user && request.user_id != user_id)
|
||||
continue;
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
out, user_id, json::object
|
||||
{
|
||||
keys
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch(const ctx::interrupted &)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
failures.emplace(remote, std::current_exception());
|
||||
log::derror
|
||||
{
|
||||
m::log, "Processing %s response from '%s' :%s",
|
||||
name,
|
||||
remote,
|
||||
e.what(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,71 +43,37 @@ ircd::m::resource::response
|
|||
ircd::m::post_keys_signatures_upload(client &client,
|
||||
const resource::request &request)
|
||||
{
|
||||
const m::device::id::buf device_id
|
||||
const auto src_dev
|
||||
{
|
||||
m::user::tokens::device(request.access_token)
|
||||
user::tokens::device(std::nothrow, request.access_token)
|
||||
};
|
||||
|
||||
const m::user::room user_room
|
||||
for(const auto &[user_id_, device_keys_] : request)
|
||||
{
|
||||
request.user_id
|
||||
};
|
||||
|
||||
for(const auto &[_user_id, devices_keys_] : request)
|
||||
{
|
||||
if(!valid(m::id::USER, _user_id))
|
||||
continue;
|
||||
|
||||
if(_user_id != request.user_id)
|
||||
throw m::ACCESS_DENIED
|
||||
{
|
||||
"Uploading for user %s by %s not allowed or supported",
|
||||
_user_id,
|
||||
string_view{request.user_id},
|
||||
};
|
||||
const json::object device_keys
|
||||
{
|
||||
device_keys_
|
||||
};
|
||||
|
||||
const m::user::id user_id
|
||||
{
|
||||
_user_id
|
||||
user_id_
|
||||
};
|
||||
|
||||
const m::user::devices devices
|
||||
const user::room user_room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const json::object &devices_keys
|
||||
for(const auto &[tgt_id, keys] : device_keys)
|
||||
{
|
||||
devices_keys_
|
||||
};
|
||||
|
||||
for(const auto &[_device_id, device_keys_] : devices_keys)
|
||||
{
|
||||
const m::device_keys device_keys
|
||||
char state_key_buf[512];
|
||||
const string_view state_key
|
||||
{
|
||||
device_keys_
|
||||
user::keys::make_sigs_state_key(state_key_buf, tgt_id, src_dev)
|
||||
};
|
||||
|
||||
if(json::get<"device_id"_>(device_keys) != _device_id)
|
||||
throw m::BAD_REQUEST
|
||||
{
|
||||
"device_id '%s' does not match object property name '%s'",
|
||||
json::get<"device_id"_>(device_keys),
|
||||
_device_id,
|
||||
};
|
||||
|
||||
if((false) && _device_id != device_id) // is this the "cross-sign?" gotta find out!
|
||||
throw m::ACCESS_DENIED
|
||||
{
|
||||
"device_id '%s' does not match your current device_id '%s'",
|
||||
_device_id,
|
||||
string_view{device_id},
|
||||
};
|
||||
|
||||
const bool set
|
||||
{
|
||||
devices.set(_device_id, "signatures", device_keys_)
|
||||
};
|
||||
send(user_room, request.user_id, "ircd.keys.signatures", state_key, keys);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,12 +121,6 @@ post__login_password(client &client,
|
|||
m::id::device::buf{m::id::generate, my_host()}
|
||||
};
|
||||
|
||||
if(!my(device_id))
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Device ID's with foreign hostparts are not supported."
|
||||
};
|
||||
|
||||
char access_token_buf[32];
|
||||
const string_view access_token
|
||||
{
|
||||
|
|
|
@ -206,7 +206,7 @@ ircd::m::get_notifications(client &client,
|
|||
{
|
||||
event_object, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.event_idx = event_idx,
|
||||
.keys = ¬ification_event_keys,
|
||||
//.query_txnid = false,
|
||||
//.query_prev_state = false,
|
||||
|
|
|
@ -134,6 +134,7 @@ get__publicrooms(client &client,
|
|||
opts.lower_bound = true;
|
||||
opts.room_id = since;
|
||||
opts.request_user_id = request.user_id;
|
||||
opts.room_type = json::string{filter["room_type"]};
|
||||
if(m::valid(m::id::USER, search_term))
|
||||
opts.user_id = search_term;
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ try
|
|||
// Send response to user
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED, response
|
||||
client, http::OK, response
|
||||
};
|
||||
}
|
||||
catch(const m::INVALID_MXID &e)
|
||||
|
@ -191,7 +191,7 @@ post__register_guest(client &client,
|
|||
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED,
|
||||
client, http::OK,
|
||||
{
|
||||
{ "user_id", user_id },
|
||||
{ "home_server", my_host() },
|
||||
|
@ -243,7 +243,7 @@ try
|
|||
// Send response to user
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED, response
|
||||
client, http::OK, response
|
||||
};
|
||||
}
|
||||
catch(const m::INVALID_MXID &e)
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#include "room_keys.h"
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
static string_view make_state_key(const mutable_buffer &, const string_view &, const string_view &, const event::idx &);
|
||||
|
||||
static resource::response _get_room_keys_keys(client &, const resource::request &, const room::state &, const event::idx &, const string_view &, const string_view &);
|
||||
static void _get_room_keys_keys(client &, const resource::request &, const room::state &, const event::idx &, const string_view &, json::stack::object &);
|
||||
static resource::response get_room_keys_keys(client &, const resource::request &);
|
||||
|
@ -21,18 +21,14 @@ namespace ircd::m
|
|||
static resource::response put_room_keys_keys(client &, const resource::request &);
|
||||
extern resource::method room_keys_keys_put;
|
||||
|
||||
static event::id::buf delete_room_keys_key(client &, const resource::request &, const room &, const event::idx &);
|
||||
static event::id::buf delete_room_keys_key(client &, const resource::request &, const room &, const room::id &, const string_view &, const event::idx &);
|
||||
static resource::response delete_room_keys_keys(client &, const resource::request &);
|
||||
extern resource::method room_keys_keys_delete;
|
||||
|
||||
extern resource room_keys_keys;
|
||||
}
|
||||
|
||||
ircd::mapi::header
|
||||
IRCD_MODULE
|
||||
{
|
||||
"Client (undocumented) :e2e Room Keys Keys"
|
||||
};
|
||||
|
||||
decltype(ircd::m::room_keys_keys)
|
||||
ircd::m::room_keys_keys
|
||||
{
|
||||
|
@ -79,7 +75,7 @@ ircd::m::delete_room_keys_keys(client &client,
|
|||
|
||||
const event::idx version
|
||||
{
|
||||
request.query.at<event::idx>("version")
|
||||
request.query.get<event::idx>("version", 0)
|
||||
};
|
||||
|
||||
const m::user::room user_room
|
||||
|
@ -92,15 +88,104 @@ ircd::m::delete_room_keys_keys(client &client,
|
|||
user_room
|
||||
};
|
||||
|
||||
if(!room_id && !session_id)
|
||||
{
|
||||
state.for_each("ircd.room_keys.key", [&client, &request, &user_room, &version]
|
||||
(const string_view &, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
const auto &[room_id, session_id, _version]
|
||||
{
|
||||
unmake_state_key(state_key)
|
||||
};
|
||||
|
||||
if(version && _version != lex_cast(version))
|
||||
return true;
|
||||
|
||||
delete_room_keys_key(client, request, user_room, event_idx);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else if(!session_id)
|
||||
{
|
||||
state.for_each("ircd.room_keys.key", [&client, &request, &user_room, &version, &room_id]
|
||||
(const string_view &, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
const auto &[_room_id, session_id, _version]
|
||||
{
|
||||
unmake_state_key(state_key)
|
||||
};
|
||||
|
||||
if(version && _version != lex_cast(version))
|
||||
return true;
|
||||
|
||||
if(_room_id != room_id)
|
||||
return true;
|
||||
|
||||
delete_room_keys_key(client, request, user_room, event_idx);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else delete_room_keys_key(client, request, user_room, room_id, session_id, version);
|
||||
|
||||
const auto &[count, etag]
|
||||
{
|
||||
count_etag(state, version)
|
||||
};
|
||||
|
||||
const json::value _etag
|
||||
{
|
||||
lex_cast(etag), json::STRING
|
||||
};
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, json::members
|
||||
{
|
||||
{ "count", count },
|
||||
{ "etag", _etag },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::delete_room_keys_key(client &client,
|
||||
const resource::request &request,
|
||||
const room &user_room,
|
||||
const room::id &room_id,
|
||||
const string_view &session_id,
|
||||
const event::idx &version)
|
||||
{
|
||||
char state_key_buf[event::STATE_KEY_MAX_SIZE];
|
||||
const string_view state_key
|
||||
{
|
||||
make_state_key(state_key_buf, room_id, session_id, version)
|
||||
};
|
||||
|
||||
const room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
{
|
||||
state.get(std::nothrow, "ircd.room_keys.key", state_key)
|
||||
};
|
||||
|
||||
if(!event_idx)
|
||||
return {};
|
||||
|
||||
return delete_room_keys_key(client, request, user_room, event_idx);
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::delete_room_keys_key(client &client,
|
||||
const resource::request &request,
|
||||
const room &user_room,
|
||||
const event::idx &event_idx)
|
||||
{
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(state.get("ircd.room_keys.key", state_key))
|
||||
m::event_id(event_idx)
|
||||
};
|
||||
|
||||
const auto redact_id
|
||||
|
@ -108,10 +193,7 @@ ircd::m::delete_room_keys_keys(client &client,
|
|||
m::redact(user_room, request.user_id, event_id, "deleted by client")
|
||||
};
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, http::OK
|
||||
};
|
||||
return redact_id;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -160,6 +242,16 @@ ircd::m::put_room_keys_keys(client &client,
|
|||
request.query.at<event::idx>("version")
|
||||
};
|
||||
|
||||
const m::user::room user_room
|
||||
{
|
||||
request.user_id
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
if(!room_id && !session_id)
|
||||
{
|
||||
const json::object &rooms
|
||||
|
@ -167,9 +259,16 @@ ircd::m::put_room_keys_keys(client &client,
|
|||
request["rooms"]
|
||||
};
|
||||
|
||||
for(const auto &[room_id, sessions] : rooms)
|
||||
for(const auto &[session_id, session] : json::object(sessions))
|
||||
for(const auto &[room_id, room_data] : rooms)
|
||||
{
|
||||
const json::object sessions
|
||||
{
|
||||
json::object(room_data)["sessions"]
|
||||
};
|
||||
|
||||
for(const auto &[session_id, session] : sessions)
|
||||
put_room_keys_keys_key(client, request, room_id, session_id, version, session);
|
||||
}
|
||||
}
|
||||
else if(!session_id)
|
||||
{
|
||||
|
@ -183,9 +282,23 @@ ircd::m::put_room_keys_keys(client &client,
|
|||
}
|
||||
else put_room_keys_keys_key(client, request, room_id, session_id, version, request);
|
||||
|
||||
const auto &[count, etag]
|
||||
{
|
||||
count_etag(state, version)
|
||||
};
|
||||
|
||||
const json::value _etag
|
||||
{
|
||||
lex_cast(etag), json::STRING
|
||||
};
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, http::OK
|
||||
client, json::members
|
||||
{
|
||||
{ "count", count },
|
||||
{ "etag", _etag },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -320,12 +433,12 @@ ircd::m::get_room_keys_keys(client &client,
|
|||
state.for_each("ircd.room_keys.key", [&client, &request, &state, &version, &rooms, &last_room]
|
||||
(const string_view &, const string_view &state_key, const event::idx &)
|
||||
{
|
||||
const auto &room_id
|
||||
const auto &[room_id, _session_id, _version]
|
||||
{
|
||||
token(state_key, ":::", 0)
|
||||
unmake_state_key(state_key)
|
||||
};
|
||||
|
||||
if(!m::valid(id::ROOM, room_id))
|
||||
if(_version != lex_cast(version))
|
||||
return true;
|
||||
|
||||
if(room_id == last_room)
|
||||
|
@ -359,29 +472,28 @@ ircd::m::_get_room_keys_keys(client &client,
|
|||
state.for_each("ircd.room_keys.key", [&room_id, &version, &sessions]
|
||||
(const string_view &type, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
string_view part[3]; const auto parts
|
||||
const auto &[_room_id, _session_id, _version]
|
||||
{
|
||||
tokens(state_key, ":::", part)
|
||||
unmake_state_key(state_key)
|
||||
};
|
||||
|
||||
const auto &_room_id{part[0]};
|
||||
const auto &_session_id{part[1]};
|
||||
const auto &_version{part[2]};
|
||||
if(!m::valid(id::ROOM, _room_id))
|
||||
return true;
|
||||
|
||||
if(_room_id != room_id)
|
||||
return true;
|
||||
|
||||
if(_version != lex_cast<event::idx>(version))
|
||||
if(_version != lex_cast(version))
|
||||
return true;
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&sessions, &_session_id]
|
||||
const string_view &session_id
|
||||
{
|
||||
_session_id
|
||||
};
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&sessions, &session_id]
|
||||
(const json::object &session)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
sessions, _session_id, session
|
||||
sessions, session_id, session
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -419,18 +531,3 @@ ircd::m::_get_room_keys_keys(client &client,
|
|||
|
||||
return {}; // responded from closure or thrown
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::make_state_key(const mutable_buffer &buf,
|
||||
const string_view &room_id,
|
||||
const string_view &session_id,
|
||||
const event::idx &version)
|
||||
{
|
||||
return fmt::sprintf
|
||||
{
|
||||
buf, "%s:::%s:::%u",
|
||||
room_id,
|
||||
session_id,
|
||||
version,
|
||||
};
|
||||
}
|
||||
|
|
94
modules/client/room_keys/room_keys.cc
Normal file
94
modules/client/room_keys/room_keys.cc
Normal file
|
@ -0,0 +1,94 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#include "room_keys.h"
|
||||
|
||||
ircd::mapi::header
|
||||
IRCD_MODULE
|
||||
{
|
||||
"Client :e2e Room Keys"
|
||||
};
|
||||
|
||||
std::tuple<int64_t, int64_t>
|
||||
ircd::m::count_etag(const room::state &state,
|
||||
const event::idx &version)
|
||||
{
|
||||
char version_buf[64];
|
||||
const auto version_str
|
||||
{
|
||||
lex_cast(version, version_buf)
|
||||
};
|
||||
|
||||
uint64_t count(0), etag(0);
|
||||
state.for_each("ircd.room_keys.key", [&]
|
||||
(const string_view &type, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
const auto &[room_id, session_id, _version_str]
|
||||
{
|
||||
unmake_state_key(state_key)
|
||||
};
|
||||
|
||||
if(_version_str != version_str)
|
||||
return true;
|
||||
|
||||
etag += event_idx;
|
||||
count += 1;
|
||||
return true;
|
||||
});
|
||||
|
||||
return
|
||||
{
|
||||
int64_t(count),
|
||||
int64_t(etag),
|
||||
};
|
||||
}
|
||||
|
||||
std::tuple<ircd::string_view, ircd::string_view, ircd::string_view>
|
||||
ircd::m::unmake_state_key(const string_view &state_key)
|
||||
{
|
||||
assert(state_key);
|
||||
string_view part[3];
|
||||
const auto parts
|
||||
{
|
||||
tokens(state_key, ":::", part)
|
||||
};
|
||||
|
||||
assert(parts == 3);
|
||||
if(unlikely(!m::valid(id::ROOM, part[0])))
|
||||
part[0] = {};
|
||||
|
||||
if(unlikely(!lex_castable<ulong>(part[2])))
|
||||
part[2] = {};
|
||||
|
||||
return std::make_tuple
|
||||
(
|
||||
part[0], part[1], part[2]
|
||||
);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::make_state_key(const mutable_buffer &buf,
|
||||
const string_view &room_id,
|
||||
const string_view &session_id,
|
||||
const event::idx &version)
|
||||
{
|
||||
assert(room_id);
|
||||
assert(m::valid(id::ROOM, room_id));
|
||||
assert(session_id);
|
||||
assert(session_id != "sessions");
|
||||
assert(version != 0);
|
||||
return fmt::sprintf
|
||||
{
|
||||
buf, "%s:::%s:::%u",
|
||||
room_id,
|
||||
session_id,
|
||||
version,
|
||||
};
|
||||
}
|
18
modules/client/room_keys/room_keys.h
Normal file
18
modules/client/room_keys/room_keys.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
// The Construct
|
||||
//
|
||||
// Copyright (C) The Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#pragma GCC visibility push(hidden)
|
||||
namespace ircd::m
|
||||
{
|
||||
string_view make_state_key(const mutable_buffer &, const string_view &, const string_view &, const event::idx &);
|
||||
std::tuple<string_view, string_view, string_view> unmake_state_key(const string_view &);
|
||||
std::tuple<int64_t, int64_t> count_etag(const room::state &, const event::idx &version);
|
||||
}
|
||||
#pragma GCC visibility pop
|
|
@ -8,6 +8,8 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
#include "room_keys.h"
|
||||
|
||||
namespace ircd::m
|
||||
{
|
||||
static resource::response get_room_keys_version(client &, const resource::request &);
|
||||
|
@ -25,12 +27,6 @@ namespace ircd::m
|
|||
extern resource room_keys_version;
|
||||
}
|
||||
|
||||
ircd::mapi::header
|
||||
IRCD_MODULE
|
||||
{
|
||||
"Client (undocumented) :e2e Room Keys Version"
|
||||
};
|
||||
|
||||
decltype(ircd::m::room_keys_version)
|
||||
ircd::m::room_keys_version
|
||||
{
|
||||
|
@ -209,11 +205,6 @@ ircd::m::put_room_keys_version(client &client,
|
|||
event_idx,
|
||||
};
|
||||
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(event_idx)
|
||||
};
|
||||
|
||||
const json::string &algorithm
|
||||
{
|
||||
request["algorithm"]
|
||||
|
@ -224,13 +215,36 @@ ircd::m::put_room_keys_version(client &client,
|
|||
request["auth_data"]
|
||||
};
|
||||
|
||||
//
|
||||
// TODO: XXX
|
||||
//
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(event_idx)
|
||||
};
|
||||
|
||||
const json::member relates[]
|
||||
{
|
||||
{ "event_id", event_id },
|
||||
{ "rel_type", "m.replace" },
|
||||
};
|
||||
|
||||
const json::strung content
|
||||
{
|
||||
json::insert(request, json::members
|
||||
{
|
||||
{ "m.relates_to", relates }
|
||||
})
|
||||
};
|
||||
|
||||
const auto update_id
|
||||
{
|
||||
m::send(user_room, request.user_id, "ircd.room_keys.version", json::object
|
||||
{
|
||||
content
|
||||
})
|
||||
};
|
||||
|
||||
return resource::response
|
||||
{
|
||||
client, http::NOT_IMPLEMENTED
|
||||
client, http::OK
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -301,9 +315,36 @@ ircd::m::get_room_keys_version(client &client,
|
|||
"No version found.",
|
||||
};
|
||||
|
||||
m::get(event_idx, "content", [&client, &event_idx]
|
||||
const m::replaced latest_idx
|
||||
{
|
||||
event_idx, m::replaced::latest
|
||||
};
|
||||
|
||||
const event::idx version_idx
|
||||
{
|
||||
latest_idx?
|
||||
event::idx{latest_idx}:
|
||||
event_idx
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
m::get(version_idx, "content", [&client, &event_idx, &state]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto &[count, etag]
|
||||
{
|
||||
count_etag(state, event_idx)
|
||||
};
|
||||
|
||||
const json::value _etag
|
||||
{
|
||||
lex_cast(etag), json::STRING
|
||||
};
|
||||
|
||||
const json::value version
|
||||
{
|
||||
lex_cast(event_idx), json::STRING
|
||||
|
@ -313,9 +354,11 @@ ircd::m::get_room_keys_version(client &client,
|
|||
{
|
||||
client, json::members
|
||||
{
|
||||
{ "version", version },
|
||||
{ "algorithm", content["algorithm"] },
|
||||
{ "auth_data", content["auth_data"] },
|
||||
{ "count", count },
|
||||
{ "etag", _etag },
|
||||
{ "version", version },
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -132,10 +132,10 @@ get__context(client &client,
|
|||
{
|
||||
_event, event,
|
||||
{
|
||||
.event_idx = &event.event_idx,
|
||||
.user_id = &user_room.user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.event_idx = event.event_idx,
|
||||
.user_id = user_room.user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -179,10 +179,10 @@ get__context(client &client,
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &user_room.user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = user_room.user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
.query_txnid = true,
|
||||
}
|
||||
};
|
||||
|
@ -233,10 +233,10 @@ get__context(client &client,
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &user_room.user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = user_room.user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
.query_txnid = true,
|
||||
}
|
||||
};
|
||||
|
@ -301,10 +301,10 @@ get__context(client &client,
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &user_room.user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = user_room.user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
.query_txnid = false,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -46,8 +46,32 @@ get__event(client &client,
|
|||
event_id, fopts
|
||||
};
|
||||
|
||||
const unique_mutable_buffer buf
|
||||
{
|
||||
m::event::MAX_SIZE
|
||||
};
|
||||
|
||||
json::stack out{buf};
|
||||
{
|
||||
json::stack::object top{out};
|
||||
m::event::append
|
||||
{
|
||||
top, event,
|
||||
{
|
||||
.event_idx = event.event_idx,
|
||||
.user_id = request.user_id,
|
||||
.query_prev_state = false,
|
||||
.query_redacted = false,
|
||||
.query_visible = false,
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return m::resource::response
|
||||
{
|
||||
client, event.source
|
||||
client, json::object
|
||||
{
|
||||
out.completed()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -202,10 +202,10 @@ get__initialsync_local(client &client,
|
|||
{
|
||||
state, state_event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
.query_txnid = false,
|
||||
}
|
||||
};
|
||||
|
@ -255,14 +255,17 @@ get__initialsync_local(client &client,
|
|||
if(!visible(event, user.user_id))
|
||||
continue;
|
||||
|
||||
m::event::append(chunk, event,
|
||||
m::event::append
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.query_txnid = true,
|
||||
});
|
||||
chunk, event,
|
||||
{
|
||||
.event_idx = event_idx,
|
||||
.user_id = user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
.query_txnid = true,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -165,8 +165,8 @@ get__members(client &client,
|
|||
{
|
||||
chunk, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &request.user_id,
|
||||
.event_idx = event_idx,
|
||||
.user_id = request.user_id,
|
||||
.query_txnid = false,
|
||||
.query_prev_state = false,
|
||||
.query_redacted = false,
|
||||
|
|
|
@ -152,10 +152,10 @@ get__messages(client &client,
|
|||
{
|
||||
chunk, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &user_room.user.user_id,
|
||||
.user_room = &user_room,
|
||||
.room_depth = &room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = user_room.user.user_id,
|
||||
.user_room_id = user_room.room_id,
|
||||
.room_depth = room_depth,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -60,9 +60,11 @@ get__relations(client &client,
|
|||
"relation rel_type path parameter required"
|
||||
};
|
||||
|
||||
const string_view &rel_type
|
||||
const string_view rel_type
|
||||
{
|
||||
url::decode(rel_type_buf, request.parv[3])
|
||||
request.parv.size() > 3?
|
||||
url::decode(rel_type_buf, request.parv[3]):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
// Get the alleged type path parameter.
|
||||
|
@ -75,9 +77,11 @@ get__relations(client &client,
|
|||
"relation ?type? path parameter required"
|
||||
};
|
||||
|
||||
const string_view &type
|
||||
const string_view type
|
||||
{
|
||||
url::decode(type_buf, request.parv[4])
|
||||
request.parv.size() > 4?
|
||||
url::decode(type_buf, request.parv[4]):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
|
@ -170,8 +174,8 @@ relations_chunk_append(client &client,
|
|||
{
|
||||
chunk, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &request.user_id,
|
||||
.event_idx = event_idx,
|
||||
.user_id = request.user_id,
|
||||
.query_txnid = false,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -206,8 +206,8 @@ append_event(const m::resource::request &request,
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &request.user_id,
|
||||
.event_idx = event_idx,
|
||||
.user_id = request.user_id,
|
||||
.query_txnid = false,
|
||||
.query_prev_state = false,
|
||||
.query_redacted = false,
|
||||
|
|
|
@ -550,8 +550,8 @@ try
|
|||
{
|
||||
result_event, event,
|
||||
{
|
||||
.event_idx = &result.event_idx,
|
||||
.user_id = &query.user_id,
|
||||
.event_idx = result.event_idx,
|
||||
.user_id = query.user_id,
|
||||
.event_filter = &event_filter,
|
||||
.query_prev_state = false,
|
||||
.query_visible = true,
|
||||
|
@ -599,8 +599,8 @@ try
|
|||
{
|
||||
events_before, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &query.user_id,
|
||||
.event_idx = event_idx,
|
||||
.user_id = query.user_id,
|
||||
.event_filter = &event_filter,
|
||||
.query_prev_state = false,
|
||||
.query_visible = true,
|
||||
|
@ -628,8 +628,8 @@ try
|
|||
{
|
||||
events_after, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &query.user_id,
|
||||
.event_idx = event_idx,
|
||||
.user_id = query.user_id,
|
||||
.event_filter = &event_filter,
|
||||
.query_prev_state = false,
|
||||
.query_visible = true,
|
||||
|
|
|
@ -38,7 +38,21 @@ ircd::m::sync::device_lists_linear(data &data)
|
|||
|
||||
assert(data.event);
|
||||
const m::event &event{*data.event};
|
||||
if(!startswith(json::get<"type"_>(event), "ircd.device"))
|
||||
|
||||
const bool including
|
||||
{
|
||||
false
|
||||
|| startswith(json::get<"type"_>(event), "ircd.device")
|
||||
|| startswith(json::get<"type"_>(event), "ircd.keys.signatures")
|
||||
};
|
||||
|
||||
const bool excluding
|
||||
{
|
||||
false
|
||||
|| startswith(json::get<"type"_>(event), "ircd.device.one_time_key")
|
||||
};
|
||||
|
||||
if(!including || excluding)
|
||||
return false;
|
||||
|
||||
const m::user sender
|
||||
|
@ -56,7 +70,9 @@ ircd::m::sync::device_lists_linear(data &data)
|
|||
|
||||
const bool changed
|
||||
{
|
||||
mitsein.has(data.user, "join")
|
||||
false
|
||||
|| sender == data.user.user_id
|
||||
|| mitsein.has(data.user, "join")
|
||||
};
|
||||
|
||||
const bool left
|
||||
|
@ -67,6 +83,11 @@ ircd::m::sync::device_lists_linear(data &data)
|
|||
if(!changed && !left)
|
||||
return false;
|
||||
|
||||
json::stack::object device_lists
|
||||
{
|
||||
*data.out, "device_lists"
|
||||
};
|
||||
|
||||
json::stack::array array
|
||||
{
|
||||
*data.out, left? "left": "changed"
|
||||
|
|
64
modules/client/sync/device_unused_fallback_key_types.cc
Normal file
64
modules/client/sync/device_unused_fallback_key_types.cc
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2023 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
namespace ircd::m::sync
|
||||
{
|
||||
static bool device_unused_fallback_key_types_polylog(data &);
|
||||
static bool device_unused_fallback_key_types_linear(data &);
|
||||
|
||||
extern item device_unused_fallback_key_types;
|
||||
}
|
||||
|
||||
ircd::mapi::header
|
||||
IRCD_MODULE
|
||||
{
|
||||
"Client Sync :Device Unused Fallback Key Types"
|
||||
};
|
||||
|
||||
decltype(ircd::m::sync::device_unused_fallback_key_types)
|
||||
ircd::m::sync::device_unused_fallback_key_types
|
||||
{
|
||||
"device_unused_fallback_key_types",
|
||||
device_unused_fallback_key_types_polylog,
|
||||
device_unused_fallback_key_types_linear
|
||||
};
|
||||
|
||||
bool
|
||||
ircd::m::sync::device_unused_fallback_key_types_linear(data &data)
|
||||
{
|
||||
if(!data.device_id)
|
||||
return false;
|
||||
|
||||
if(!data.event || !data.event->event_id)
|
||||
return false;
|
||||
|
||||
if(!startswith(json::get<"type"_>(*data.event), "ircd.device"))
|
||||
return false;
|
||||
|
||||
if(json::get<"room_id"_>(*data.event) != data.user_room)
|
||||
return false;
|
||||
|
||||
json::stack::array array
|
||||
{
|
||||
*data.out, "device_unused_fallback_key_types"
|
||||
};
|
||||
|
||||
array.append("signed_curve25519");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::sync::device_unused_fallback_key_types_polylog(data &data)
|
||||
{
|
||||
if(!data.device_id)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
|
@ -221,10 +221,10 @@ ircd::m::sync::room_state_linear_events(data &data)
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
.query_txnid = false,
|
||||
.query_prev_state = true,
|
||||
}
|
||||
|
@ -265,10 +265,10 @@ ircd::m::sync::room_state_linear_events(data &data)
|
|||
{
|
||||
array, *data.event,
|
||||
{
|
||||
.event_idx = &data.event_idx,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = data.event_idx,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
.query_txnid = false,
|
||||
.query_prev_state = true,
|
||||
}
|
||||
|
@ -377,10 +377,10 @@ ircd::m::sync::room_state_polylog_events(data &data)
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
.query_txnid = false,
|
||||
.query_prev_state = false,
|
||||
}
|
||||
|
@ -517,10 +517,10 @@ ircd::m::sync::room_state_phased_events(data &data)
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = event_idx,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
.query_txnid = false,
|
||||
.query_prev_state = true,
|
||||
}
|
||||
|
@ -664,10 +664,10 @@ ircd::m::sync::room_state_phased_member_events(data &data,
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &sender_idx,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = sender_idx,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
.query_txnid = false,
|
||||
.query_prev_state = false,
|
||||
}
|
||||
|
|
|
@ -212,11 +212,11 @@ ircd::m::sync::room_timeline_linear(data &data)
|
|||
{
|
||||
array, *data.event,
|
||||
{
|
||||
.event_idx = &data.event_idx,
|
||||
.client_txnid = &data.client_txnid,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = data.event_idx,
|
||||
.client_txnid = data.client_txnid,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -289,11 +289,11 @@ ircd::m::sync::_room_timeline_linear_command(data &data)
|
|||
{
|
||||
array, *data.event,
|
||||
{
|
||||
.event_idx = &data.event_idx,
|
||||
.client_txnid = &data.client_txnid,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = data.event_idx,
|
||||
.client_txnid = data.client_txnid,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -400,11 +400,11 @@ ircd::m::sync::_room_timeline_polylog_events(data &data,
|
|||
{
|
||||
array, event,
|
||||
{
|
||||
.event_idx = &event_idx,
|
||||
.client_txnid = &data.client_txnid,
|
||||
.user_id = &data.user.user_id,
|
||||
.user_room = &data.user_room,
|
||||
.room_depth = &data.room_depth,
|
||||
.event_idx = event_idx,
|
||||
.client_txnid = data.client_txnid,
|
||||
.user_id = data.user.user_id,
|
||||
.user_room_id = data.user_room.room_id,
|
||||
.room_depth = data.room_depth,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ post__filter(client &client,
|
|||
|
||||
return m::resource::response
|
||||
{
|
||||
client, http::CREATED,
|
||||
client, http::OK,
|
||||
{
|
||||
{ "filter_id", filter_id }
|
||||
}
|
||||
|
|
|
@ -43,17 +43,17 @@ get_user(client &client,
|
|||
url::decode(user_id, request.parv[0])
|
||||
};
|
||||
|
||||
if(request.user_id != user_id)
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Getting user data as someone else is not yet supported"
|
||||
};
|
||||
|
||||
const string_view &cmd
|
||||
{
|
||||
request.parv[1]
|
||||
};
|
||||
|
||||
if(request.user_id != user_id && !request.bridge_id)
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Getting user data as someone else is only for bridges."
|
||||
};
|
||||
|
||||
if(cmd == "filter")
|
||||
return get__filter(client, request, user_id);
|
||||
|
||||
|
@ -93,10 +93,10 @@ post_user(client &client,
|
|||
url::decode(user_id, request.parv[0])
|
||||
};
|
||||
|
||||
if(request.user_id != user_id)
|
||||
if(request.user_id != user_id && !request.bridge_id)
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Posting user data as someone else is not yet supported"
|
||||
"Posting user data as someone else is only for bridges."
|
||||
};
|
||||
|
||||
const string_view &cmd
|
||||
|
@ -140,10 +140,10 @@ put_user(client &client,
|
|||
url::decode(user_id, request.parv[0])
|
||||
};
|
||||
|
||||
if(request.user_id != user_id)
|
||||
if(request.user_id != user_id && !request.bridge_id)
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Putting user data as someone else is not yet supported"
|
||||
"Putting user data as someone else is only for bridges."
|
||||
};
|
||||
|
||||
if(request.parv.size() < 2)
|
||||
|
@ -193,10 +193,10 @@ delete_user(client &client,
|
|||
url::decode(user_id, request.parv[0])
|
||||
};
|
||||
|
||||
if(request.user_id != user_id)
|
||||
if(request.user_id != user_id && !request.bridge_id)
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Deleting user data as someone else is not yet supported"
|
||||
"Deleting user data as someone else is only for bridges."
|
||||
};
|
||||
|
||||
if(request.parv.size() < 2)
|
||||
|
|
|
@ -85,6 +85,7 @@ ircd::m::client_versions::versions_default
|
|||
" v1.3"
|
||||
" v1.4"
|
||||
" v1.5"
|
||||
" v1.6"
|
||||
};
|
||||
|
||||
/// Note this conf item doesn't persist to and from the database, which means
|
||||
|
@ -240,4 +241,13 @@ ircd::m::client_versions::append_unstable_features(client &client,
|
|||
bool(e2ee_forced_trusted_private)
|
||||
}
|
||||
};
|
||||
|
||||
// Supports filtering of /publicRooms by room type as per MSC3827
|
||||
json::stack::member
|
||||
{
|
||||
out, "org.matrix.msc3827.stable", json::value
|
||||
{
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4596,7 +4596,7 @@ static void
|
|||
_print_sst_info_header(opt &out)
|
||||
{
|
||||
out << std::left << std::setfill(' ')
|
||||
<< std::setw(3) << "chkp"
|
||||
<< std::setw(3) << "cp"
|
||||
<< " " << std::setw(12) << "name"
|
||||
<< " " << std::setw(32) << "creation"
|
||||
<< " " << std::setw(3) << "flt"
|
||||
|
@ -11455,31 +11455,47 @@ console_cmd__room__count(opt &out, const string_view &line)
|
|||
bool
|
||||
console_cmd__room__events(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
const params param_any{line, " ",
|
||||
{
|
||||
"room_id", "depth|-limit", "order", "limit"
|
||||
}};
|
||||
|
||||
const params param_type{line, " ",
|
||||
{
|
||||
"room_id", "type", "depth|-limit", "order", "limit"
|
||||
}};
|
||||
|
||||
const bool use_type
|
||||
{
|
||||
param_type["type"] && !lex_castable<int64_t>(param_type["type"])
|
||||
};
|
||||
|
||||
// decide which argument overload to use
|
||||
const params ¶m
|
||||
{
|
||||
use_type? param_type : param_any
|
||||
};
|
||||
|
||||
const auto &room_id
|
||||
{
|
||||
m::room_id(param.at(0))
|
||||
m::room_id(param.at("room_id"))
|
||||
};
|
||||
|
||||
const int64_t depth
|
||||
{
|
||||
param.at<int64_t>(1, std::numeric_limits<int64_t>::max())
|
||||
param.at<int64_t>("depth|-limit", std::numeric_limits<int64_t>::max())
|
||||
};
|
||||
|
||||
const char order
|
||||
{
|
||||
param.at(2, "b"_sv).at(0)
|
||||
param.at("order", "b"_sv).at(0)
|
||||
};
|
||||
|
||||
ssize_t limit
|
||||
{
|
||||
depth < 0?
|
||||
std::abs(depth):
|
||||
param.at(3, ssize_t(32))
|
||||
param.at("depth|-limit", ssize_t(32))
|
||||
};
|
||||
|
||||
const m::room room
|
||||
|
@ -11493,15 +11509,21 @@ console_cmd__room__events(opt &out, const string_view &line)
|
|||
};
|
||||
|
||||
m::event::fetch event;
|
||||
for(; it && limit > 0; order == 'b'? --it : ++it, --limit)
|
||||
for(; it && limit > 0; order == 'b'? --it : ++it)
|
||||
{
|
||||
if(!seek(std::nothrow, event, it.event_idx()))
|
||||
continue;
|
||||
|
||||
if(use_type)
|
||||
if(!globular_imatch(param["type"])(json::get<"type"_>(event)))
|
||||
continue;
|
||||
|
||||
out
|
||||
<< std::left << std::setw(10) << it.event_idx() << " "
|
||||
<< pretty_oneline(event)
|
||||
<< std::endl;
|
||||
|
||||
--limit;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -13312,9 +13334,9 @@ console_cmd__user__read(opt &out, const string_view &line)
|
|||
"user_id", "room_id", "limit"
|
||||
}};
|
||||
|
||||
const m::user::id user_id
|
||||
const auto user_id
|
||||
{
|
||||
param.at("user_id")
|
||||
param.at("user_id", "*"_sv)
|
||||
};
|
||||
|
||||
const auto room_id
|
||||
|
@ -13324,6 +13346,11 @@ console_cmd__user__read(opt &out, const string_view &line)
|
|||
m::room::id::buf{}
|
||||
};
|
||||
|
||||
const bool all_users
|
||||
{
|
||||
param["user_id"] == "*"
|
||||
};
|
||||
|
||||
const bool all_rooms
|
||||
{
|
||||
param["room_id"] == "*"
|
||||
|
@ -13340,14 +13367,9 @@ console_cmd__user__read(opt &out, const string_view &line)
|
|||
param["room_id"] == "***"
|
||||
};
|
||||
|
||||
size_t limit
|
||||
ssize_t limit
|
||||
{
|
||||
param.at("limit", 32UL)
|
||||
};
|
||||
|
||||
const m::user::room user_room
|
||||
{
|
||||
user_id
|
||||
param.at("limit", 32L)
|
||||
};
|
||||
|
||||
const m::event::closure each_event{[&out]
|
||||
|
@ -13411,19 +13433,35 @@ console_cmd__user__read(opt &out, const string_view &line)
|
|||
out << std::endl;
|
||||
}};
|
||||
|
||||
if(all_rooms)
|
||||
if(all_rooms && !all_users)
|
||||
{
|
||||
const m::user::room user_room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
state.for_each("ircd.read", each_event);
|
||||
state.for_each("ircd.read", m::event::closure_bool{[&each_event, &limit]
|
||||
(const m::event &event)
|
||||
{
|
||||
each_event(event);
|
||||
return --limit > 0;
|
||||
}});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(eye_track)
|
||||
if(eye_track && !all_users)
|
||||
{
|
||||
const m::user::room user_room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const m::room::type type
|
||||
{
|
||||
user_room, "ircd.read"
|
||||
|
@ -13440,14 +13478,19 @@ console_cmd__user__read(opt &out, const string_view &line)
|
|||
if(likely(event.valid))
|
||||
each_event(event);
|
||||
|
||||
return --limit;
|
||||
return --limit > 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(fully_read)
|
||||
if(fully_read && !all_users)
|
||||
{
|
||||
const m::user::room user_room
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const m::room::type type
|
||||
{
|
||||
user_room, "ircd.account_data!", { -1UL, -1UL }, true
|
||||
|
@ -13468,29 +13511,55 @@ console_cmd__user__read(opt &out, const string_view &line)
|
|||
return true;
|
||||
|
||||
each_event(event);
|
||||
return --limit;
|
||||
return --limit > 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const m::room::state::space space
|
||||
if(!all_users)
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
space.for_each("ircd.read", room_id, [&each_event, &limit]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx) -> bool
|
||||
{
|
||||
const m::event::fetch event
|
||||
const m::user::room user_room
|
||||
{
|
||||
std::nothrow, event_idx
|
||||
user_id
|
||||
};
|
||||
|
||||
if(likely(event.valid))
|
||||
each_event(event);
|
||||
const m::room::state::space space
|
||||
{
|
||||
user_room
|
||||
};
|
||||
|
||||
return --limit;
|
||||
space.for_each("ircd.read", room_id, [&each_event, &limit]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx) -> bool
|
||||
{
|
||||
const m::event::fetch event
|
||||
{
|
||||
std::nothrow, event_idx
|
||||
};
|
||||
|
||||
if(likely(event.valid))
|
||||
each_event(event);
|
||||
|
||||
return --limit > 0;
|
||||
});
|
||||
}
|
||||
|
||||
const m::events::range range
|
||||
{
|
||||
-1UL, 0UL
|
||||
};
|
||||
|
||||
m::events::for_each(range, [&each_event, &limit]
|
||||
(const m::event::idx &seq, const m::event &event)
|
||||
{
|
||||
if(json::get<"type"_>(event) != "ircd.read")
|
||||
return true;
|
||||
|
||||
if(!my(event))
|
||||
return true;
|
||||
|
||||
each_event(event);
|
||||
return --limit > 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -13894,7 +13963,7 @@ console_cmd__user__tokens(opt &out, const string_view &line)
|
|||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "clear"
|
||||
"user_id"
|
||||
}};
|
||||
|
||||
const m::user user
|
||||
|
@ -13902,27 +13971,11 @@ console_cmd__user__tokens(opt &out, const string_view &line)
|
|||
param.at("user_id")
|
||||
};
|
||||
|
||||
const bool clear
|
||||
{
|
||||
param["clear"] == "clear"
|
||||
};
|
||||
|
||||
const m::user::tokens tokens
|
||||
{
|
||||
user
|
||||
};
|
||||
|
||||
if(clear)
|
||||
{
|
||||
const size_t count
|
||||
{
|
||||
tokens.del("Invalidated by administrator console.")
|
||||
};
|
||||
|
||||
out << "Invalidated " << count << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
tokens.for_each([&out]
|
||||
(const m::event::idx &event_idx, const string_view &token)
|
||||
{
|
||||
|
@ -13963,6 +14016,33 @@ console_cmd__user__tokens(opt &out, const string_view &line)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__tokens__clear(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id"
|
||||
}};
|
||||
|
||||
const m::user user
|
||||
{
|
||||
param.at("user_id")
|
||||
};
|
||||
|
||||
const m::user::tokens tokens
|
||||
{
|
||||
user
|
||||
};
|
||||
|
||||
const size_t count
|
||||
{
|
||||
tokens.del("Invalidated by administrator console.")
|
||||
};
|
||||
|
||||
out << "Invalidated " << count << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__profile(opt &out, const string_view &line)
|
||||
{
|
||||
|
@ -14324,11 +14404,11 @@ console_cmd__user__devices(opt &out, const string_view &line)
|
|||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__devices__update(opt &out, const string_view &line)
|
||||
console_cmd__user__devices__delete(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "device_id", "deleted"
|
||||
"user_id", "device_id"
|
||||
}};
|
||||
|
||||
const m::user::id &user_id
|
||||
|
@ -14338,12 +14418,7 @@ console_cmd__user__devices__update(opt &out, const string_view &line)
|
|||
|
||||
const string_view &device_id
|
||||
{
|
||||
param.at("device_id")
|
||||
};
|
||||
|
||||
const bool deleted
|
||||
{
|
||||
param["deleted"] == "deleted"
|
||||
param.at("device_id", string_view{})
|
||||
};
|
||||
|
||||
const m::user::devices devices
|
||||
|
@ -14351,20 +14426,84 @@ console_cmd__user__devices__update(opt &out, const string_view &line)
|
|||
user_id
|
||||
};
|
||||
|
||||
json::iov content;
|
||||
const json::iov::push push[]
|
||||
if(device_id)
|
||||
{
|
||||
{ content, { "user_id", user_id } },
|
||||
{ content, { "device_id", device_id } },
|
||||
{ content, { "deleted", deleted } },
|
||||
devices.del(device_id);
|
||||
out << device_id << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
devices.for_each([&out, &devices]
|
||||
(const auto &event_idx, const string_view &device_id)
|
||||
{
|
||||
devices.del(device_id);
|
||||
out << device_id << std::endl;
|
||||
return true;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__devices__update(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "device_id", "room_id"
|
||||
}};
|
||||
|
||||
const m::user::id &user_id
|
||||
{
|
||||
param.at("user_id")
|
||||
};
|
||||
|
||||
const bool broadcasted
|
||||
const string_view &device_id
|
||||
{
|
||||
m::user::devices::send(content)
|
||||
param.at("device_id", "*"_sv)
|
||||
};
|
||||
|
||||
out << "done" << std::endl;
|
||||
const m::room::id::buf room_id
|
||||
{
|
||||
m::valid(m::id::ROOM, param["room_id"])?
|
||||
m::room_id(param["room_id"]):
|
||||
m::room::id::buf{}
|
||||
};
|
||||
|
||||
const m::user::devices devices
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const auto update{[&out, &devices, &user_id, &room_id]
|
||||
(const auto &device_id)
|
||||
{
|
||||
m::user::devices::send
|
||||
{
|
||||
devices, device_id, room_id
|
||||
};
|
||||
|
||||
out
|
||||
<< "broadcast: "
|
||||
<< device_id
|
||||
<< std::endl;
|
||||
}};
|
||||
|
||||
const bool found
|
||||
{
|
||||
!devices.for_each([&update, &device_id]
|
||||
(const auto &, const string_view &_device_id)
|
||||
{
|
||||
if(device_id != "*" && _device_id != device_id)
|
||||
return true;
|
||||
|
||||
update(_device_id);
|
||||
return _device_id != device_id; // false to break
|
||||
})
|
||||
};
|
||||
|
||||
if(device_id != "*" && !found)
|
||||
update(device_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -14376,6 +14515,43 @@ console_id__device(opt &out,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__keys__update(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "room_id"
|
||||
}};
|
||||
|
||||
const m::user::id &user_id
|
||||
{
|
||||
param.at("user_id")
|
||||
};
|
||||
|
||||
const m::room::id::buf room_id
|
||||
{
|
||||
m::valid(m::id::ROOM, param["room_id"])?
|
||||
m::room_id(param["room_id"]):
|
||||
m::room::id::buf{}
|
||||
};
|
||||
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
m::user::keys::send
|
||||
{
|
||||
keys, room_id
|
||||
};
|
||||
|
||||
out
|
||||
<< "broadcast: "
|
||||
<< user_id
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__user__ignores(opt &out, const string_view &line)
|
||||
{
|
||||
|
@ -15058,7 +15234,7 @@ console_cmd__feds__head(opt &out, const string_view &line)
|
|||
if(prev_event.valid)
|
||||
out << pretty_oneline(prev_event);
|
||||
else
|
||||
out << string_view{prev_event_id};
|
||||
out << result.request->room_id << ' ' << prev_event_id;
|
||||
|
||||
out << std::endl;
|
||||
}
|
||||
|
@ -17084,7 +17260,7 @@ console_cmd__fed__user__devices(opt &out, const string_view &line)
|
|||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"user_id", "remote"
|
||||
"user_id", "remote", "op"
|
||||
}};
|
||||
|
||||
const m::user::id &user_id
|
||||
|
@ -17097,6 +17273,11 @@ console_cmd__fed__user__devices(opt &out, const string_view &line)
|
|||
param.at("remote", user_id.host())
|
||||
};
|
||||
|
||||
const bool raw
|
||||
{
|
||||
has(param["op"], "raw")
|
||||
};
|
||||
|
||||
m::fed::user::devices::opts opts;
|
||||
opts.remote = remote;
|
||||
|
||||
|
@ -17121,6 +17302,14 @@ console_cmd__fed__user__devices(opt &out, const string_view &line)
|
|||
request
|
||||
};
|
||||
|
||||
if(raw)
|
||||
{
|
||||
out
|
||||
<< string_view{response}
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
const string_view stream_id
|
||||
{
|
||||
unquote(response["stream_id"])
|
||||
|
@ -17151,16 +17340,23 @@ console_cmd__fed__user__keys__query(opt &out, const string_view &line)
|
|||
param.at("user_id")
|
||||
};
|
||||
|
||||
const string_view &device_id
|
||||
{
|
||||
param.at("device_id", string_view{})
|
||||
};
|
||||
|
||||
const string_view remote
|
||||
{
|
||||
param.at("remote", user_id.host())
|
||||
};
|
||||
|
||||
const bool raw
|
||||
{
|
||||
param["device_id"] == "raw"
|
||||
};
|
||||
|
||||
const string_view device_id
|
||||
{
|
||||
!raw?
|
||||
param.at("device_id", string_view{}):
|
||||
string_view{}
|
||||
};
|
||||
|
||||
m::fed::user::opts opts;
|
||||
opts.remote = remote;
|
||||
|
||||
|
@ -17185,6 +17381,14 @@ console_cmd__fed__user__keys__query(opt &out, const string_view &line)
|
|||
request
|
||||
};
|
||||
|
||||
if(raw)
|
||||
{
|
||||
out
|
||||
<< string_view{response}
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
const json::object &device_keys
|
||||
{
|
||||
response["device_keys"]
|
||||
|
@ -18067,6 +18271,183 @@ console_cmd__redact(opt &out, const string_view &line)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__redact__last(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"room_id", "sender", "redactor", "count", "type", "reason"
|
||||
}};
|
||||
|
||||
const m::room::id::buf room_id
|
||||
{
|
||||
m::room_id(param["room_id"])
|
||||
};
|
||||
|
||||
const m::room room
|
||||
{
|
||||
room_id
|
||||
};
|
||||
|
||||
const m::user::id sender
|
||||
{
|
||||
param["sender"]
|
||||
};
|
||||
|
||||
const m::user::id redactor
|
||||
{
|
||||
param["redactor"]
|
||||
};
|
||||
|
||||
const auto type
|
||||
{
|
||||
param["type"]
|
||||
};
|
||||
|
||||
auto count
|
||||
{
|
||||
param.at<uint>("count", 1)
|
||||
};
|
||||
|
||||
m::room::iterate events
|
||||
{
|
||||
room, "sender"
|
||||
};
|
||||
|
||||
size_t limit(10000);
|
||||
events.for_each([&]
|
||||
(const auto &_sender, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
if(--limit <= 0)
|
||||
return false;
|
||||
|
||||
if(_sender != sender)
|
||||
return true;
|
||||
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(std::nothrow, event_idx)
|
||||
};
|
||||
|
||||
if(unlikely(!event_id))
|
||||
return true;
|
||||
|
||||
const auto type_equal{[&type]
|
||||
(const auto &t)
|
||||
{
|
||||
return type == t;
|
||||
}};
|
||||
|
||||
if(type && !m::query(event_idx, "type", type_equal))
|
||||
return true;
|
||||
|
||||
const auto redact_id
|
||||
{
|
||||
m::redact(room, redactor, event_id, param["reason"])
|
||||
};
|
||||
|
||||
out
|
||||
<< std::setw(3) << std::right << count
|
||||
<< ' '
|
||||
<< redact_id
|
||||
<< " redacted "
|
||||
<< event_id
|
||||
<< std::endl;
|
||||
|
||||
return --count > 0;
|
||||
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__redact__state(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"room_id", "redactor", "type", "state_key"
|
||||
}};
|
||||
|
||||
const m::room::id::buf room_id
|
||||
{
|
||||
m::room_id(param.at("room_id"))
|
||||
};
|
||||
|
||||
const m::user::id redactor
|
||||
{
|
||||
param.at("redactor")
|
||||
};
|
||||
|
||||
const auto type
|
||||
{
|
||||
param["type"]
|
||||
};
|
||||
|
||||
const auto state_key
|
||||
{
|
||||
param["state_key"]
|
||||
};
|
||||
|
||||
const m::room room
|
||||
{
|
||||
room_id
|
||||
};
|
||||
|
||||
if(state_key)
|
||||
{
|
||||
const auto event_idx
|
||||
{
|
||||
room.get(type, state_key)
|
||||
};
|
||||
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(std::nothrow, event_idx)
|
||||
};
|
||||
|
||||
const auto redact_id
|
||||
{
|
||||
m::redact(room, redactor, event_id, "console")
|
||||
};
|
||||
|
||||
out
|
||||
<< redact_id
|
||||
<< " redacted "
|
||||
<< event_id
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
room
|
||||
};
|
||||
|
||||
state.for_each(type, [&out, &room, &redactor]
|
||||
(const auto &, const auto &state_key, const m::event::idx &event_idx)
|
||||
{
|
||||
const auto event_id
|
||||
{
|
||||
m::event_id(std::nothrow, event_idx)
|
||||
};
|
||||
|
||||
const auto redact_id
|
||||
{
|
||||
m::redact(room, redactor, event_id, "console")
|
||||
};
|
||||
|
||||
out
|
||||
<< redact_id
|
||||
<< " redacted "
|
||||
<< event_id
|
||||
<< std::endl;
|
||||
return true;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// well-known
|
||||
//
|
||||
|
@ -18150,6 +18531,35 @@ console_cmd__well_known__matrix__server(opt &out, const string_view &line)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
console_cmd__well_known__matrix__client(opt &out, const string_view &line)
|
||||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"remote"
|
||||
}};
|
||||
|
||||
const fmt::bsprintf<512> url
|
||||
{
|
||||
"https://%s/.well-known/matrix/client",
|
||||
param.at("remote"),
|
||||
};
|
||||
|
||||
char buf[1024];
|
||||
const json::object response
|
||||
{
|
||||
rest::get
|
||||
{
|
||||
buf, string_view{url}
|
||||
}
|
||||
};
|
||||
|
||||
out
|
||||
<< string_view{response}
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// bridge
|
||||
//
|
||||
|
|
|
@ -114,6 +114,7 @@ handle_get(client &client,
|
|||
opts.room_id = since;
|
||||
opts.search_term = search_term;
|
||||
opts.request_node_id = request.node_id;
|
||||
opts.room_type = json::string(filter["room_type"]);
|
||||
|
||||
size_t count{0};
|
||||
m::room::id::buf prev_batch_buf;
|
||||
|
|
|
@ -64,6 +64,11 @@ get__user_devices(client &client,
|
|||
user_id
|
||||
};
|
||||
|
||||
const m::user::keys user_keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
m::resource::response::chunked::json response
|
||||
{
|
||||
client, http::OK
|
||||
|
@ -82,40 +87,42 @@ get__user_devices(client &client,
|
|||
}
|
||||
};
|
||||
|
||||
const auto master_event_idx
|
||||
if(user_keys.has_cross_master())
|
||||
{
|
||||
user_room.get(std::nothrow, "ircd.device.signing.master", "")
|
||||
};
|
||||
|
||||
m::get(std::nothrow, master_event_idx, "content", [&response]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
json::stack::object object
|
||||
{
|
||||
response, "master_key", content
|
||||
response, "master_key"
|
||||
};
|
||||
});
|
||||
|
||||
const auto self_event_idx
|
||||
{
|
||||
user_room.get(std::nothrow, "ircd.device.signing.self", "")
|
||||
};
|
||||
user_keys.cross_master(object);
|
||||
}
|
||||
|
||||
m::get(std::nothrow, self_event_idx, "content", [&response]
|
||||
(const json::object &content)
|
||||
if(user_keys.has_cross_self())
|
||||
{
|
||||
json::stack::member
|
||||
json::stack::object object
|
||||
{
|
||||
response, "self_signing_keys", content
|
||||
response, "self_signing_key"
|
||||
};
|
||||
});
|
||||
|
||||
user_keys.cross_self(object);
|
||||
}
|
||||
|
||||
if(my_host(request.node_id) && user_keys.has_cross_user())
|
||||
{
|
||||
json::stack::object object
|
||||
{
|
||||
response, "user_signing_key"
|
||||
};
|
||||
|
||||
user_keys.cross_user(object);
|
||||
}
|
||||
|
||||
json::stack::array devices
|
||||
{
|
||||
response, "devices"
|
||||
};
|
||||
|
||||
user_devices.for_each([&user_devices, &devices]
|
||||
user_devices.for_each([&user_devices, &devices, &user_keys]
|
||||
(const auto &, const string_view &device_id)
|
||||
{
|
||||
json::stack::object device
|
||||
|
@ -128,6 +135,16 @@ get__user_devices(client &client,
|
|||
device, "device_id", device_id
|
||||
};
|
||||
|
||||
if(user_keys.has_device(device_id))
|
||||
{
|
||||
json::stack::object keys
|
||||
{
|
||||
device, "keys"
|
||||
};
|
||||
|
||||
user_keys.device(keys, device_id);
|
||||
}
|
||||
|
||||
// The property name difference here is on purpose, probably one of
|
||||
// those so-called spec "thinkos"
|
||||
user_devices.get(std::nothrow, device_id, "display_name", [&device]
|
||||
|
@ -139,15 +156,6 @@ get__user_devices(client &client,
|
|||
};
|
||||
});
|
||||
|
||||
user_devices.get(std::nothrow, device_id, "keys", [&device]
|
||||
(const auto &, const json::object &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
device, "keys", value
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
|
@ -47,29 +47,27 @@ post__user_keys_claim(client &client,
|
|||
request["one_time_keys"]
|
||||
};
|
||||
|
||||
m::resource::response::chunked response
|
||||
m::resource::response::chunked::json response
|
||||
{
|
||||
client, http::OK
|
||||
};
|
||||
|
||||
json::stack out
|
||||
{
|
||||
response.buf, response.flusher()
|
||||
};
|
||||
|
||||
json::stack::object top
|
||||
{
|
||||
out
|
||||
};
|
||||
|
||||
json::stack::object response_keys
|
||||
{
|
||||
top, "one_time_keys"
|
||||
response, "one_time_keys"
|
||||
};
|
||||
|
||||
for(const auto &[user_id, devices] : one_time_keys)
|
||||
for(const auto &[user_id_, devices] : one_time_keys)
|
||||
{
|
||||
const m::user::room user_room
|
||||
const m::user::id user_id
|
||||
{
|
||||
user_id_
|
||||
};
|
||||
|
||||
if(!m::exists(user_id))
|
||||
continue;
|
||||
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
@ -79,26 +77,11 @@ post__user_keys_claim(client &client,
|
|||
response_keys, user_id
|
||||
};
|
||||
|
||||
for(const auto &[device_id_, algorithm_] : json::object(devices))
|
||||
for(const auto &[device_id, algorithm_] : json::object(devices))
|
||||
{
|
||||
const json::string &algorithm{algorithm_};
|
||||
const json::string &device_id{device_id_};
|
||||
const auto match{[&device_id]
|
||||
(const string_view &state_key) noexcept
|
||||
const json::string algorithm
|
||||
{
|
||||
return state_key == device_id;
|
||||
}};
|
||||
|
||||
char buf[m::event::TYPE_MAX_SIZE];
|
||||
const string_view type{fmt::sprintf
|
||||
{
|
||||
buf, "ircd.device.one_time_key|%s",
|
||||
algorithm
|
||||
}};
|
||||
|
||||
const m::room::type events
|
||||
{
|
||||
user_room, type, { -1UL, -1L }, true
|
||||
algorithm_
|
||||
};
|
||||
|
||||
json::stack::object response_device
|
||||
|
@ -106,33 +89,9 @@ post__user_keys_claim(client &client,
|
|||
response_user, device_id
|
||||
};
|
||||
|
||||
events.for_each([&response_device, &match]
|
||||
(const string_view &type, const auto &, const m::event::idx &event_idx)
|
||||
{
|
||||
if(!m::query(std::nothrow, event_idx, "state_key", match))
|
||||
return true;
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&response_device, type]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto algorithm
|
||||
{
|
||||
split(type, '|').second
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
response_device, algorithm, json::object
|
||||
{
|
||||
content[""] // device quirk
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
keys.claim(response_device, device_id, algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,11 @@ _query_user_device(client &,
|
|||
const string_view &device_id,
|
||||
json::stack::object &out);
|
||||
|
||||
static void
|
||||
_query_user_keys(client &,
|
||||
const m::resource::request &,
|
||||
json::stack &);
|
||||
|
||||
static void
|
||||
_query_self_keys(client &,
|
||||
const m::resource::request &,
|
||||
|
@ -72,6 +77,9 @@ post__user_keys_query(client &client,
|
|||
_query_device_keys(client, request, response);
|
||||
_query_master_keys(client, request, response);
|
||||
_query_self_keys(client, request, response);
|
||||
if(my_host(request.node_id))
|
||||
_query_user_keys(client, request, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -147,24 +155,20 @@ _query_master_keys(client &client,
|
|||
user_id_
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
if(!keys.has_cross_master())
|
||||
continue;
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
room.get(std::nothrow, "ircd.device.signing.master", "")
|
||||
response_keys, user_id
|
||||
};
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&response_keys, &user_id]
|
||||
(const json::object &content)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
response_keys, user_id, content
|
||||
};
|
||||
});
|
||||
keys.cross_master(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,24 +194,59 @@ _query_self_keys(client &client,
|
|||
user_id_
|
||||
};
|
||||
|
||||
const m::user::room room
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
if(!keys.has_cross_self())
|
||||
continue;
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
room.get(std::nothrow, "ircd.device.signing.self", "")
|
||||
response_keys, user_id
|
||||
};
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&response_keys, &user_id]
|
||||
(const json::object &content)
|
||||
keys.cross_self(object);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_query_user_keys(client &client,
|
||||
const m::resource::request &request,
|
||||
json::stack &out)
|
||||
{
|
||||
const json::object request_keys
|
||||
{
|
||||
request.at("device_keys")
|
||||
};
|
||||
|
||||
json::stack::object response_keys
|
||||
{
|
||||
out, "user_signing_keys"
|
||||
};
|
||||
|
||||
for(const auto &[user_id_, device_ids_] : request_keys)
|
||||
{
|
||||
const m::user::id &user_id
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
response_keys, user_id, content
|
||||
};
|
||||
});
|
||||
user_id_
|
||||
};
|
||||
|
||||
const m::user::keys keys
|
||||
{
|
||||
user_id
|
||||
};
|
||||
|
||||
if(!keys.has_cross_user())
|
||||
continue;
|
||||
|
||||
json::stack::object object
|
||||
{
|
||||
response_keys, user_id
|
||||
};
|
||||
|
||||
keys.cross_user(object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +257,12 @@ _query_user_device(client &client,
|
|||
const string_view &device_id,
|
||||
json::stack::object &out)
|
||||
{
|
||||
if(!devices.has(device_id, "keys"))
|
||||
const m::user::keys keys
|
||||
{
|
||||
devices.user
|
||||
};
|
||||
|
||||
if(!keys.has_device(device_id))
|
||||
return;
|
||||
|
||||
json::stack::object object
|
||||
|
@ -226,15 +270,7 @@ _query_user_device(client &client,
|
|||
out, device_id
|
||||
};
|
||||
|
||||
devices.get(std::nothrow, device_id, "keys", [&device_id, &object]
|
||||
(const auto &event_idx, const json::object &device_keys)
|
||||
{
|
||||
for(const auto &member : device_keys)
|
||||
json::stack::member
|
||||
{
|
||||
object, member.first, member.second
|
||||
};
|
||||
});
|
||||
keys.device(object, device_id);
|
||||
|
||||
devices.get(std::nothrow, device_id, "display_name", [&device_id, &object]
|
||||
(const auto &event_idx, const string_view &display_name)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue