Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d97366fe74 | |||
| 0258195682 | |||
| 7805241296 | |||
| 33ea1823de | |||
| 038444773b | |||
| 85b614e77b | |||
| 02aa256c59 | |||
| 21c6cc8fd1 | |||
| 5728db746d | |||
| 5083217fce | |||
| 527c000441 | |||
| 48af6467ea | |||
| b051bcd983 | |||
| 11a6d3659e | |||
| 412489d31b | |||
| b75e08edf7 | |||
| ff83eb960a | |||
| 67208fad9d | |||
| e02864bf6c | |||
| 45abd38d09 | |||
| e7cf1d0b87 | |||
| b7aa7dc64c | |||
| d10d7aa8cc | |||
| 6b3333c3ff | |||
| 3ba6a8e039 | |||
| c27a3a02fe | |||
| ca05bbd652 | |||
| 357988b662 | |||
| c40df92263 | |||
| cbe30c1fcb | |||
| f77b55c073 | |||
| f9ab175a79 | |||
| fb0097a6e0 | |||
| d247003038 | |||
| 0f2d64c10d | |||
| d49ccbf031 | |||
| 6d5065ae3a | |||
| 803e6d743a | |||
| 2bf4a5970a | |||
| 9e985e3e79 | |||
| 72661acb43 | |||
| eace76b16e | |||
| ca03a97ca2 | |||
| 95d54f9568 | |||
| 62185c8d30 | |||
| ef94ee66fd | |||
| f666cd4643 | |||
| 12989ba5eb | |||
| 63f3b4f7af | |||
| 7a4ea0eba2 | |||
| ad4876821b | |||
| cf2b2d5e85 | |||
| a0c5df0c5b | |||
| ae36405466 | |||
| e7eb116d93 | |||
| 10a82afd46 | |||
| bba9563525 | |||
| 558cabd860 | |||
| 2118c3028f | |||
| 4a7238afea | |||
| 127c909f0f | |||
| 1a7c1c5c06 | |||
| dd5d2aeee0 | |||
| 1d99c5cb60 | |||
| b9e4bbc070 | |||
| 41fab88ee5 | |||
| 0ca877f925 | |||
| b480f125ee | |||
| 7ee5f81923 | |||
| 3d43b98d42 | |||
| 1636c202a7 | |||
| 060992bafa | |||
| c0c69394b9 | |||
| 2b7285e0b2 | |||
| c3a24ffdf8 | |||
| 48fb446f8c | |||
| 0283a501fa | |||
| fb572bf901 | |||
| 7eb09295a3 | |||
| e0eae52eae | |||
| dd270f99fc | |||
| 1472caab50 | |||
| ef2e62a01c | |||
| 4e909fc50d | |||
| 19b7106124 | |||
| ccd9fa6561 | |||
| 1ddd72ffe7 | |||
| b759acdcea | |||
| 4eee6d7cec | |||
| 5575d4e78e | |||
| 4dea2f1eb6 | |||
| 1d5b9bba15 | |||
| 2951281115 | |||
| fd8aba6d41 | |||
| 066e0a8101 | |||
| bf262a6051 | |||
| 7840924b17 | |||
| 60bdab4e52 | |||
| cda1d4ce7f |
@@ -0,0 +1,155 @@
|
||||
name: 'Manage Database'
|
||||
|
||||
inputs:
|
||||
for:
|
||||
default: ""
|
||||
description: 'One or more databases to manage:
|
||||
mysql, pgsql, redis, mssql'
|
||||
|
||||
do:
|
||||
default: ""
|
||||
description: 'One or more tasks to do:
|
||||
install, start, user, create, drop, dump'
|
||||
|
||||
dump-suffix:
|
||||
default: ""
|
||||
description: 'Suffix to append to the dump file name'
|
||||
|
||||
mssql-schema:
|
||||
default: ""
|
||||
description: 'SQL schema for the MSSQL database:
|
||||
singlehost or multihost'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
||||
############################################################# Install #####
|
||||
|
||||
- if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'install')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo sed -i 's/yes/no/g' /etc/initramfs-tools/update-initramfs.conf
|
||||
sudo rm -f /var/lib/man-db/auto-update || echo ok
|
||||
sudo apt-get -q update
|
||||
sudo apt-get -q -y install postgresql
|
||||
|
||||
- if: contains(inputs.for, 'redis') && contains(inputs.do, 'install')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo sed -i 's/yes/no/g' /etc/initramfs-tools/update-initramfs.conf
|
||||
sudo rm -f /var/lib/man-db/auto-update || echo ok
|
||||
sudo apt-get -q update
|
||||
sudo apt-get -q -y install redis-server
|
||||
|
||||
- if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: tdsodbc
|
||||
|
||||
- if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
|
||||
shell: sh
|
||||
run: |
|
||||
docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \
|
||||
-v $(pwd)/test/docker/db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro \
|
||||
-v $(pwd)/sql/mssql.sql:/mssql.sql:ro \
|
||||
-v $(pwd)/sql/mssql.new.sql:/mssql.new.sql:ro \
|
||||
-p 1433:1433 --name ejabberd-mssql \
|
||||
"mcr.microsoft.com/mssql/server:2019-latest"
|
||||
sleep 10
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd \
|
||||
-C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql
|
||||
|
||||
- if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
|
||||
&& inputs.mssql-schema == 'singlehost'
|
||||
shell: sh
|
||||
run: |
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd \
|
||||
-C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test \
|
||||
-i /mssql.sql
|
||||
|
||||
- if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
|
||||
&& inputs.mssql-schema == 'multihost'
|
||||
shell: sh
|
||||
run: |
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd \
|
||||
-C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test \
|
||||
-i /mssql.new.sql
|
||||
|
||||
############################################################### Start #####
|
||||
|
||||
- if: contains(inputs.for, 'mysql') && contains(inputs.do, 'start')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo systemctl start mysql.service
|
||||
|
||||
- if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'start')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo systemctl start postgresql.service
|
||||
pg_isready
|
||||
|
||||
################################################################ User #####
|
||||
|
||||
- if: contains(inputs.for, 'mysql') && contains(inputs.do, 'user')
|
||||
shell: sh
|
||||
run: |
|
||||
mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost'
|
||||
IDENTIFIED BY 'ejabberd_test';"
|
||||
|
||||
- if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'user')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo -u postgres psql -c "CREATE USER ejabberd_test
|
||||
WITH PASSWORD 'ejabberd_test';"
|
||||
|
||||
################################################################ Dump #####
|
||||
|
||||
- if: contains(inputs.for, 'mysql') && contains(inputs.do, 'dump')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo mysqldump -u root -proot ejabberd_test \
|
||||
> mysql-${{ inputs.dump-suffix }}.sql
|
||||
|
||||
- if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'dump')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo -u postgres pg_dump ejabberd_test \
|
||||
> pgsql-${{ inputs.dump-suffix }}.sql
|
||||
|
||||
################################################################ Drop #####
|
||||
|
||||
- if: contains(inputs.for, 'mysql') && contains(inputs.do, 'drop')
|
||||
shell: sh
|
||||
run: |
|
||||
mysql -u root -proot -e "DROP DATABASE ejabberd_test;"
|
||||
|
||||
- if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'drop')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo -u postgres psql -c "DROP DATABASE ejabberd_test;"
|
||||
|
||||
############################################################## Create #####
|
||||
|
||||
- if: contains(inputs.for, 'mysql') && contains(inputs.do, 'create')
|
||||
shell: sh
|
||||
run: |
|
||||
mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
|
||||
mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
|
||||
TO 'ejabberd_test'@'localhost';"
|
||||
|
||||
- if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'create')
|
||||
shell: sh
|
||||
run: |
|
||||
sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES
|
||||
ON DATABASE ejabberd_test TO ejabberd_test;"
|
||||
sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;"
|
||||
sudo -u postgres psql -c "ALTER DATABASE ejabberd_test
|
||||
OWNER TO ejabberd_test;"
|
||||
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
||||
TABLES IN SCHEMA public
|
||||
TO ejabberd_test;"
|
||||
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
||||
SEQUENCES IN SCHEMA public
|
||||
TO ejabberd_test;"
|
||||
@@ -0,0 +1,178 @@
|
||||
name: 'Manage ejabberd'
|
||||
|
||||
inputs:
|
||||
for:
|
||||
default: ""
|
||||
description: 'Release method, one of:
|
||||
prod, dev, install, run, deb'
|
||||
do:
|
||||
default: ""
|
||||
description: 'One or more tasks to perform:
|
||||
deploy, no_acme, no_tls,
|
||||
start, register, update_sql, stop,
|
||||
logs, check'
|
||||
username:
|
||||
default: "user1"
|
||||
description: 'Username part of the account JID'
|
||||
host:
|
||||
default: "localhost"
|
||||
description: 'Host part of the account JID'
|
||||
tool:
|
||||
default: "rebar3"
|
||||
description: 'Build tool to use:
|
||||
rebar or rebar3 (only relevant to make rebar2)'
|
||||
rel_name_vsn:
|
||||
default: ""
|
||||
description: 'Base name of installer files'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
||||
- name: Path Definitions
|
||||
id: path
|
||||
shell: bash
|
||||
run: |
|
||||
case ${{ inputs.for }} in
|
||||
'prod')
|
||||
BASE="_build/prod/rel/ejabberd"
|
||||
echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
|
||||
echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
|
||||
echo "ectl=$BASE/bin/ejabberdctl" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
'dev')
|
||||
BASE="_build/dev/rel/ejabberd"
|
||||
echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
|
||||
echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
|
||||
echo "ectl=$BASE/bin/ejabberdctl" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
'install')
|
||||
BASE="/tmp/ejabberd"
|
||||
echo "conf=$BASE/etc/ejabberd" >> $GITHUB_OUTPUT
|
||||
echo "logs=$BASE/var/log/ejabberd" >> $GITHUB_OUTPUT
|
||||
echo "ectl=$BASE/sbin/ejabberdctl" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
'deb')
|
||||
BASE="/opt/ejabberd"
|
||||
echo "base=$BASE" >> $GITHUB_OUTPUT
|
||||
echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
|
||||
echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
|
||||
echo "ectl=sudo /opt/${{ inputs.rel_name_vsn }}/bin/ejabberdctl" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
'run')
|
||||
BASE="$HOME/opt/ejabberd"
|
||||
echo "base=$BASE" >> $GITHUB_OUTPUT
|
||||
echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
|
||||
echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
|
||||
echo "ectl=$HOME/opt/${{ inputs.rel_name_vsn }}/bin/ejabberdctl" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
esac
|
||||
|
||||
############################################################## Deploy #####
|
||||
|
||||
- if: contains(inputs.do, 'deploy') &&
|
||||
inputs.for == 'prod' && inputs.tool == 'rebar'
|
||||
shell: sh
|
||||
run: |
|
||||
mkdir -p _build/prod && ln -s `pwd`/rel/ _build/prod/rel
|
||||
|
||||
- if: contains(inputs.do, 'deploy') &&
|
||||
inputs.for == 'dev' && inputs.tool == 'rebar'
|
||||
shell: sh
|
||||
run: |
|
||||
mkdir -p _build/dev && ln -s `pwd`/rel/ _build/dev/rel
|
||||
|
||||
- if: contains(inputs.do, 'deploy') &&
|
||||
(inputs.for == 'prod' ||
|
||||
inputs.for == 'dev' ||
|
||||
inputs.for == 'install')
|
||||
shell: sh
|
||||
run: |
|
||||
make ${{ inputs.for }}
|
||||
|
||||
- if: contains(inputs.do, 'deploy') &&
|
||||
inputs.for == 'deb'
|
||||
shell: sh
|
||||
run: |
|
||||
sudo dpkg -i $(ls -1 *.deb)
|
||||
|
||||
- if: contains(inputs.do, 'deploy') &&
|
||||
inputs.for == 'run'
|
||||
shell: sh
|
||||
run: |
|
||||
./$(ls -1 *.run)
|
||||
|
||||
################################################################ ACME #####
|
||||
|
||||
- if: contains(inputs.do, 'no_acme')
|
||||
shell: sh
|
||||
run: |
|
||||
sed -i 's/loglevel/acme:\n auto: false\nloglevel/g' \
|
||||
${{ steps.path.outputs.conf }}/ejabberd.yml
|
||||
|
||||
################################################################# TLS #####
|
||||
|
||||
- if: contains(inputs.do, 'no_tls') &&
|
||||
inputs.for == 'dev'
|
||||
shell: sh
|
||||
run: |
|
||||
sed -i 's/starttls_required: true/starttls_required: false/g' \
|
||||
${{ steps.path.outputs.conf }}/ejabberd.yml
|
||||
|
||||
############################################################### Start #####
|
||||
|
||||
- if: contains(inputs.do, 'start')
|
||||
shell: sh
|
||||
run: |
|
||||
${{ steps.path.outputs.ectl }} start
|
||||
${{ steps.path.outputs.ectl }} started
|
||||
|
||||
############################################################ Register #####
|
||||
|
||||
- if: contains(inputs.do, 'register')
|
||||
shell: sh
|
||||
run: |
|
||||
${{ steps.path.outputs.ectl }} \
|
||||
register ${{ inputs.username }} ${{ inputs.host }} s0mePass
|
||||
${{ steps.path.outputs.ectl }} \
|
||||
registered_users ${{ inputs.host }} >> registered.log
|
||||
grep -q '${{ inputs.username }}' registered.log
|
||||
|
||||
########################################################### UpdateSQL #####
|
||||
|
||||
- if: contains(inputs.do, 'update_sql')
|
||||
shell: sh
|
||||
run: |
|
||||
${{ steps.path.outputs.ectl }} \
|
||||
update_sql
|
||||
|
||||
################################################################ Stop #####
|
||||
|
||||
- if: contains(inputs.do, 'stop')
|
||||
shell: sh
|
||||
run: |
|
||||
${{ steps.path.outputs.ectl }} stop
|
||||
${{ steps.path.outputs.ectl }} stopped
|
||||
|
||||
################################################################ Logs #####
|
||||
|
||||
- if: contains(inputs.do, 'logs')
|
||||
shell: sh
|
||||
run: |
|
||||
SUDO=sudo
|
||||
[ "${{ inputs.for }}" = "deb" ] || SUDO=""
|
||||
echo "::group::View ejabberd.log"
|
||||
$SUDO cat ${{ steps.path.outputs.logs }}/ejabberd.log
|
||||
echo "::endgroup::"
|
||||
echo "::group::View error.log"
|
||||
$SUDO cat ${{ steps.path.outputs.logs }}/error.log
|
||||
echo "::endgroup::"
|
||||
|
||||
############################################################### Check #####
|
||||
|
||||
- if: contains(inputs.do, 'check')
|
||||
shell: sh
|
||||
run: |
|
||||
grep -q 'is started' ${{ steps.path.outputs.logs }}/ejabberd.log
|
||||
grep -q 'is stopped' ${{ steps.path.outputs.logs }}/ejabberd.log
|
||||
test $(find ${{ steps.path.outputs.logs }}/ -empty -name error.log)
|
||||
@@ -1,5 +1,5 @@
|
||||
#' Define default build variables
|
||||
ARG OTP_VSN='27.3.4.2'
|
||||
ARG OTP_VSN='27.3.4.3'
|
||||
ARG ELIXIR_VSN='1.18.4'
|
||||
ARG UID='9000'
|
||||
ARG USER='ejabberd'
|
||||
|
||||
@@ -151,7 +151,12 @@ api_permissions:
|
||||
from: ejabberd_web_admin
|
||||
who: admin
|
||||
what: "*"
|
||||
"admin access":
|
||||
"adhoc commands":
|
||||
from: mod_adhoc_api
|
||||
who: admin
|
||||
what: "*"
|
||||
"http access":
|
||||
from: mod_http_api
|
||||
who:
|
||||
access:
|
||||
allow:
|
||||
@@ -192,6 +197,7 @@ shaper_rules:
|
||||
|
||||
modules:
|
||||
mod_adhoc: {}
|
||||
mod_adhoc_api: {}
|
||||
mod_admin_extra: {}
|
||||
mod_announce:
|
||||
access: announce
|
||||
@@ -232,6 +238,7 @@ modules:
|
||||
default_room_options:
|
||||
mam: true
|
||||
mod_muc_admin: {}
|
||||
mod_muc_occupantid: {}
|
||||
mod_offline:
|
||||
access_max_user_messages: max_user_offline_messages
|
||||
mod_ping: {}
|
||||
|
||||
@@ -121,6 +121,9 @@ export CONTRIB_MODULES_CONF_DIR
|
||||
export ERL_LIBS
|
||||
export SCRIPT_DIR
|
||||
|
||||
# Only required for Erlang/OTP 25:
|
||||
export ERL_FLAGS="$ERL_FLAGS -enable-feature maybe_expr"
|
||||
|
||||
set_dist_client()
|
||||
{
|
||||
[ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -dist_listen false"
|
||||
|
||||
+339
-207
@@ -18,89 +18,42 @@ on:
|
||||
- 'priv/**'
|
||||
- '**.md'
|
||||
|
||||
env:
|
||||
DEV: _build/dev/rel/ejabberd
|
||||
|
||||
jobs:
|
||||
|
||||
tests:
|
||||
name: Tests
|
||||
############################################################### Compile #####
|
||||
|
||||
compile:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
otp: ['25', '26', '27', '28']
|
||||
runs-on: ubuntu-24.04
|
||||
services:
|
||||
redis:
|
||||
image: public.ecr.aws/docker/library/redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
otp: ['27']
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Test shell scripts
|
||||
if: matrix.otp == '27'
|
||||
run: |
|
||||
shellcheck test/ejabberd_SUITE_data/gencerts.sh
|
||||
shellcheck tools/captcha.sh
|
||||
shellcheck ejabberd.init.template
|
||||
shellcheck -x ejabberdctl.template
|
||||
|
||||
- name: Get specific Erlang/OTP
|
||||
uses: erlef/setup-beam@v1
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: ${{ matrix.otp }}
|
||||
hexpm-mirrors: |
|
||||
https://cdn.jsdelivr.net/hex
|
||||
https://builds.hex.pm
|
||||
|
||||
- name: Install MS SQL Server
|
||||
run: |
|
||||
docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \
|
||||
-v $(pwd)/test/docker/db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro \
|
||||
-v $(pwd)/sql/mssql.sql:/mssql.sql:ro \
|
||||
-v $(pwd)/sql/mssql.new.sql:/mssql.new.sql:ro \
|
||||
-p 1433:1433 --name ejabberd-mssql "mcr.microsoft.com/mssql/server:2019-latest"
|
||||
sleep 10
|
||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: libexpat1-dev libgd-dev libpam0g-dev
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- name: Prepare databases
|
||||
run: |
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql
|
||||
sudo systemctl start mysql.service
|
||||
sudo systemctl start postgresql.service
|
||||
mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
|
||||
mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost'
|
||||
IDENTIFIED BY 'ejabberd_test';"
|
||||
mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
|
||||
TO 'ejabberd_test'@'localhost';"
|
||||
pg_isready
|
||||
sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
|
||||
sudo -u postgres psql -c "CREATE USER ejabberd_test
|
||||
WITH PASSWORD 'ejabberd_test';"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES
|
||||
ON DATABASE ejabberd_test TO ejabberd_test;"
|
||||
sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;"
|
||||
sudo -u postgres psql -c "ALTER DATABASE ejabberd_test OWNER TO ejabberd_test;"
|
||||
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
||||
TABLES IN SCHEMA public
|
||||
TO ejabberd_test;"
|
||||
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
||||
SEQUENCES IN SCHEMA public
|
||||
TO ejabberd_test;"
|
||||
|
||||
- name: Prepare libraries
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get -y purge libgd3 nginx
|
||||
sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- name: Remove syntax_tools from release
|
||||
run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script
|
||||
|
||||
- name: Cache Hex.pm
|
||||
- name: Cache rebar3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/rebar3/
|
||||
key: ${{matrix.otp}}-${{hashFiles('rebar.config')}}
|
||||
_build/default/lib/
|
||||
key: ci-${{ matrix.otp }}-${{hashFiles('rebar.config')}}
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
@@ -108,72 +61,196 @@ jobs:
|
||||
./configure --with-rebar=./rebar3 \
|
||||
--prefix=/tmp/ejabberd \
|
||||
--enable-all \
|
||||
--disable-elixir \
|
||||
--disable-mssql \
|
||||
--disable-odbc
|
||||
--disable-elixir
|
||||
sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script
|
||||
make
|
||||
tar -cvf /tmp/compiled.tar .
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: compiled-${{ matrix.otp }}
|
||||
path: /tmp/compiled.tar
|
||||
retention-days: 1
|
||||
|
||||
########################################################## Static Tests #####
|
||||
|
||||
static:
|
||||
needs: compile
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
matrix:
|
||||
otp: ['27']
|
||||
|
||||
steps:
|
||||
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: ${{ matrix.otp }}
|
||||
hexpm-mirrors: |
|
||||
https://cdn.jsdelivr.net/hex
|
||||
https://builds.hex.pm
|
||||
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: compiled-${{ matrix.otp }}
|
||||
- run: tar -xf compiled.tar
|
||||
|
||||
- name: Test shell scripts
|
||||
run: |
|
||||
shellcheck ejabberd.init.template
|
||||
shellcheck -x ejabberdctl.template
|
||||
shellcheck .vscode/relive.sh
|
||||
shellcheck rel/setup-dev.sh
|
||||
shellcheck rel/setup-relive.sh
|
||||
shellcheck test/ejabberd_SUITE_data/gencerts.sh
|
||||
shellcheck tools/captcha.sh
|
||||
shellcheck tools/emacs-indent.sh
|
||||
shellcheck tools/generate-doap.sh
|
||||
shellcheck tools/prepare-tr.sh
|
||||
shellcheck tools/rebar3-format.sh
|
||||
|
||||
- run: make install -s
|
||||
- run: make hooks
|
||||
- run: make options
|
||||
- run: make xref
|
||||
- run: make dialyzer
|
||||
- run: make test-eunit
|
||||
- run: make elvis
|
||||
if: matrix.otp >= '26'
|
||||
if: matrix.otp > '25'
|
||||
|
||||
- name: Check Production Release
|
||||
run: |
|
||||
make rel
|
||||
RE=_build/prod/rel/ejabberd
|
||||
$RE/bin/ejabberdctl start
|
||||
$RE/bin/ejabberdctl started
|
||||
$RE/bin/ejabberdctl stop
|
||||
$RE/bin/ejabberdctl stopped
|
||||
cat $RE/logs/ejabberd.log
|
||||
grep -q "is stopped in" $RE/logs/ejabberd.log
|
||||
- run: make install -s
|
||||
|
||||
- name: Start Development Release
|
||||
run: |
|
||||
make dev
|
||||
RE=_build/dev/rel/ejabberd
|
||||
sed -i 's/starttls_required: true/starttls_required: false/g' $RE/conf/ejabberd.yml
|
||||
$RE/bin/ejabberdctl start
|
||||
$RE/bin/ejabberdctl started
|
||||
$RE/bin/ejabberdctl register admin localhost admin
|
||||
grep -q "is started in" $RE/logs/ejabberd.log
|
||||
######################################################### Dynamic Tests #####
|
||||
|
||||
- name: Run XMPP Interoperability Tests against CI server.
|
||||
dynamic:
|
||||
needs: compile
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
matrix:
|
||||
otp: ['27']
|
||||
|
||||
steps:
|
||||
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: ${{ matrix.otp }}
|
||||
hexpm-mirrors: |
|
||||
https://cdn.jsdelivr.net/hex
|
||||
https://builds.hex.pm
|
||||
|
||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
if: matrix.otp < '27'
|
||||
with:
|
||||
packages: libexpat1-dev libgd-dev libpam0g-dev
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: compiled-${{ matrix.otp }}
|
||||
- run: tar -xf compiled.tar
|
||||
|
||||
- name: Check production release
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: prod
|
||||
do: deploy, start, stop, check
|
||||
|
||||
- name: Start development release
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: deploy, no_tls, start, register
|
||||
username: user123
|
||||
|
||||
- name: Run XMPP Interoperability Tests against CI server
|
||||
if: matrix.otp == '27'
|
||||
continue-on-error: true
|
||||
uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.6.0
|
||||
uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.7.2
|
||||
with:
|
||||
domain: 'localhost'
|
||||
adminAccountUsername: 'admin'
|
||||
adminAccountPassword: 'admin'
|
||||
disabledSpecifications: RFC6121,XEP-0030,XEP-0045,XEP-0054,XEP-0060,XEP-0080,XEP-0115,XEP-0118,XEP-0215,XEP-0347,XEP-0363,XEP-0384
|
||||
adminAccountUsername: 'user123'
|
||||
adminAccountPassword: 's0mePass'
|
||||
disabledSpecifications: RFC6121,XEP-0030,XEP-0045,XEP-0054,XEP-0060,
|
||||
XEP-0080,XEP-0115,XEP-0118,XEP-0215,XEP-0347,
|
||||
XEP-0363,XEP-0384,XEP-0421
|
||||
|
||||
- name: Stop Development Release
|
||||
- name: Stop development release
|
||||
if: always()
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: stop, check
|
||||
|
||||
- name: View production logs
|
||||
if: always()
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: prod
|
||||
do: logs
|
||||
|
||||
- name: View development logs
|
||||
if: always()
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: logs
|
||||
|
||||
##################################################### Common Test Suite #####
|
||||
|
||||
ct:
|
||||
needs: compile
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
matrix:
|
||||
otp: ['27']
|
||||
backend: [agnostic, extauth, ldap, mnesia, mysql, pgsql, redis, sqlite]
|
||||
schema: [single, multi]
|
||||
exclude:
|
||||
- backend: agnostic
|
||||
schema: single
|
||||
- backend: extauth
|
||||
schema: single
|
||||
- backend: ldap
|
||||
schema: single
|
||||
- backend: mnesia
|
||||
schema: single
|
||||
- backend: redis
|
||||
schema: single
|
||||
|
||||
steps:
|
||||
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: ${{ matrix.otp }}
|
||||
hexpm-mirrors: |
|
||||
https://cdn.jsdelivr.net/hex
|
||||
https://builds.hex.pm
|
||||
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: compiled-${{ matrix.otp }}
|
||||
- run: tar -xf compiled.tar
|
||||
|
||||
- name: Prepare database
|
||||
uses: ./.github/actions/manage-database
|
||||
with:
|
||||
for: ${{ matrix.backend }}
|
||||
do: install, start, user, create
|
||||
|
||||
- name: Setup multihost SQL schema
|
||||
if: matrix.schema == 'multi'
|
||||
run: |
|
||||
RE=_build/dev/rel/ejabberd
|
||||
$RE/bin/ejabberdctl stop
|
||||
$RE/bin/ejabberdctl stopped
|
||||
cat $RE/logs/ejabberd.log
|
||||
grep -q "is stopped in" $RE/logs/ejabberd.log
|
||||
sed -i 's|multihost_schema, false|multihost_schema, true|g' \
|
||||
test/suite.erl
|
||||
|
||||
- name: Run tests
|
||||
id: ct
|
||||
run: CT_BACKENDS=${{ matrix.backend }} make test
|
||||
|
||||
- name: Send to coveralls
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
(cd priv && ln -sf ../sql)
|
||||
sed -i -e 's/ct:pal/ct:log/' test/suite.erl
|
||||
COMMIT=`echo $GITHUB_SHA | cut -c 1-7`
|
||||
DATE=`date +%s`
|
||||
REF_NAME=`echo $GITHUB_REF_NAME | tr "/" "_"`
|
||||
NODENAME=$DATE@$GITHUB_RUN_NUMBER-$GITHUB_ACTOR-$REF_NAME-$COMMIT
|
||||
LABEL=`git show -s --format=%s | cut -c 1-30`
|
||||
./rebar3 ct --name $NODENAME --label "$LABEL"
|
||||
./rebar3 cover
|
||||
DIAGNOSTIC=1 ./rebar3 as test coveralls send
|
||||
|
||||
- name: Check results
|
||||
if: always() && (steps.ct.outcome != 'skipped')
|
||||
@@ -185,20 +262,45 @@ jobs:
|
||||
grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
|
||||
test $(find logs/ -empty -name error.log)
|
||||
|
||||
- name: View logs failures
|
||||
if: failure() && steps.ctresults.outcome == 'failure'
|
||||
- name: View logs
|
||||
if: always()
|
||||
run: |
|
||||
echo "::group::ejabberd.log"
|
||||
find logs/ -name ejabberd.log -exec cat '{}' ';'
|
||||
echo "::endgroup::"
|
||||
echo "::group::error.log"
|
||||
find logs/ -name error.log -exec cat '{}' ';'
|
||||
echo "::endgroup::"
|
||||
echo "::group::exunit.log"
|
||||
find logs/ -name exunit.log -exec cat '{}' ';'
|
||||
echo "::endgroup::"
|
||||
echo "::group::suite.log (only failures)"
|
||||
cat logs/suite.log | awk \
|
||||
'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
|
||||
find logs/ -name error.log -exec cat '{}' ';'
|
||||
find logs/ -name exunit.log -exec cat '{}' ';'
|
||||
echo "::endgroup::"
|
||||
echo "::group::suite.log (complete)"
|
||||
cat logs/suite.log
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Send to coveralls
|
||||
if: matrix.otp == '27'
|
||||
- name: Upload CT logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: ct-logs-${{ matrix.otp }}-${{ matrix.backend }}-${{ matrix.schema }}
|
||||
path: _build/test/logs
|
||||
retention-days: 14
|
||||
|
||||
############################################################# Coveralls #####
|
||||
|
||||
cover:
|
||||
needs: ct
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Finish parallel upload to coveralls
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
DIAGNOSTIC=1 ./rebar3 as test coveralls send
|
||||
curl -v -k https://coveralls.io/webhook \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{"repo_name":"$GITHUB_REPOSITORY",
|
||||
@@ -206,96 +308,126 @@ jobs:
|
||||
"payload":{"build_num":$GITHUB_RUN_ID,
|
||||
"status":"done"}}'
|
||||
|
||||
- name: Check for changes to trigger schema upgrade test
|
||||
uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
################################################################ Schema #####
|
||||
|
||||
schema:
|
||||
needs: compile
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
matrix:
|
||||
otp: ['27']
|
||||
|
||||
steps:
|
||||
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
filters: |
|
||||
sql:
|
||||
- 'sql/**'
|
||||
- 'src/mod_admin_update_sql.erl'
|
||||
otp-version: ${{ matrix.otp }}
|
||||
hexpm-mirrors: |
|
||||
https://cdn.jsdelivr.net/hex
|
||||
https://builds.hex.pm
|
||||
|
||||
- name: Prepare for schema upgrade test
|
||||
id: prepupgradetest
|
||||
if: ${{ steps.filter.outputs.sql == 'true' }}
|
||||
run: |
|
||||
[[ -d logs ]] && rm -rf logs
|
||||
[[ -d _build/test/logs ]] && rm -rf _build/test/logs || true
|
||||
sed -i 's|update_sql, false|update_sql, true|g' test/suite.erl
|
||||
- name: Run DB tests on upgraded schema (mssql, mysql, pgsql)
|
||||
run: CT_BACKENDS=mssql,mysql,pgsql make test
|
||||
if: always() && steps.prepupgradetest.outcome != 'skipped'
|
||||
id: ctupgradedschema
|
||||
- name: Check results
|
||||
if: always() && steps.ctupgradedschema.outcome != 'skipped'
|
||||
run: |
|
||||
[[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
|
||||
ln `find logs/ -name suite.log` logs/suite.log
|
||||
grep 'TEST COMPLETE' logs/suite.log
|
||||
grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
|
||||
test $(find logs/ -empty -name error.log)
|
||||
- name: View logs failures
|
||||
if: failure() && steps.ctupgradedschema.outcome != 'skipped'
|
||||
run: |
|
||||
cat logs/suite.log | awk \
|
||||
'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
|
||||
find logs/ -name error.log -exec cat '{}' ';'
|
||||
find logs/ -name exunit.log -exec cat '{}' ';'
|
||||
|
||||
- name: Prepare new schema
|
||||
run: |
|
||||
[[ -d logs ]] && rm -rf logs
|
||||
[[ -d _build/test/logs ]] && rm -rf _build/test/logs || true
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];"
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];"
|
||||
mysql -u root -proot -e "DROP DATABASE ejabberd_test;"
|
||||
sudo -u postgres psql -c "DROP DATABASE ejabberd_test;"
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql
|
||||
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql
|
||||
mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
|
||||
mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
|
||||
TO 'ejabberd_test'@'localhost';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES
|
||||
ON DATABASE ejabberd_test TO ejabberd_test;"
|
||||
sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;"
|
||||
sudo -u postgres psql -c "ALTER DATABASE ejabberd_test OWNER TO ejabberd_test;"
|
||||
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
||||
TABLES IN SCHEMA public
|
||||
TO ejabberd_test;"
|
||||
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
||||
SEQUENCES IN SCHEMA public
|
||||
TO ejabberd_test;"
|
||||
sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl
|
||||
- name: Run DB tests on new schema (mssql, mysql, pgsql)
|
||||
run: CT_BACKENDS=mssql,mysql,pgsql make test
|
||||
id: ctnewschema
|
||||
- name: Check results
|
||||
if: always() && steps.ctnewschema.outcome != 'skipped'
|
||||
run: |
|
||||
[[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
|
||||
ln `find logs/ -name suite.log` logs/suite.log
|
||||
grep 'TEST COMPLETE' logs/suite.log
|
||||
grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
|
||||
test $(find logs/ -empty -name error.log)
|
||||
- name: View logs failures
|
||||
if: failure() && steps.ctnewschema.outcome != 'skipped'
|
||||
run: |
|
||||
cat logs/suite.log | awk \
|
||||
'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
|
||||
find logs/ -name error.log -exec cat '{}' ';'
|
||||
find logs/ -name exunit.log -exec cat '{}' ';'
|
||||
|
||||
- name: Upload CT logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
- uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: ejabberd-ct-logs-${{matrix.otp}}
|
||||
#
|
||||
# Appending the wildcard character ("*") is a trick to make
|
||||
# "ejabberd-packages" the root directory of the uploaded ZIP file:
|
||||
#
|
||||
# https://github.com/actions/upload-artifact#upload-using-multiple-paths-and-exclusions
|
||||
#
|
||||
path: _build/test/logs
|
||||
retention-days: 14
|
||||
name: compiled-${{ matrix.otp }}
|
||||
- run: tar -xf compiled.tar
|
||||
|
||||
###################################### multi-changed ##
|
||||
|
||||
- name: Prepare databases
|
||||
uses: ./.github/actions/manage-database
|
||||
with:
|
||||
for: mysql, pgsql
|
||||
do: install, start, user, create
|
||||
|
||||
- name: Prepare configuration
|
||||
run: |
|
||||
CT_BACKENDS=mysql,pgsql ./rebar3 ct \
|
||||
--suite=test/ejabberd_SUITE --group=configtest_single
|
||||
make dev
|
||||
cp test/ejabberd_SUITE_data/ejabberd.yml ${{ env.DEV }}/conf/
|
||||
cp _build/test/logs/last/*.yml ${{ env.DEV }}/database/
|
||||
echo "define_macro: [CONFIGTEST_CONFIG: {modules: {mod_muc: {}}}]" \
|
||||
> ${{ env.DEV }}/database/configtest.yml
|
||||
cp _build/test/logs/last/*.pem ${{ env.DEV }}/conf/
|
||||
cp _build/test/logs/last/*.pem ${{ env.DEV }}/database/
|
||||
|
||||
- name: Run ejabberd
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: start, update_sql, stop, logs, check
|
||||
username: user2
|
||||
|
||||
- name: Dump multihost databases
|
||||
uses: ./.github/actions/manage-database
|
||||
with:
|
||||
for: mysql, pgsql
|
||||
do: dump
|
||||
dump-suffix: multi-changed
|
||||
|
||||
######################################### multi-auto ##
|
||||
|
||||
- name: Prepare databases
|
||||
uses: ./.github/actions/manage-database
|
||||
with:
|
||||
for: mysql, pgsql
|
||||
do: drop, create
|
||||
|
||||
- name: Configure multihost schema
|
||||
run: |
|
||||
sed -i 's|MULTIHOST_SCHEMA|true|g' ${{ env.DEV }}/conf/ejabberd.yml
|
||||
|
||||
- name: Run ejabberd for multihost
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: start, stop, logs, check
|
||||
username: user2
|
||||
|
||||
- name: Dump multihost databases
|
||||
uses: ./.github/actions/manage-database
|
||||
with:
|
||||
for: mysql, pgsql
|
||||
do: dump
|
||||
dump-suffix: multi-auto
|
||||
|
||||
############################################ compare ##
|
||||
|
||||
- name: View SQL schemas (mysql)
|
||||
run: |
|
||||
perl test/ejabberd_SUITE_data/sql_sort.pl \
|
||||
<mysql-multi-auto.sql >mysql-multi2.sql
|
||||
perl test/ejabberd_SUITE_data/sql_sort.pl \
|
||||
<mysql-multi-changed.sql >mysql-changed2.sql
|
||||
echo "::group::differences multi-auto > multi-changed"
|
||||
diff -u mysql-multi2.sql mysql-changed2.sql || echo ok
|
||||
echo "::endgroup::"
|
||||
echo "::group::multi-auto.sql"
|
||||
cat mysql-multi-auto.sql
|
||||
echo "::endgroup::"
|
||||
echo "::group::multi-changed.sql"
|
||||
cat mysql-multi-changed.sql
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: View SQL schemas (pgsql)
|
||||
run: |
|
||||
perl test/ejabberd_SUITE_data/sql_sort.pl \
|
||||
<pgsql-multi-auto.sql >pgsql-multi2.sql
|
||||
perl test/ejabberd_SUITE_data/sql_sort.pl \
|
||||
<pgsql-multi-changed.sql >pgsql-changed2.sql
|
||||
echo "::group::differences (multi-auto > multi-changed)"
|
||||
diff -u pgsql-multi2.sql pgsql-changed2.sql || echo ok
|
||||
echo "::endgroup::"
|
||||
echo "::group::multi-auto.sql"
|
||||
cat pgsql-multi-auto.sql
|
||||
echo "::endgroup::"
|
||||
echo "::group::multi-changed.sql"
|
||||
cat pgsql-multi-changed.sql
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: View ejabberd logs
|
||||
if: always()
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: logs
|
||||
|
||||
@@ -15,9 +15,17 @@ env:
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
container:
|
||||
name: Container
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-24.04${{ matrix.suffix }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [amd64, arm64]
|
||||
include:
|
||||
- platform: amd64
|
||||
suffix:
|
||||
- platform: arm64
|
||||
suffix: -arm
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
@@ -32,8 +40,7 @@ jobs:
|
||||
repository: processone/ejabberd-contrib
|
||||
path: .ejabberd-modules/sources/ejabberd-contrib
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -43,24 +50,19 @@ jobs:
|
||||
id: gitdescribe
|
||||
run: echo "ver=$(git describe --tags)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: suffix=--${{ matrix.platform }}
|
||||
labels: |
|
||||
org.opencontainers.image.revision=${{ steps.gitdescribe.outputs.ver }}
|
||||
org.opencontainers.image.licenses=GPL-2.0
|
||||
org.opencontainers.image.vendor=ProcessOne
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
build-args: |
|
||||
VERSION=${{ steps.gitdescribe.outputs.ver }}
|
||||
@@ -69,6 +71,50 @@ jobs:
|
||||
context: .
|
||||
file: .github/container/Dockerfile
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/${{ matrix.platform }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
|
||||
merge:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
image-uri: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.merge.outputs.digest }}
|
||||
steps:
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta-amd
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: suffix=--amd64
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta-arm
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: suffix=--arm64
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta-result
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- uses: int128/docker-manifest-create-action@v2
|
||||
id: merge
|
||||
with:
|
||||
index-annotations: ${{ steps.metadata.outputs.labels }}
|
||||
tags: ${{ steps.meta-result.outputs.tags }}
|
||||
sources: |
|
||||
${{ steps.meta-amd.outputs.tags }}
|
||||
${{ steps.meta-arm.outputs.tags }}
|
||||
- uses: dataaxiom/ghcr-cleanup-action@v1
|
||||
with:
|
||||
dry-run: true
|
||||
delete-tags: '*--a??64'
|
||||
delete-untagged: true
|
||||
delete-ghost-images: true
|
||||
delete-partial-images: true
|
||||
delete-orphaned-images: true
|
||||
|
||||
@@ -20,14 +20,25 @@ on:
|
||||
|
||||
jobs:
|
||||
binaries:
|
||||
name: Binaries
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-22.04${{ matrix.suffix }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [amd64, arm64]
|
||||
include:
|
||||
- platform: amd64
|
||||
suffix:
|
||||
- platform: arm64
|
||||
suffix: -arm
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Cache build directory
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/build/
|
||||
key: ${{runner.os}}-ct-ng-1.27.0
|
||||
key: installers-ubuntu-22.04-${{runner.arch}}-${{hashFiles('tools/make-binaries')}}
|
||||
- name: Install prerequisites
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
@@ -40,24 +51,40 @@ jobs:
|
||||
run: |
|
||||
gem install --no-document --user-install fpm
|
||||
echo $HOME/.local/share/gem/ruby/*/bin >> $GITHUB_PATH
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build binary archives
|
||||
run: CHECK_DEPS=false tools/make-binaries
|
||||
- name: Build DEB and RPM packages
|
||||
run: tools/make-packages
|
||||
- name: Build installers
|
||||
run: tools/make-installers
|
||||
|
||||
- run: echo "rel_name_vsn=$(ls *.run | sed 's/-1-linux.*//g')" >> $GITHUB_OUTPUT
|
||||
id: vsn
|
||||
- name: Test RUN
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: run
|
||||
do: deploy, no_acme, start, register, stop, logs, check
|
||||
username: user1
|
||||
host: $(hostname --fqdn)
|
||||
rel_name_vsn: ${{ steps.vsn.outputs.rel_name_vsn }}
|
||||
- name: Test DEB
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: deb
|
||||
do: deploy, register, stop, logs
|
||||
username: user1
|
||||
host: $(hostname --fqdn)
|
||||
rel_name_vsn: ${{ steps.vsn.outputs.rel_name_vsn }}
|
||||
|
||||
- name: Collect packages
|
||||
run: |
|
||||
mkdir ejabberd-packages
|
||||
mv ejabberd_*.deb ejabberd-*.rpm ejabberd-*.run ejabberd-packages
|
||||
- name: Upload packages
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: ejabberd-packages
|
||||
name: ejabberd-packages-${{ matrix.platform }}
|
||||
#
|
||||
# Appending the wildcard character ("*") is a trick to make
|
||||
# "ejabberd-packages" the root directory of the uploaded ZIP file:
|
||||
@@ -75,10 +102,10 @@ jobs:
|
||||
steps:
|
||||
- name: Download packages
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: ejabberd-packages
|
||||
- name: Draft Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
files: ejabberd-packages/*
|
||||
files: |
|
||||
ejabberd-packages-amd64/*
|
||||
ejabberd-packages-arm64/*
|
||||
|
||||
+106
-260
@@ -26,21 +26,19 @@ on:
|
||||
|
||||
jobs:
|
||||
|
||||
################################################################ Rebars #####
|
||||
|
||||
rebars:
|
||||
name: Rebars
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
otp: ['24', '25', '26', '27', '28']
|
||||
otp: ['25', '26', '27', '28']
|
||||
rebar: ['rebar', 'rebar3']
|
||||
exclude:
|
||||
- otp: '24'
|
||||
rebar: 'rebar'
|
||||
- otp: '27'
|
||||
rebar: 'rebar'
|
||||
- otp: '28'
|
||||
rebar: 'rebar'
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: public.ecr.aws/docker/library/erlang:${{ matrix.otp }}
|
||||
|
||||
@@ -48,18 +46,8 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Get old compatible Rebar binaries
|
||||
if: matrix.otp < 24
|
||||
run: |
|
||||
rm rebar
|
||||
rm rebar3
|
||||
wget https://github.com/processone/ejabberd/raw/21.12/rebar
|
||||
wget https://github.com/processone/ejabberd/raw/21.12/rebar3
|
||||
chmod +x rebar
|
||||
chmod +x rebar3
|
||||
|
||||
- name: Get recent compatible Rebar binaries
|
||||
if: matrix.otp > 23 && matrix.otp < 25
|
||||
if: matrix.otp < 25
|
||||
run: |
|
||||
rm rebar
|
||||
rm rebar3
|
||||
@@ -71,20 +59,17 @@ jobs:
|
||||
- name: Prepare libraries
|
||||
run: |
|
||||
apt-get -qq update
|
||||
apt-get purge -y libgd3 nginx
|
||||
apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
apt-get -q -y install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- name: Cache Hex.pm
|
||||
- name: Cache rebar3
|
||||
if: matrix.rebar == 'rebar3'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/rebar3/
|
||||
key: ${{matrix.otp}}-${{hashFiles('rebar.config')}}
|
||||
|
||||
- name: Unlock eredis dependency
|
||||
if: matrix.rebar == 'rebar3' && matrix.otp < 21
|
||||
run: rebar3 unlock eredis
|
||||
_build/default/lib/
|
||||
key: runtime-${{ matrix.otp }}-${{matrix.rebar}}-${{hashFiles('rebar.config')}}
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
@@ -93,94 +78,47 @@ jobs:
|
||||
--prefix=/tmp/ejabberd \
|
||||
--with-min-erlang=9.0.5 \
|
||||
--enable-all \
|
||||
--disable-elixir \
|
||||
--disable-tools \
|
||||
--disable-odbc
|
||||
--disable-elixir
|
||||
make
|
||||
|
||||
- run: make hooks
|
||||
- run: make options
|
||||
- run: make xref
|
||||
|
||||
- run: make dialyzer
|
||||
- run: make elvis
|
||||
if: matrix.otp > '25' && matrix.rebar == 'rebar3'
|
||||
|
||||
- name: Prepare rel (rebar2)
|
||||
if: matrix.rebar == 'rebar'
|
||||
run: |
|
||||
mkdir -p _build/prod && ln -s `pwd`/rel/ _build/prod/rel
|
||||
mkdir -p _build/dev && ln -s `pwd`/rel/ _build/dev/rel
|
||||
- name: Production
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: prod
|
||||
do: deploy, start, register, stop, logs, check
|
||||
tool: ${{ matrix.rebar }}
|
||||
username: user1
|
||||
|
||||
- name: Run rel
|
||||
run: |
|
||||
make rel
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl start \
|
||||
&& _build/prod/rel/ejabberd/bin/ejabberdctl started
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl stop \
|
||||
&& _build/prod/rel/ejabberd/bin/ejabberdctl stopped
|
||||
- name: Development
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: deploy, start, register, stop, logs, check
|
||||
tool: ${{ matrix.rebar }}
|
||||
username: user2
|
||||
|
||||
- name: Run dev
|
||||
run: |
|
||||
make dev
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl start \
|
||||
&& _build/dev/rel/ejabberd/bin/ejabberdctl started
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl stop \
|
||||
&& _build/dev/rel/ejabberd/bin/ejabberdctl stopped
|
||||
- name: Installed
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: install
|
||||
do: deploy, start, register, stop, logs, check
|
||||
tool: ${{ matrix.rebar }}
|
||||
username: user3
|
||||
|
||||
- name: Run install
|
||||
run: |
|
||||
make install
|
||||
/tmp/ejabberd/sbin/ejabberdctl start \
|
||||
&& /tmp/ejabberd/sbin/ejabberdctl started
|
||||
/tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass
|
||||
/tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log
|
||||
/tmp/ejabberd/sbin/ejabberdctl stop \
|
||||
&& /tmp/ejabberd/sbin/ejabberdctl stopped
|
||||
|
||||
- name: View logs
|
||||
run: |
|
||||
echo "===> Registered:"
|
||||
cat registered.log
|
||||
echo "===> Prod:"
|
||||
cat _build/prod/rel/ejabberd/logs/*
|
||||
echo "===> Dev:"
|
||||
cat _build/dev/rel/ejabberd/logs/*
|
||||
echo "===> Install:"
|
||||
cat /tmp/ejabberd/var/log/ejabberd/*
|
||||
|
||||
- name: Check logs
|
||||
run: |
|
||||
grep -q '^user1$' registered.log
|
||||
grep -q '^user2$' registered.log
|
||||
grep -q '^user3$' registered.log
|
||||
grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
test $(find _build/prod/rel/ -empty -name error.log)
|
||||
grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
test $(find _build/dev/rel/ -empty -name error.log)
|
||||
grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log)
|
||||
|
||||
- name: View logs failures
|
||||
if: always()
|
||||
run: |
|
||||
cat _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
cat _build/prod/rel/ejabberd/logs/error.log
|
||||
cat _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
cat _build/dev/rel/ejabberd/logs/error.log
|
||||
cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
cat /tmp/ejabberd/var/log/ejabberd/error.log
|
||||
####################################################### Rebar3 + Elixir #####
|
||||
|
||||
rebar3-elixir:
|
||||
name: Rebar3+Elixir
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
elixir: ['1.14', '1.15', '1.16', '1.17', '1.18']
|
||||
runs-on: ubuntu-24.04
|
||||
elixir: ['1.14', '1.18', '1.19']
|
||||
container:
|
||||
image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }}
|
||||
|
||||
@@ -191,26 +129,26 @@ jobs:
|
||||
- name: Prepare libraries
|
||||
run: |
|
||||
apt-get -qq update
|
||||
apt-get -y purge libgd3 nginx
|
||||
apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
apt-get -q -y install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- name: Enable Module.Example and an Elixir dependency
|
||||
run: |
|
||||
sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" ejabberd.yml.example
|
||||
sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" \
|
||||
ejabberd.yml.example
|
||||
cat ejabberd.yml.example
|
||||
sed -i 's|^{deps, \[\(.*\)|{deps, [{decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}},\n \1|g' rebar.config
|
||||
cat rebar.config
|
||||
|
||||
- name: Cache Hex.pm
|
||||
- name: Cache hex.pm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/rebar3/
|
||||
key: ${{matrix.elixir}}-${{hashFiles('rebar.config')}}
|
||||
key: runtime-${{matrix.elixir}}-${{hashFiles('rebar.config')}}
|
||||
|
||||
- name: Install Hex and Rebar3 manually on older Elixir
|
||||
if: matrix.elixir <= '1.14'
|
||||
if: matrix.elixir < '1.15'
|
||||
run: |
|
||||
mix local.hex --force
|
||||
mix local.rebar --force
|
||||
@@ -220,90 +158,45 @@ jobs:
|
||||
./autogen.sh
|
||||
./configure --with-rebar=./rebar3 \
|
||||
--prefix=/tmp/ejabberd \
|
||||
--enable-all \
|
||||
--disable-odbc
|
||||
make
|
||||
--enable-all
|
||||
make scripts deps
|
||||
./rebar3 eunit --verbose
|
||||
|
||||
- run: make hooks
|
||||
- run: make options
|
||||
- run: make xref
|
||||
#- run: make dialyzer
|
||||
- run: make elvis
|
||||
if: matrix.otp > '25'
|
||||
|
||||
- name: Run rel
|
||||
run: |
|
||||
make rel
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl start \
|
||||
&& _build/prod/rel/ejabberd/bin/ejabberdctl started
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl stop \
|
||||
&& _build/prod/rel/ejabberd/bin/ejabberdctl stopped
|
||||
- name: Production
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: prod
|
||||
do: deploy, start, register, stop, logs, check
|
||||
username: user1
|
||||
|
||||
- name: Run dev
|
||||
run: |
|
||||
make dev
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl start \
|
||||
&& _build/dev/rel/ejabberd/bin/ejabberdctl started
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl stop \
|
||||
&& _build/dev/rel/ejabberd/bin/ejabberdctl stopped
|
||||
- name: Release
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: deploy, start, register, stop, logs, check
|
||||
username: user2
|
||||
|
||||
- name: Run install
|
||||
run: |
|
||||
make install
|
||||
/tmp/ejabberd/sbin/ejabberdctl start \
|
||||
&& /tmp/ejabberd/sbin/ejabberdctl started
|
||||
/tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass
|
||||
/tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log
|
||||
/tmp/ejabberd/sbin/ejabberdctl stop \
|
||||
&& /tmp/ejabberd/sbin/ejabberdctl stopped
|
||||
- name: Installed
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: install
|
||||
do: deploy, start, register, stop, logs, check
|
||||
username: user3
|
||||
|
||||
- name: View logs
|
||||
if: always()
|
||||
run: |
|
||||
echo "===> Registered:"
|
||||
cat registered.log
|
||||
echo "===> Prod:"
|
||||
cat _build/prod/rel/ejabberd/logs/*
|
||||
echo "===> Dev:"
|
||||
cat _build/dev/rel/ejabberd/logs/*
|
||||
echo "===> Install:"
|
||||
cat /tmp/ejabberd/var/log/ejabberd/*
|
||||
|
||||
- name: Check logs
|
||||
if: always()
|
||||
run: |
|
||||
grep -q '^user1$' registered.log
|
||||
grep -q '^user2$' registered.log
|
||||
grep -q '^user3$' registered.log
|
||||
grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'Stopping Ejabberd.Module.Example' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
test $(find _build/prod/ -empty -name error.log)
|
||||
grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'Stopping Ejabberd.Module.Example' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
test $(find _build/dev/ -empty -name error.log)
|
||||
grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
grep -q 'Stopping Ejabberd.Module.Example' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log)
|
||||
|
||||
- name: View logs failures
|
||||
if: failure()
|
||||
run: |
|
||||
cat _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
cat _build/prod/rel/ejabberd/logs/error.log
|
||||
cat _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
cat _build/dev/rel/ejabberd/logs/error.log
|
||||
cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
cat /tmp/ejabberd/var/log/ejabberd/error.log
|
||||
################################################################### Mix #####
|
||||
|
||||
mix:
|
||||
name: Mix
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
elixir: ['1.14', '1.15', '1.16', '1.17', '1.18']
|
||||
runs-on: ubuntu-24.04
|
||||
elixir: ['1.14', '1.18', '1.19']
|
||||
container:
|
||||
image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }}
|
||||
|
||||
@@ -314,11 +207,10 @@ jobs:
|
||||
- name: Prepare libraries
|
||||
run: |
|
||||
apt-get -qq update
|
||||
apt-get -y purge libgd3 nginx
|
||||
apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
apt-get -q -y install libexpat1-dev libgd-dev libpam0g-dev \
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- name: Remove Elixir Matchers
|
||||
- name: Remove Elixir matchers
|
||||
run: |
|
||||
echo "::remove-matcher owner=elixir-mixCompileWarning::"
|
||||
echo "::remove-matcher owner=elixir-credoOutputDefault::"
|
||||
@@ -328,20 +220,21 @@ jobs:
|
||||
|
||||
- name: Enable Module.Example and an Elixir dependency
|
||||
run: |
|
||||
sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" ejabberd.yml.example
|
||||
sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" \
|
||||
ejabberd.yml.example
|
||||
cat ejabberd.yml.example
|
||||
sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config
|
||||
cat rebar.config
|
||||
|
||||
- name: Cache Hex.pm
|
||||
- name: Cache hex.pm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.hex/
|
||||
key: ${{matrix.elixir}}-${{hashFiles('mix.exs')}}
|
||||
key: runtime-${{matrix.elixir}}-${{hashFiles('mix.exs')}}
|
||||
|
||||
- name: Install Hex and Rebar3 manually on older Elixir
|
||||
if: matrix.elixir <= '1.14'
|
||||
if: matrix.elixir < '1.15'
|
||||
run: |
|
||||
mix local.hex --force
|
||||
mix local.rebar --force
|
||||
@@ -354,79 +247,32 @@ jobs:
|
||||
--enable-all
|
||||
make
|
||||
|
||||
- run: make hooks
|
||||
- run: make options
|
||||
- run: make xref
|
||||
|
||||
- run: make dialyzer
|
||||
- run: make elvis
|
||||
if: matrix.otp > '25'
|
||||
|
||||
- run: make edoc
|
||||
|
||||
- name: Run rel
|
||||
run: |
|
||||
make rel
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl start \
|
||||
&& _build/prod/rel/ejabberd/bin/ejabberdctl started
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log
|
||||
_build/prod/rel/ejabberd/bin/ejabberdctl stop \
|
||||
&& _build/prod/rel/ejabberd/bin/ejabberdctl stopped
|
||||
- name: Production
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: prod
|
||||
do: deploy, start, register, stop, logs, check
|
||||
username: user1
|
||||
|
||||
- name: Run dev
|
||||
run: |
|
||||
make dev
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl start \
|
||||
&& _build/dev/rel/ejabberd/bin/ejabberdctl started
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log
|
||||
_build/dev/rel/ejabberd/bin/ejabberdctl stop \
|
||||
&& _build/dev/rel/ejabberd/bin/ejabberdctl stopped
|
||||
- name: Development
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: deploy, start, register, stop, logs, check
|
||||
username: user2
|
||||
|
||||
- name: Run install
|
||||
run: |
|
||||
make install
|
||||
/tmp/ejabberd/sbin/ejabberdctl start \
|
||||
&& /tmp/ejabberd/sbin/ejabberdctl started
|
||||
/tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass
|
||||
/tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log
|
||||
/tmp/ejabberd/sbin/ejabberdctl stop \
|
||||
&& /tmp/ejabberd/sbin/ejabberdctl stopped
|
||||
|
||||
- name: View logs
|
||||
if: always()
|
||||
run: |
|
||||
echo "===> Registered:"
|
||||
cat registered.log
|
||||
echo "===> Prod:"
|
||||
cat _build/prod/rel/ejabberd/logs/*
|
||||
echo "===> Dev:"
|
||||
cat _build/dev/rel/ejabberd/logs/*
|
||||
echo "===> Install:"
|
||||
cat /tmp/ejabberd/var/log/ejabberd/*
|
||||
|
||||
- name: Check logs
|
||||
if: always()
|
||||
run: |
|
||||
grep -q '^user1$' registered.log
|
||||
grep -q '^user2$' registered.log
|
||||
grep -q '^user3$' registered.log
|
||||
grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'Stopping Ejabberd.Module.Example' _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
test $(find _build/prod/ -empty -name error.log)
|
||||
grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
grep -q 'Stopping Ejabberd.Module.Example' _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
test $(find _build/dev/ -empty -name error.log)
|
||||
grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
grep -q 'Stopping Ejabberd.Module.Example' /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log)
|
||||
|
||||
- name: View logs failures
|
||||
if: failure()
|
||||
run: |
|
||||
cat _build/prod/rel/ejabberd/logs/ejabberd.log
|
||||
cat _build/prod/rel/ejabberd/logs/error.log
|
||||
cat _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||
cat _build/dev/rel/ejabberd/logs/error.log
|
||||
cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log
|
||||
cat /tmp/ejabberd/var/log/ejabberd/error.log
|
||||
- name: Installed
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: install
|
||||
do: deploy, start, register, stop, logs, check
|
||||
username: user3
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
name: Weekly
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 9 * * 0'
|
||||
|
||||
jobs:
|
||||
|
||||
############################################################### Compile #####
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
strategy:
|
||||
matrix:
|
||||
otp: ['25', '26', '27', '28']
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: ${{ matrix.otp }}
|
||||
hexpm-mirrors: |
|
||||
https://cdn.jsdelivr.net/hex
|
||||
https://builds.hex.pm
|
||||
|
||||
- uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: libexpat1-dev libgd-dev libpam0g-dev
|
||||
libsqlite3-dev libwebp-dev libyaml-dev
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure --with-rebar=./rebar3 \
|
||||
--prefix=/tmp/ejabberd \
|
||||
--enable-all \
|
||||
--disable-elixir \
|
||||
--disable-mssql \
|
||||
--disable-odbc
|
||||
sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script
|
||||
make
|
||||
|
||||
########################################################## Static Tests #####
|
||||
|
||||
- name: Test shell scripts
|
||||
run: |
|
||||
shellcheck ejabberd.init.template
|
||||
shellcheck -x ejabberdctl.template
|
||||
shellcheck .vscode/relive.sh
|
||||
shellcheck rel/setup-dev.sh
|
||||
shellcheck rel/setup-relive.sh
|
||||
shellcheck test/ejabberd_SUITE_data/gencerts.sh
|
||||
shellcheck tools/captcha.sh
|
||||
shellcheck tools/emacs-indent.sh
|
||||
shellcheck tools/generate-doap.sh
|
||||
shellcheck tools/prepare-tr.sh
|
||||
shellcheck tools/rebar3-format.sh
|
||||
|
||||
- run: make hooks
|
||||
- run: make options
|
||||
- run: make xref
|
||||
- run: make dialyzer
|
||||
- run: make test-eunit
|
||||
- run: make elvis
|
||||
if: matrix.otp > '25'
|
||||
|
||||
- run: make install -s
|
||||
|
||||
######################################################### Dynamic Tests #####
|
||||
|
||||
- name: Check production release
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: prod
|
||||
do: deploy, start, stop, check
|
||||
|
||||
- name: Start development release
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: deploy, no_tls, start, register
|
||||
username: user123
|
||||
|
||||
- name: Stop development release
|
||||
if: always()
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: stop, check
|
||||
|
||||
- name: View production logs
|
||||
if: always()
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: prod
|
||||
do: logs
|
||||
|
||||
- name: View development logs
|
||||
if: always()
|
||||
uses: ./.github/actions/manage-ejabberd
|
||||
with:
|
||||
for: dev
|
||||
do: logs
|
||||
|
||||
##################################################### Common Test Suite #####
|
||||
|
||||
- name: Prepare database
|
||||
uses: ./.github/actions/manage-database
|
||||
with:
|
||||
for: mysql, pgsql, redis
|
||||
do: install, start, user, create
|
||||
|
||||
- name: Setup multihost SQL schema
|
||||
run: |
|
||||
sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl
|
||||
|
||||
- name: Run tests
|
||||
id: ct
|
||||
run: make test
|
||||
|
||||
- name: Send to coveralls
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
DIAGNOSTIC=1 ./rebar3 as test coveralls send
|
||||
|
||||
- name: Check results
|
||||
if: always() && (steps.ct.outcome != 'skipped')
|
||||
id: ctresults
|
||||
run: |
|
||||
[[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
|
||||
ln `find logs/ -name suite.log` logs/suite.log
|
||||
grep 'TEST COMPLETE' logs/suite.log
|
||||
grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
|
||||
test $(find logs/ -empty -name error.log)
|
||||
|
||||
- name: View logs
|
||||
if: always()
|
||||
run: |
|
||||
echo "::group::ejabberd.log"
|
||||
find logs/ -name ejabberd.log -exec cat '{}' ';'
|
||||
echo "::endgroup::"
|
||||
echo "::group::error.log"
|
||||
find logs/ -name error.log -exec cat '{}' ';'
|
||||
echo "::endgroup::"
|
||||
echo "::group::exunit.log"
|
||||
find logs/ -name exunit.log -exec cat '{}' ';'
|
||||
echo "::endgroup::"
|
||||
echo "::group::suite.log (only failures)"
|
||||
cat logs/suite.log | awk \
|
||||
'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
|
||||
echo "::endgroup::"
|
||||
echo "::group::suite.log (complete)"
|
||||
cat logs/suite.log
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Upload CT logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: ct-logs-${{ matrix.otp }}
|
||||
path: _build/test/logs
|
||||
retention-days: 14
|
||||
|
||||
############################################################# Coveralls #####
|
||||
|
||||
cover:
|
||||
needs: test
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Finish parallel upload to coveralls
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
curl -v -k https://coveralls.io/webhook \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{"repo_name":"$GITHUB_REPOSITORY",
|
||||
"repo_token":"$GITHUB_TOKEN",
|
||||
"payload":{"build_num":$GITHUB_RUN_ID,
|
||||
"status":"done"}}'
|
||||
@@ -1,3 +1,78 @@
|
||||
## Version 25.10
|
||||
|
||||
#### Ad-hoc Commands
|
||||
|
||||
- `mod_configure`: New ad-hoc commands that were missing from XEP-0133
|
||||
- `mod_adhoc_api`: Add support for asynchronous command calling
|
||||
- `mod_adhoc_api`: If argument is a list of jids, type is `jid-multi`
|
||||
- `mod_adhoc_api`: If field has several values, type is `text-multi`
|
||||
|
||||
#### API Commands
|
||||
|
||||
- Add commands argument type `binary_or_list`
|
||||
- `mod_http_api`: Format sub elements for tuples from maps
|
||||
- `mod_admin_extra`: Improve roster API commands documentation
|
||||
- `mod_announce`: New API commands, reusing existing ad-hoc functions
|
||||
- `ejabberd_admin`: New API command `restart_kindly`, improve `stop_kindly`
|
||||
- `mod_admin_extra`: New API commands `list_banned` and `count_banned`
|
||||
- `mod_admin_extra`: Improve API command `status_list`: support for status to be a list
|
||||
- `mod_muc_admin`: New API commands `muc_get_registered_nick` and nicks ([#4468](https://github.com/processone/ejabberd/issues/4468))
|
||||
- Use `mod_private:del_data` in `unban_account` API command
|
||||
|
||||
#### Configuration
|
||||
|
||||
- Rename `New` SQL schema to `Multihost`, and `Default` to `Singlehost` ([#4456](https://github.com/processone/ejabberd/issues/4456))
|
||||
- Add config transformer from `use_new_schema` -> `sql_multihost_schema`
|
||||
- `mod_sip`: Fix problem parsing `via` in `yconf` library ([#4444](https://github.com/processone/ejabberd/issues/4444))
|
||||
|
||||
#### Erlang/OTP support
|
||||
|
||||
- Enable feature `maybe_expr` in the compiler for Erlang/OTP 26 ([#4459](https://github.com/processone/ejabberd/issues/4459))
|
||||
- Enable feature `maybe_expr` also in the runtime for Erlang/OTP 25
|
||||
- Runtime: Remove Erlang 24 which won't work anymore with `maybe_expr`
|
||||
- Remove `EX_RULE` and `EX_STACK` macros only used with ancient erlang
|
||||
|
||||
#### GitHub Workflows
|
||||
|
||||
- CI: Bump XMPP-Interop-Testing/xmpp-interop-tests-action ([#4469](https://github.com/processone/ejabberd/issues/4469))
|
||||
- CI: Don't care to include commit details in the CT logs HTML page
|
||||
- CI and Runtime: Reorganize steps to run in parallel, and ARM runner ([#4460](https://github.com/processone/ejabberd/issues/4460))
|
||||
- Add local composite actions to manage ejabberd and databases
|
||||
- Container: Build ARM in native runner instead of QEMU, merge and clean
|
||||
- Installers: Generate ARM installers in native runner
|
||||
- Tests: Run agnostic-database tests only once, not for every backend
|
||||
- Tests: The odbc backend is not actually used in Commont Tests
|
||||
- Weekly: New workflow that condenses CI, test all erlang without caching
|
||||
|
||||
#### Installers and Container
|
||||
|
||||
- Bump Erlang/OTP version to 27.3.4.3 in installers and container
|
||||
- Bump Expat 2.7.3, OpenSSL 3.5.4, unixODBC 2.3.14 in installers
|
||||
|
||||
#### MUC
|
||||
|
||||
- `mod_mam`: New option `archive_muc_as_mucsub`
|
||||
- `mod_muc`: Check if room is hibernated before calling mod_muc process
|
||||
- `mod_muc`: Update implementation of XEP-0317 Hats to version 0.3.1 ([#4380](https://github.com/processone/ejabberd/issues/4380))
|
||||
- `mod_muc`: Make mod_muc_sql properly handle new hats data ([#4380](https://github.com/processone/ejabberd/issues/4380))
|
||||
- `mod_muc_room`: Don't require password if user is owner of room
|
||||
- `mod_muc_admin`: Use in WebAdmin the new API commands that get nick registers
|
||||
|
||||
#### Core and Modules
|
||||
|
||||
- `ejabberd_http_ws`: Pass HTTP headers from WS to C2S connection ([#4471](https://github.com/processone/ejabberd/issues/4471))
|
||||
- `ejabberd_listener`: Properly pass `send_timeout` option to listener sockets
|
||||
- `ejabberdctl`: When ping returns pang, return also status code 1 ([#4327](https://github.com/processone/ejabberd/issues/4327))
|
||||
- `ext_mod`: Print module status message after installation
|
||||
- `misc`: json_encode should always call json with our filter
|
||||
- `mod_admin_update_sql`: Use same index name than when creating database
|
||||
- `mod_block_strangers`: Clarify `access` and `captcha` documentation ([#4221](https://github.com/processone/ejabberd/issues/4221))
|
||||
- `mod_http_upload`: Encode URL before parsing, as done before `bba1a1e3c` ([#4450](https://github.com/processone/ejabberd/issues/4450))
|
||||
- `mod_private`: Add `del_data/3`, `get_users_with_data/2`, `count_users_with_data/2`
|
||||
- `mod_pubsub`: Don't catch `exit:{aborted, _}` inside mnesia transactions
|
||||
- `mod_push`: Run new hook `push_send_notification` ([#4383](https://github.com/processone/ejabberd/issues/4383))
|
||||
- WebAdmin: Respect newline and whitespace characters in results
|
||||
|
||||
## Version 25.08
|
||||
|
||||
#### API Commands
|
||||
|
||||
+1
-1
@@ -1072,7 +1072,7 @@ Let's summarize the differences between both container images. Legend:
|
||||
| Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) |
|
||||
| Built for | stable releases <br /> `master` branch | stable releases <br /> [`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) |
|
||||
| Architectures | `linux/amd64` <br /> `linux/arm64` | `linux/amd64` |
|
||||
| Software | Erlang/OTP 27.3.4.2-alpine <br /> Elixir 1.18.4 | Alpine 3.19 <br /> Erlang/OTP 26.2 <br /> Elixir 1.15.7 |
|
||||
| Software | Erlang/OTP 27.3.4.3-alpine <br /> Elixir 1.18.4 | Alpine 3.22 <br /> Erlang/OTP 26.2 <br /> Elixir 1.18.3 |
|
||||
| Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/) <br /> [ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) |
|
||||
| :black_square_button: **Additional content** |
|
||||
| [ejabberd-contrib](#ejabberd-contrib) | included | not included |
|
||||
|
||||
+9
-5
@@ -2,8 +2,11 @@
|
||||
#' definitions
|
||||
#
|
||||
|
||||
# Only required for Erlang/OTP 25
|
||||
MAYBE=ERL_FLAGS="-enable-feature maybe_expr"
|
||||
|
||||
ESCRIPT = @ESCRIPT@
|
||||
REBAR = @rebar@ # rebar|rebar3|mix binary (or path to binary)
|
||||
REBAR = $(MAYBE) @rebar@ # rebar|rebar3|mix binary (or path to binary)
|
||||
REBAR3 = @REBAR3@ # path to rebar3 binary
|
||||
MIX = @rebar@
|
||||
AWK = @AWK@
|
||||
@@ -123,7 +126,7 @@ REBAR_VER_318:=$(shell $(REBAR) --version | $(AWK) -F '[ .]' '/rebar / {print ($
|
||||
endif
|
||||
|
||||
ifeq "$(REBAR_VER)" "6"
|
||||
REBAR=$(MIX)
|
||||
REBAR=$(MAYBE) $(MIX)
|
||||
SKIPDEPS=
|
||||
LISTDEPS=deps.tree
|
||||
UPDATEDEPS=deps.update
|
||||
@@ -141,7 +144,7 @@ ifeq "$(REBAR_VER)" "6"
|
||||
ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)"
|
||||
REBARREL=MIX_ENV=prod $(REBAR) release --overwrite
|
||||
REBARDEV=MIX_ENV=dev $(REBAR) release --overwrite
|
||||
RELIVECMD=$(ESCRIPT) rel/relive.escript && MIX_ENV=dev RELIVE=true $(IEX) --name ejabberd@localhost -S mix run
|
||||
RELIVECMD=$(ESCRIPT) rel/relive.escript && MIX_ENV=dev RELIVE=true $(MAYBE) $(IEX) --name ejabberd@localhost -S mix run
|
||||
REL_LIB_DIR = _build/dev/rel/ejabberd/lib
|
||||
COPY_REL_TARGET = dev
|
||||
GET_DEPS_TRANSLATIONS=MIX_ENV=translations $(REBAR) $(GET_DEPS)
|
||||
@@ -190,9 +193,10 @@ else
|
||||
XREFOPTIONS=
|
||||
CLEANARG=
|
||||
REBARREL=$(REBAR) generate
|
||||
REBARDEV=
|
||||
REBARDEV=@echo "Rebar2 detected... dev not supported.\
|
||||
\nTry: make prod, or: ./configure --with-rebar=rebar3 ; make dev"
|
||||
RELIVECMD=@echo "Rebar2 detected... relive not supported.\
|
||||
\nTry: ./configure --with-rebar=rebar3 ; make relive"
|
||||
\nTry: ./configure --with-rebar=rebar3 ; make relive"
|
||||
REL_LIB_DIR = rel/ejabberd/lib
|
||||
COPY_REL_TARGET = rel
|
||||
endif
|
||||
|
||||
+14
-6
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.08` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd])
|
||||
AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.10` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd])
|
||||
|
||||
AC_ARG_WITH(min-erlang,
|
||||
AS_HELP_STRING([--with-min-erlang=version],[set minimal required erlang version, default to OTP25]),
|
||||
@@ -192,12 +192,20 @@ AC_ARG_ENABLE(mysql,
|
||||
esac],[if test "x$mysql" = "x"; then mysql=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(new_sql_schema,
|
||||
[AS_HELP_STRING([--enable-new-sql-schema],[use new SQL schema by default (default: no)])],
|
||||
[AS_HELP_STRING([--enable-new-sql-schema],[obsolete, use --enable-multihost-sql-schema instead (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) new_sql_schema=true ;;
|
||||
no) new_sql_schema=false ;;
|
||||
yes) multihost_sql_schema=true ;;
|
||||
no) multihost_sql_schema=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-new-sql-schema) ;;
|
||||
esac],[new_sql_schema=false])
|
||||
esac],[multihost_sql_schema=false])
|
||||
|
||||
AC_ARG_ENABLE(multihost_sql_schema,
|
||||
[AS_HELP_STRING([--enable-multihost-sql-schema],[use multihost SQL schema by default (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) multihost_sql_schema=true ;;
|
||||
no) multihost_sql_schema=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-multihost-sql-schema) ;;
|
||||
esac],[multihost_sql_schema=false])
|
||||
|
||||
AC_ARG_ENABLE(odbc,
|
||||
[AS_HELP_STRING([--enable-odbc],[enable pure ODBC support (default: no)])],
|
||||
@@ -318,7 +326,7 @@ esac
|
||||
AC_MSG_RESULT([build tool to use (change using --with-rebar): $rebar])
|
||||
|
||||
AC_SUBST(roster_gateway_workaround)
|
||||
AC_SUBST(new_sql_schema)
|
||||
AC_SUBST(multihost_sql_schema)
|
||||
AC_SUBST(full_xml)
|
||||
AC_SUBST(odbc)
|
||||
AC_SUBST(mssql)
|
||||
|
||||
+3
-3
@@ -299,7 +299,7 @@
|
||||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0133.html"/>
|
||||
<xmpp:version>1.3.0</xmpp:version>
|
||||
<xmpp:version>1.3.1</xmpp:version>
|
||||
<xmpp:since>13.10</xmpp:since>
|
||||
<xmpp:status>partial</xmpp:status>
|
||||
<xmpp:note>mod_configure</xmpp:note>
|
||||
@@ -578,10 +578,10 @@
|
||||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0317.html"/>
|
||||
<xmpp:version>0.2.0</xmpp:version>
|
||||
<xmpp:version>0.3.1</xmpp:version>
|
||||
<xmpp:since>21.12</xmpp:since>
|
||||
<xmpp:status>complete</xmpp:status>
|
||||
<xmpp:note>mod_muc_room, 0.2.0 since 25.03</xmpp:note>
|
||||
<xmpp:note>mod_muc_room, 0.3.1 since 25.10</xmpp:note>
|
||||
</xmpp:SupportedXep>
|
||||
</implements>
|
||||
<implements>
|
||||
|
||||
@@ -121,6 +121,9 @@ export CONTRIB_MODULES_CONF_DIR
|
||||
export ERL_LIBS
|
||||
export SCRIPT_DIR
|
||||
|
||||
# Only required for Erlang/OTP 25:
|
||||
export ERL_FLAGS="$ERL_FLAGS -enable-feature maybe_expr"
|
||||
|
||||
set_dist_client()
|
||||
{
|
||||
[ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -dist_listen false"
|
||||
@@ -553,7 +556,8 @@ case $1 in
|
||||
-noinput -hidden \
|
||||
-eval 'net_kernel:connect_node('"'$PEER'"')' \
|
||||
-eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \
|
||||
-s erlang halt -output text
|
||||
-eval 'halt(case net_adm:ping('"'$PEER'"') of pong -> 0; pang -> 1 end).' \
|
||||
-output text
|
||||
;;
|
||||
started)
|
||||
set_dist_client
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
{elvis_style, function_naming_convention, disable},
|
||||
{elvis_style, god_modules, #{limit => 300}},
|
||||
{elvis_style, invalid_dynamic_call, disable},
|
||||
{elvis_style, macro_names, disable},
|
||||
{elvis_style, max_function_arity, disable}, % #{max_arity => 15}},
|
||||
{elvis_style, nesting_level, disable},
|
||||
{elvis_style, no_author, disable},
|
||||
|
||||
@@ -27,10 +27,14 @@
|
||||
rescode | restuple.
|
||||
|
||||
%% The 'any' and 'atom' argument types and 'any' result type
|
||||
%% should only be used %% by commands with tag 'internal',
|
||||
%% should only be used by commands with tag 'internal',
|
||||
%% which are meant to be used only internally in ejabberd,
|
||||
%% and not called using external frontends.
|
||||
|
||||
%% When a command with tag 'async' is called by mod_adhoc_api,
|
||||
%% it is spawned in a new process for the command execution,
|
||||
%% and the command immediately returns success.
|
||||
|
||||
%% The purpose of a command can either be:
|
||||
%% - informative: its purpose is to obtain information
|
||||
%% - modifier: its purpose is to produce some change in the server
|
||||
|
||||
@@ -72,4 +72,4 @@
|
||||
-record(sql_schema_info,
|
||||
{db_type :: pgsql | mysql | sqlite,
|
||||
db_version :: any(),
|
||||
new_schema = true :: boolean()}).
|
||||
multihost_schema = true :: boolean()}).
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2025 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-ifdef(DEPRECATED_GET_STACKTRACE).
|
||||
-define(EX_RULE(Class, Reason, Stack), Class:Reason:Stack).
|
||||
-define(EX_STACK(Stack), Stack).
|
||||
-else.
|
||||
-define(EX_RULE(Class, Reason, _), Class:Reason).
|
||||
-define(EX_STACK(_), erlang:get_stacktrace()).
|
||||
-endif.
|
||||
+3
-2
@@ -23,8 +23,9 @@
|
||||
opts = [] :: list() | '_'}).
|
||||
|
||||
-record(muc_registered,
|
||||
{us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()}, binary()} | '$1',
|
||||
nick = <<"">> :: binary()}).
|
||||
{us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary() | '$1', binary() | '$2'},
|
||||
binary() | '_'} | '$1',
|
||||
nick = <<"">> :: binary() | '$3'}).
|
||||
|
||||
-record(muc_online_room,
|
||||
{name_host :: {binary(), binary()} | '$1' | {'_', binary()} | '_',
|
||||
|
||||
@@ -126,7 +126,8 @@
|
||||
history = #lqueue{} :: lqueue(),
|
||||
subject = [] :: [text()],
|
||||
subject_author = {<<"">>, #jid{}} :: {binary(), jid()},
|
||||
hats_users = #{} :: map(), % FIXME on OTP 21+: #{ljid() => #{binary() => binary()}},
|
||||
hats_defs = #{} :: #{binary() => {binary(), binary()}},
|
||||
hats_users = #{} :: #{ljid() => [binary()]},
|
||||
just_created = erlang:system_time(microsecond) :: true | integer(),
|
||||
activity = treap:empty() :: treap:treap(),
|
||||
room_shaper = none :: ejabberd_shaper:shaper(),
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(private_storage,
|
||||
{usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() |
|
||||
{usns = {<<"">>, <<"">>, <<"">>} :: {binary() | '$1' | '_', binary(), binary() |
|
||||
'$1' | '_'},
|
||||
xml = #xmlel{} :: xmlel() | '_' | '$1'}).
|
||||
|
||||
+147
-27
@@ -2,12 +2,12 @@
|
||||
.\" Title: ejabberd.yml
|
||||
.\" Author: [see the "AUTHOR" section]
|
||||
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
|
||||
.\" Date: 08/22/2025
|
||||
.\" Date: 10/28/2025
|
||||
.\" Manual: \ \&
|
||||
.\" Source: \ \&
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "EJABBERD\&.YML" "5" "08/22/2025" "\ \&" "\ \&"
|
||||
.TH "EJABBERD\&.YML" "5" "10/28/2025" "\ \&" "\ \&"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@@ -82,12 +82,12 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f
|
||||
.sp
|
||||
Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&.
|
||||
.sp
|
||||
It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.08/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&.
|
||||
It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.10/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&.
|
||||
.sp
|
||||
Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&.
|
||||
.SH "TOP LEVEL OPTIONS"
|
||||
.sp
|
||||
This section describes top level options of ejabberd 25\&.08\&. The options that changed in this version are marked with 🟤\&.
|
||||
This section describes top level options of ejabberd 25\&.10\&. The options that changed in this version are marked with 🟤\&.
|
||||
.PP
|
||||
\fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLName|ACLDefinition}}\fR
|
||||
.RS 4
|
||||
@@ -1282,17 +1282,12 @@ This option can be used to tune tick time parameter of
|
||||
\fI1 minute\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBnew_sql_schema\fR: \fItrue | false\fR
|
||||
\fBnew_sql_schema 🟤\fR: \fItrue | false\fR
|
||||
.RS 4
|
||||
Whether to use the
|
||||
\fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at
|
||||
https://github\&.com/processone/ejabberd/tree/25\&.08/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The
|
||||
\fInew\fR
|
||||
schema can handle several XMPP domains in a single ejabberd database\&. Using this
|
||||
\fInew\fR
|
||||
schema is best when serving several XMPP domains and/or changing domains from time to time\&. This avoid need to manage several databases and handle complex configuration changes\&. The default depends on configuration flag
|
||||
\fI\-\-enable\-new\-sql\-schema\fR
|
||||
which is set at compile time\&.
|
||||
\fINote\fR
|
||||
about this option: obsoleted in 25\&.10\&. This option was renamed to
|
||||
\fIsql_schema_multihost\fR
|
||||
in ejabberd 25\&.10\&. Please update your configuration to use the new option name
|
||||
.RE
|
||||
.PP
|
||||
\fBoauth_access\fR: \fIAccessName\fR
|
||||
@@ -1958,6 +1953,22 @@ or
|
||||
if the latter is not set\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBsql_schema_multihost 🟤\fR: \fItrue | false\fR
|
||||
.RS 4
|
||||
\fINote\fR
|
||||
about this option: renamed in 25\&.10\&. Whether to use the
|
||||
\fIdatabase\&.md#default\-and\-new\-schemas|multihost SQL schema\fR\&. All schemas are located at
|
||||
https://github\&.com/processone/ejabberd/tree/25\&.10/sql\&. There are two schemas available\&. The legacy
|
||||
\fIsinglehost\fR
|
||||
schema stores one XMPP domain into one ejabberd database\&. The
|
||||
\fImultihost\fR
|
||||
schema can handle several XMPP domains in a single ejabberd database\&. The
|
||||
\fImultihost\fR
|
||||
schema is preferable when serving several XMPP domains and/or changing domains from time to time\&. This avoid need to manage several databases and handle complex configuration changes\&. The default depends on configuration flag
|
||||
\fI\-\-enable\-sql\-schema\-multihost\fR
|
||||
which is set at compile time\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBsql_server\fR: \fIHost | IP Address | ODBC Connection String | Unix Socket Path\fR
|
||||
.RS 4
|
||||
\fINote\fR
|
||||
@@ -2107,7 +2118,7 @@ seconds\&.
|
||||
.RE
|
||||
.SH "MODULES"
|
||||
.sp
|
||||
This section describes modules options of ejabberd 25\&.08\&. The modules that changed in this version are marked with 🟤\&.
|
||||
This section describes modules options of ejabberd 25\&.10\&. The modules that changed in this version are marked with 🟤\&.
|
||||
.SS "mod_adhoc"
|
||||
.sp
|
||||
def:ad\-hoc command
|
||||
@@ -2277,7 +2288,7 @@ This module can be used to update existing SQL database from the default to the
|
||||
The module has no options\&.
|
||||
.SS "mod_announce"
|
||||
.sp
|
||||
This module enables configured users to broadcast announcements and to set the message of the day (MOTD)\&. Configured users can perform these actions with an XMPP client either using Ad\-Hoc Commands or sending messages to specific JIDs\&.
|
||||
This module enables configured users to broadcast announcements and to set the message of the day (MOTD)\&. Configured users can perform these actions with an XMPP client either using Ad\-Hoc Commands or sending messages to specific JIDs\&. Equivalent API commands are also available\&.
|
||||
.if n \{\
|
||||
.sp
|
||||
.\}
|
||||
@@ -2462,6 +2473,8 @@ Same as top\-level
|
||||
\fIuse_cache\fR
|
||||
option, but applied to this module only\&.
|
||||
.RE
|
||||
.sp
|
||||
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#announce|announce\fR
|
||||
.RE
|
||||
.SS "mod_antispam"
|
||||
.sp
|
||||
@@ -2590,6 +2603,8 @@ modules:
|
||||
.if n \{\
|
||||
.RE
|
||||
.\}
|
||||
.sp
|
||||
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#spam|spam\fR
|
||||
.RE
|
||||
.SS "mod_auth_fast"
|
||||
.sp
|
||||
@@ -2708,9 +2723,11 @@ The option is supposed to be used when
|
||||
\fIallow_local_users\fR
|
||||
and
|
||||
\fIallow_transports\fR
|
||||
are not enough\&. It\(cqs an ACL where
|
||||
are not enough\&. It\(cqs an Access Rule where
|
||||
\fIdeny\fR
|
||||
means the message will be rejected (or a CAPTCHA would be generated for a presence, if configured), and
|
||||
means the stanza will be rejected; there\(cqs an exception if option
|
||||
\fIcaptcha\fR
|
||||
is configured\&. And
|
||||
\fIallow\fR
|
||||
means the sender is whitelisted and the stanza will pass through\&. The default value is
|
||||
\fInone\fR, which means nothing is whitelisted\&.
|
||||
@@ -2732,7 +2749,7 @@ and some server\(cqs JID is in user\(cqs roster, then messages from any user of
|
||||
.PP
|
||||
\fBcaptcha\fR: \fItrue | false\fR
|
||||
.RS 4
|
||||
Whether to generate CAPTCHA or not in response to messages from strangers\&. See also section
|
||||
Whether to generate CAPTCHA challenges in response to incoming presence subscription requests from strangers\&. See also section
|
||||
\fIbasic\&.md#captcha|CAPTCHA\fR
|
||||
of the Configuration Guide\&. The default value is
|
||||
\fIfalse\fR\&.
|
||||
@@ -2956,7 +2973,9 @@ While a client is inactive, queue presence stanzas that indicate (un)availabilit
|
||||
\fItrue\fR\&.
|
||||
.RE
|
||||
.RE
|
||||
.SS "mod_configure"
|
||||
.SS "mod_configure 🟤"
|
||||
.sp
|
||||
\fINote\fR about this option: improved in 25\&.10\&.
|
||||
.sp
|
||||
The module provides server configuration functionalities using XEP\-0030: Service Discovery and XEP\-0050: Ad\-Hoc Commands:
|
||||
.sp
|
||||
@@ -2994,6 +3013,100 @@ XEP\-0133: Service Administration
|
||||
Additional custom ad\-hoc commands specific to ejabberd
|
||||
.RE
|
||||
.sp
|
||||
Ad\-hoc commands from XEP\-0133 that behave differently to the XEP:
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
get\-user\-roster: returns standard fields instead of roster items that client cannot display
|
||||
.RE
|
||||
.sp
|
||||
Those ad\-hoc commands from XEP\-0133 do not include in the response the client that executed the command:
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
get\-active\-users\-num
|
||||
.RE
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
get\-idle\-users\-num
|
||||
.RE
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
get\-active\-users
|
||||
.RE
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
get\-idle\-users
|
||||
.RE
|
||||
.sp
|
||||
Those ad\-hoc commands from XEP\-0133 are not implemented:
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
edit\-blacklist
|
||||
.RE
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
edit\-whitelist
|
||||
.RE
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
edit\-admin
|
||||
.RE
|
||||
.sp
|
||||
This module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&.
|
||||
.sp
|
||||
Please notice that all the ad\-hoc commands implemented by this module have an equivalent API Command that you can execute using \fImod_adhoc_api\fR or any other API frontend\&.
|
||||
@@ -4091,6 +4204,13 @@ This access rule defines who is allowed to modify the MAM preferences\&. The def
|
||||
\fIall\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBarchive_muc_as_mucsub 🟤\fR: \fItrue | false\fR
|
||||
.RS 4
|
||||
\fINote\fR
|
||||
about this option: added in 25\&.10\&. When this option is enabled incoming groupchat messages for users that have mucsub subscription to a room from which message originated will have those messages archived after being converted to mucsub event messages\&.The default value is
|
||||
\fIfalse\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBassume_mam_usage\fR: \fItrue | false\fR
|
||||
.RS 4
|
||||
This option determines how ejabberd\(cqs stream management code (see
|
||||
@@ -4176,7 +4296,7 @@ When this option is disabled, for each individual subscriber a separate mucsub m
|
||||
.sp
|
||||
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#mam|mam\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR
|
||||
.RE
|
||||
.SS "mod_matrix_gw 🟤"
|
||||
.SS "mod_matrix_gw"
|
||||
.sp
|
||||
\fINote\fR about this option: improved in 25\&.08\&.
|
||||
.sp
|
||||
@@ -4861,10 +4981,10 @@ in order to accept their join in the room\&. The default value is
|
||||
Short description of the room\&. The default value is an empty string\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBenable_hats\fR: \fItrue | false\fR
|
||||
\fBenable_hats 🟤\fR: \fItrue | false\fR
|
||||
.RS 4
|
||||
\fINote\fR
|
||||
about this option: improved in 25\&.03\&. Allow extended roles as defined in XEP\-0317 Hats\&. Check the
|
||||
about this option: improved in 25\&.10\&. Allow extended roles as defined in XEP\-0317 Hats\&. Check the
|
||||
\fI\&.\&./\&.\&./tutorials/muc\-hats\&.md|MUC Hats\fR
|
||||
tutorial\&. The default value is
|
||||
\fIfalse\fR\&.
|
||||
@@ -6203,7 +6323,7 @@ modules:
|
||||
.RE
|
||||
.\}
|
||||
.RE
|
||||
.SS "mod_providers 🟤"
|
||||
.SS "mod_providers"
|
||||
.sp
|
||||
\fINote\fR about this option: added in 25\&.08\&.
|
||||
.sp
|
||||
@@ -8862,7 +8982,7 @@ Should the operating system be revealed or not\&. The default value is
|
||||
.RE
|
||||
.SH "LISTENERS"
|
||||
.sp
|
||||
This section describes listeners options of ejabberd 25\&.08\&.
|
||||
This section describes listeners options of ejabberd 25\&.10\&.
|
||||
.sp
|
||||
TODO
|
||||
.SH "AUTHOR"
|
||||
@@ -8870,13 +8990,13 @@ TODO
|
||||
ProcessOne\&.
|
||||
.SH "VERSION"
|
||||
.sp
|
||||
This document describes the configuration file of ejabberd 25\&.08\&. Configuration options of other ejabberd versions may differ significantly\&.
|
||||
This document describes the configuration file of ejabberd 25\&.10\&. Configuration options of other ejabberd versions may differ significantly\&.
|
||||
.SH "REPORTING BUGS"
|
||||
.sp
|
||||
Report bugs to https://github\&.com/processone/ejabberd/issues
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.08/ejabberd\&.yml\&.example
|
||||
Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.10/ejabberd\&.yml\&.example
|
||||
.sp
|
||||
Main site: https://ejabberd\&.im
|
||||
.sp
|
||||
|
||||
@@ -85,7 +85,6 @@ defmodule Ejabberd.MixProject do
|
||||
result = [{:d, :ELIXIR_ENABLED}] ++
|
||||
cond_options() ++
|
||||
Enum.map(includes, fn (path) -> {:i, path} end) ++
|
||||
if_version_above(~c"20", [{:d, :DEPRECATED_GET_STACKTRACE}]) ++
|
||||
if_version_above(~c"20", [{:d, :HAVE_URI_STRING}]) ++
|
||||
if_version_above(~c"20", [{:d, :HAVE_ERL_ERROR}]) ++
|
||||
if_version_below(~c"21", [{:d, :USE_OLD_HTTP_URI}]) ++
|
||||
@@ -99,6 +98,7 @@ defmodule Ejabberd.MixProject do
|
||||
if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) ++
|
||||
if_version_below(~c"26", [{:d, :OTP_BELOW_26}]) ++
|
||||
if_version_below(~c"27", [{:d, :OTP_BELOW_27}]) ++
|
||||
if_version_below(~c"27", [{:feature, :maybe_expr, :enable}]) ++
|
||||
if_version_below(~c"28", [{:d, :OTP_BELOW_28}])
|
||||
defines = for {:d, value} <- result, do: {:d, value}
|
||||
result ++ [{:d, :ALL_DEFS, defines}]
|
||||
@@ -110,7 +110,7 @@ defmodule Ejabberd.MixProject do
|
||||
{config(:debug), :debug_info},
|
||||
{not config(:debug), {:debug_info, false}},
|
||||
{config(:roster_gateway_workaround), {:d, :ROSTER_GATEWAY_WORKAROUND}},
|
||||
{config(:new_sql_schema), {:d, :NEW_SQL_SCHEMA}}
|
||||
{config(:multihost_sql_schema), {:d, :MULTIHOST_SQL_SCHEMA}}
|
||||
], do:
|
||||
option
|
||||
end
|
||||
@@ -130,8 +130,8 @@ defmodule Ejabberd.MixProject do
|
||||
{:p1_utils, "~> 1.0"},
|
||||
{:pkix, "~> 1.0"},
|
||||
{:stringprep, ">= 1.0.26"},
|
||||
{:xmpp, ">= 1.11.1"},
|
||||
{:yconf, ">= 1.0.21"}]
|
||||
{:xmpp, ">= 1.11.2"},
|
||||
{:yconf, ">= 1.0.22"}]
|
||||
++ cond_deps()
|
||||
end
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
"eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"},
|
||||
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
|
||||
"esip": {:hex, :esip, "1.0.59", "eb202f8c62928193588091dfedbc545fe3274c34ecd209961f86dcb6c9ebce88", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.21", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "0bdf2e3c349dc0b144f173150329e675c6a51ac473d7a0b2e362245faad3fbe6"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.39.1", "e19d356a1ba1e8f8cfc79ce1c3f83884b6abfcb79329d435d4bbb3e97ccc286e", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "8abf0ed3e3ca87c0847dfc4168ceab5bedfe881692f1b7c45f4a11b232806865"},
|
||||
"exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"},
|
||||
"ezlib": {:hex, :ezlib, "1.0.15", "d74f5df191784744726a5b1ae9062522c606334f11086363385eb3b772d91357", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "dd14ba6c12521af5cfe6923e73e3d545f4a0897dc66bfab5287fbb7ae3962eab"},
|
||||
"fast_tls": {:hex, :fast_tls, "1.1.25", "da8ed6f05a2452121b087158b17234749f36704c1f2b74dc51db99a1e27ed5e8", [:rebar3], [{:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "59e183b5740e670e02b8aa6be673b5e7779e5fe5bfcc679fe2d4993d1949a821"},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.57", "31efc0f9bceda92069704f7a25830407da5dc3dad1272b810d6f2e13e73cc11a", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "eec34e90adacafe467d5ddab635a014ded73b98b4061554b2d1972173d929c39"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.39", "2e71168091949bab0e5f583b340a99072b4d22d93eb86624e7850a12b1517be4", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "24c7b9ab9e2b9269d64e45f4a2a1280966adb17d31e63365cfd3ee277fb0a78d"},
|
||||
"file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"},
|
||||
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
|
||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||
"jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"},
|
||||
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
||||
@@ -24,16 +24,16 @@
|
||||
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
|
||||
"mqtree": {:hex, :mqtree, "1.0.19", "d769c25f898810725fc7db0dbffe5f72098647048b1be2e6d772f1c2f31d8476", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c81065715c49a1882812f80a5ae2d842e80dd3f2d130530df35990248bf8ce3c"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
|
||||
"p1_acme": {:hex, :p1_acme, "1.0.28", "64d9c17f5412aa92d75b29206b2b984d734a4fe1b7eacb66c3d7a7c697ac612c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "ce686986de3f9d5fd285afe87523cb45329a349c6c6be7acc1ed916725d46423"},
|
||||
"p1_acme": {:hex, :p1_acme, "1.0.29", "86372c34de9ce7e498842828f761060c6b7726001e2853ff118893f6dd192e24", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "08fd38f7fbe2dc28a237aa1b38b306b73455695cc8881a3ddd6a11b7c51f7bc7"},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"},
|
||||
"p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.35", "e13d89f14d717553e85c88a152ce77461916b013d88fcb851e354a0b332d4218", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "e99594446c411c660696795b062336f5c4bd800451d8f620bb4d4ce304e255c2"},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.36", "4bc0535f2f9a2355674dfdc9e1cfc18a722325fed588977fff1c88c5915ae3eb", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "82bca8b895c84f4600eb8d609a32cb5fdd72a7f5bd938dfb29179e08c643fd09"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.28", "9a7088a98d788b4c4880fd3c82d0c135650db13f2e4ef7e10db179791bc94d59", [:rebar3], [], "hexpm", "c49bd44bc4a40ad996691af826dd7e0aa56d4d0cd730817190a1f84d1a7f0033"},
|
||||
"pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"},
|
||||
"sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"},
|
||||
"stringprep": {:hex, :stringprep, "1.0.33", "22f42866b4f6f3c238ea2b9cb6241791184ddedbab55e94a025511f46325f3ca", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "96f8b30bc50887f605b33b46bca1d248c19a879319b8c482790e3b4da5da98c0"},
|
||||
"stun": {:hex, :stun, "1.2.21", "735855314ad22cb7816b88597d2f5ca22e24aa5e4d6010a0ef3affb33ceed6a5", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3d7fe8efb9d05b240a6aa9a6bf8b8b7bff2d802895d170443c588987dc1e12d9"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
|
||||
"xmpp": {:hex, :xmpp, "1.11.1", "60181e7d3e8e48aa3b23b2792075cda37e2e507ec152490b866e61e5320cb1da", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "a5c933df904ab3cec15425da334e410ce84ec3ae7b81efe069e5db368a7b3716"},
|
||||
"yconf": {:hex, :yconf, "1.0.21", "dbae1589381e044529e112b7e0097c89d88df89e446ead53bd33e8d27e2bcc83", [:rebar3], [{:fast_yaml, "1.0.39", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "c524a5f1fd86875d85b469cc2e368c204f97cca1c3918736e21f5001c01d096c"},
|
||||
"xmpp": {:hex, :xmpp, "1.11.2", "6ef43a6e5fb71506af7eccd05bbb6cccb58eb1856c539613e76fd9a5c4e936ba", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "bb681644e15e3efc0008ab3a717944d67cf611a4b7e344382aa6367447bd52d2"},
|
||||
"yconf": {:hex, :yconf, "1.0.22", "52a435f9b60ab1e13950dfe3f7131ecdd8b3d1ca72c44bf66fc74b4571027124", [:rebar3], [{:fast_yaml, "1.0.39", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "aca83457ceabe70756484b5c87ba7b1955f511d499168687eaeaa7c300e857f1"},
|
||||
}
|
||||
|
||||
+23
-2
@@ -15,7 +15,6 @@
|
||||
{"Access model","Model d'Accés"}.
|
||||
{"Account doesn't exist","El compte no existeix"}.
|
||||
{"Action on user","Acció en l'usuari"}.
|
||||
{"Add a hat to a user","Afegir un barret a un usuari"}.
|
||||
{"Add User","Afegir usuari"}.
|
||||
{"Administration of ","Administració de "}.
|
||||
{"Administration","Administració"}.
|
||||
@@ -49,6 +48,7 @@
|
||||
{"API Commands","Comandaments API"}.
|
||||
{"April","Abril"}.
|
||||
{"Arguments","Arguments"}.
|
||||
{"Assign a hat to a user","Asignar un barret a un usuari"}.
|
||||
{"Attribute 'channel' is required for this request","L'atribut 'channel' és necessari per a aquesta petició"}.
|
||||
{"Attribute 'id' is mandatory for MIX messages","L'atribut 'id' es necessari per a missatges MIX"}.
|
||||
{"Attribute 'jid' is not allowed here","L'atribut 'jid' no està permès ací"}.
|
||||
@@ -94,7 +94,9 @@
|
||||
{"Configuration of room ~s","Configuració de la sala ~s"}.
|
||||
{"Configuration","Configuració"}.
|
||||
{"Contact Addresses (normally, room owner or owners)","Adreces de contacte (normalment, propietaris de la sala)"}.
|
||||
{"Contacts","Contactes"}.
|
||||
{"Country","Pais"}.
|
||||
{"Create a Hat","Crear un barret"}.
|
||||
{"Current Discussion Topic","Assumpte de discussió actual"}.
|
||||
{"Database failure","Error a la base de dades"}.
|
||||
{"Database Tables Configuration at ","Configuració de la base de dades en "}.
|
||||
@@ -106,6 +108,8 @@
|
||||
{"Delete User","Eliminar Usuari"}.
|
||||
{"Deliver event notifications","Entrega de notificacions d'events"}.
|
||||
{"Deliver payloads with event notifications","Enviar payloads junt a les notificacions d'events"}.
|
||||
{"Destroy a Hat","Destruir un barret"}.
|
||||
{"Disable User","Deshabilitar un Usuari"}.
|
||||
{"Disc only copy","Còpia sols en disc"}.
|
||||
{"Don't tell your password to anybody, not even the administrators of the XMPP server.","No li donis la teva contrasenya a ningú, ni tan sols als administradors del servidor XMPP."}.
|
||||
{"Dump Backup to Text File at ","Exporta còpia de seguretat a fitxer de text en "}.
|
||||
@@ -157,12 +161,19 @@
|
||||
{"Full List of Room Admins","Llista completa de administradors de la sala"}.
|
||||
{"Full List of Room Owners","Llista completa de propietaris de la sala"}.
|
||||
{"Full Name","Nom complet"}.
|
||||
{"Get List of Active Users","Obté la llista d'usuaris actius"}.
|
||||
{"Get List of Disabled Users","Obté la llista d'usuaris deshabilitats"}.
|
||||
{"Get List of Idle Users","Obté la llista d'usuaris inactius"}.
|
||||
{"Get List of Online Users","Obté la llista d'usuaris en línia"}.
|
||||
{"Get List of Registered Users","Obté la llista d'usuaris registrats"}.
|
||||
{"Get Number of Active Users","Obtenir Número d'Usuaris Actius"}.
|
||||
{"Get Number of Disabled Users","Obtenir Número d'Usuaris Deshabilitats"}.
|
||||
{"Get Number of Idle Users","Obtenir Número d'Usuaris Inactius"}.
|
||||
{"Get Number of Online Users","Obtenir Número d'Usuaris Connectats"}.
|
||||
{"Get Number of Registered Users","Obtenir Número d'Usuaris Registrats"}.
|
||||
{"Get Pending","Obtenir Pendents"}.
|
||||
{"Get User Last Login Time","Obtenir la última connexió d'Usuari"}.
|
||||
{"Get User Roster","Obtenir llista de contactes de l'usuari"}.
|
||||
{"Get User Statistics","Obtenir Estadístiques d'Usuari"}.
|
||||
{"Given Name","Nom propi"}.
|
||||
{"Grant voice to this person?","Concedir veu a aquesta persona?"}.
|
||||
@@ -171,7 +182,9 @@
|
||||
{"has been kicked because of an affiliation change","ha sigut expulsat a causa d'un canvi d'afiliació"}.
|
||||
{"has been kicked because the room has been changed to members-only","ha sigut expulsat perquè la sala ara és només per a membres"}.
|
||||
{"has been kicked","ha sigut expulsat"}.
|
||||
{"Hash computed from the list of hats available in a room","Hash computat de la llista de barrets disponibles a una sala"}.
|
||||
{"Hash of the vCard-temp avatar of this room","Hash del avatar a vCard-temp d'esta sala"}.
|
||||
{"Hat hue","To de color del barret"}.
|
||||
{"Hat title","Títol del barret"}.
|
||||
{"Hat URI","URI del barret"}.
|
||||
{"Hats limit exceeded","El límit de tràfic ha sigut sobrepassat"}.
|
||||
@@ -225,7 +238,7 @@
|
||||
{"Last year","Últim any"}.
|
||||
{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Els bits menys significants del hash SHA-256 del text deurien ser iguals a l'etiqueta hexadecimal"}.
|
||||
{"leaves the room","surt de la sala"}.
|
||||
{"List of users with hats","Llista d'usuaris amb barrets"}.
|
||||
{"List of Hats","Llista de barrets"}.
|
||||
{"List users with hats","Llista d'usuaris amb barrets"}.
|
||||
{"Logged Out","Desconectat"}.
|
||||
{"Logging","Registre"}.
|
||||
@@ -319,7 +332,10 @@
|
||||
{"Notify subscribers when the node configuration changes","Notificar subscriptors quan canvia la configuració del node"}.
|
||||
{"Notify subscribers when the node is deleted","Notificar subscriptors quan el node és eliminat"}.
|
||||
{"November","Novembre"}.
|
||||
{"Number of active users","Número d'usuaris actius"}.
|
||||
{"Number of answers required","Número de respostes requerides"}.
|
||||
{"Number of disabled users","Número d'Usuaris Deshabilitats"}.
|
||||
{"Number of idle users","Número d'usuaris inactius"}.
|
||||
{"Number of occupants","Número d'ocupants"}.
|
||||
{"Number of Offline Messages","Número de missatges offline"}.
|
||||
{"Number of online users","Número d'usuaris connectats"}.
|
||||
@@ -395,6 +411,7 @@
|
||||
{"Receive notification of new items only","Rebre notificació només de nous elements"}.
|
||||
{"Receive notification of new nodes only","Rebre notificació només de nous nodes"}.
|
||||
{"Recipient is not in the conference room","El receptor no està en la sala de conferència"}.
|
||||
{"Re-Enable User","Rehabilitar usuari"}.
|
||||
{"Register an XMPP account","Registrar un compte XMPP"}.
|
||||
{"Register","Registrar"}.
|
||||
{"Remote copy","Còpia remota"}.
|
||||
@@ -489,6 +506,9 @@
|
||||
{"The JIDs of those to contact with questions","Els JIDs a qui contactar amb preguntes"}.
|
||||
{"The JIDs of those with an affiliation of owner","Els JIDs de qui tenen una afiliació de propietaris"}.
|
||||
{"The JIDs of those with an affiliation of publisher","Els JIDs de qui tenen una afiliació de publicadors"}.
|
||||
{"The list of all active users","La llista de tots els usuaris actius"}.
|
||||
{"The list of all disabled users","La llista de tots els usuaris deshabilitats"}.
|
||||
{"The list of all idle users","La llista de tots els usuaris inactius"}.
|
||||
{"The list of all online users","La llista de tots els usuaris en línia"}.
|
||||
{"The list of all users","La llista de tots els usuaris"}.
|
||||
{"The list of JIDs that may associate leaf nodes with a collection","La llista de JIDs que poden associar nodes fulla amb una col·lecció"}.
|
||||
@@ -510,6 +530,7 @@
|
||||
{"The presence states for which an entity wants to receive notifications","El estats de presencia per als quals una entitat vol rebre notificacions"}.
|
||||
{"The query is only allowed from local users","La petició està permesa només d'usuaris locals"}.
|
||||
{"The query must not contain <item/> elements","La petició no pot contenir elements <item/>"}.
|
||||
{"The role","El rol"}.
|
||||
{"The room subject can be modified by participants","El tema de la sala pot modificar-lo els participants"}.
|
||||
{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","La informació semàntica de les dades al node, usualment especificat pel espai de noms de la càrrega util (si n'hi ha)"}.
|
||||
{"The sender of the last received message","Qui ha enviat l'ultim missatge rebut"}.
|
||||
|
||||
+23
-2
@@ -15,7 +15,6 @@
|
||||
{"Access model","Modelo de Acceso"}.
|
||||
{"Account doesn't exist","La cuenta no existe"}.
|
||||
{"Action on user","Acción en el usuario"}.
|
||||
{"Add a hat to a user","Añade un sombrero a un usuario"}.
|
||||
{"Add User","Añadir usuario"}.
|
||||
{"Administration of ","Administración de "}.
|
||||
{"Administration","Administración"}.
|
||||
@@ -49,6 +48,7 @@
|
||||
{"API Commands","Comandos API"}.
|
||||
{"April","Abril"}.
|
||||
{"Arguments","Argumentos"}.
|
||||
{"Assign a hat to a user","Asigna un sombrero a un usuario"}.
|
||||
{"Attribute 'channel' is required for this request","El atributo 'channel' es necesario para esta petición"}.
|
||||
{"Attribute 'id' is mandatory for MIX messages","El atributo 'id' es necesario para mensajes MIX"}.
|
||||
{"Attribute 'jid' is not allowed here","El atributo 'jid' no está permitido aqui"}.
|
||||
@@ -94,7 +94,9 @@
|
||||
{"Configuration of room ~s","Configuración para la sala ~s"}.
|
||||
{"Configuration","Configuración"}.
|
||||
{"Contact Addresses (normally, room owner or owners)","Direcciones de contacto (normalmente la del dueño o dueños de la sala)"}.
|
||||
{"Contacts","Contactos"}.
|
||||
{"Country","País"}.
|
||||
{"Create a Hat","Crear un sombrero"}.
|
||||
{"Current Discussion Topic","Tema de discusión actual"}.
|
||||
{"Database failure","Error en la base de datos"}.
|
||||
{"Database Tables Configuration at ","Configuración de tablas de la base de datos en "}.
|
||||
@@ -106,6 +108,8 @@
|
||||
{"Delete User","Borrar usuario"}.
|
||||
{"Deliver event notifications","Entregar notificaciones de eventos"}.
|
||||
{"Deliver payloads with event notifications","Enviar contenidos junto con las notificaciones de eventos"}.
|
||||
{"Destroy a Hat","Destruir un sombrero"}.
|
||||
{"Disable User","Deshabilitar un usuario"}.
|
||||
{"Disc only copy","Copia en disco solamente"}.
|
||||
{"Don't tell your password to anybody, not even the administrators of the XMPP server.","No le digas tu contraseña a nadie, ni siquiera a los administradores del servidor XMPP."}.
|
||||
{"Dump Backup to Text File at ","Exporta copia de seguridad a fichero de texto en "}.
|
||||
@@ -157,12 +161,19 @@
|
||||
{"Full List of Room Admins","Lista completa de administradores de la sala"}.
|
||||
{"Full List of Room Owners","Lista completa de dueños de la sala"}.
|
||||
{"Full Name","Nombre completo"}.
|
||||
{"Get List of Active Users","Ver lista de usuarios activos"}.
|
||||
{"Get List of Disabled Users","Ver lista de usuarios deshabilitados"}.
|
||||
{"Get List of Idle Users","Ver lista de usuarios inactivos"}.
|
||||
{"Get List of Online Users","Ver lista de usuarios conectados"}.
|
||||
{"Get List of Registered Users","Ver lista de usuarios registrados"}.
|
||||
{"Get Number of Active Users","Ver número de usuarios activos"}.
|
||||
{"Get Number of Disabled Users","Ver número de usuarios deshabilitados"}.
|
||||
{"Get Number of Idle Users","Ver número de usuarios inactivos"}.
|
||||
{"Get Number of Online Users","Ver número de usuarios conectados"}.
|
||||
{"Get Number of Registered Users","Ver número de usuarios registrados"}.
|
||||
{"Get Pending","Obtener pendientes"}.
|
||||
{"Get User Last Login Time","Ver fecha de la última conexión de usuario"}.
|
||||
{"Get User Roster","Ver lista de contactos del usuario"}.
|
||||
{"Get User Statistics","Ver estadísticas de usuario"}.
|
||||
{"Given Name","Nombre de pila"}.
|
||||
{"Grant voice to this person?","¿Conceder voz a esta persona?"}.
|
||||
@@ -171,7 +182,9 @@
|
||||
{"has been kicked because of an affiliation change","ha sido expulsado por un cambio de su afiliación"}.
|
||||
{"has been kicked because the room has been changed to members-only","ha sido expulsado porque la sala es ahora solo para miembros"}.
|
||||
{"has been kicked","ha sido expulsado"}.
|
||||
{"Hash computed from the list of hats available in a room","Hash computado a partir de la lista de sombreros disponibles en una sala"}.
|
||||
{"Hash of the vCard-temp avatar of this room","Hash del avatar vCard-temp de esta sala"}.
|
||||
{"Hat hue","Tono de color del sombrero"}.
|
||||
{"Hat title","Título del sombrero"}.
|
||||
{"Hat URI","Dirección del sombrero"}.
|
||||
{"Hats limit exceeded","Se ha excedido el límite de sombreros"}.
|
||||
@@ -225,7 +238,7 @@
|
||||
{"Last year","Último año"}.
|
||||
{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Los bits menos significativos del hash SHA-256 del texto deberían ser iguales a la etiqueta hexadecimal"}.
|
||||
{"leaves the room","sale de la sala"}.
|
||||
{"List of users with hats","Lista de usuarios con sombreros"}.
|
||||
{"List of Hats","Lista de sombreros"}.
|
||||
{"List users with hats","Listar usuarios con sombreros"}.
|
||||
{"Logged Out","Desconectad@"}.
|
||||
{"Logging","Histórico de mensajes"}.
|
||||
@@ -319,7 +332,10 @@
|
||||
{"Notify subscribers when the node configuration changes","Notificar subscriptores cuando cambia la configuración del nodo"}.
|
||||
{"Notify subscribers when the node is deleted","Notificar subscriptores cuando el nodo se borra"}.
|
||||
{"November","Noviembre"}.
|
||||
{"Number of active users","Número de usuarios activos"}.
|
||||
{"Number of answers required","Número de respuestas necesarias"}.
|
||||
{"Number of disabled users","Número de usuarios deshabilitados"}.
|
||||
{"Number of idle users","Número de usuarios inactivos"}.
|
||||
{"Number of occupants","Número de ocupantes"}.
|
||||
{"Number of Offline Messages","Número de mensajes diferidos"}.
|
||||
{"Number of online users","Número de usuarios conectados"}.
|
||||
@@ -395,6 +411,7 @@
|
||||
{"Receive notification of new items only","Recibir notificaciones solo de nuevos elementos"}.
|
||||
{"Receive notification of new nodes only","Recibir notificaciones solo de nuevos nodos"}.
|
||||
{"Recipient is not in the conference room","El receptor no está en la sala de conferencia"}.
|
||||
{"Re-Enable User","Rehabilitar usuario"}.
|
||||
{"Register an XMPP account","Registrar una cuenta XMPP"}.
|
||||
{"Register","Registrar"}.
|
||||
{"Remote copy","Copia remota"}.
|
||||
@@ -489,6 +506,9 @@
|
||||
{"The JIDs of those to contact with questions","Los JIDs a quienes contactar con preguntas"}.
|
||||
{"The JIDs of those with an affiliation of owner","Los JIDs de quienes tienen una afiliación de dueños"}.
|
||||
{"The JIDs of those with an affiliation of publisher","Los JIDs de quienes tienen una afiliación de publicadores"}.
|
||||
{"The list of all active users","La lista de todos los usuarios activos"}.
|
||||
{"The list of all disabled users","La lista de todos los usuarios deshabilitados"}.
|
||||
{"The list of all idle users","La lista de todos los usuarios inactivos"}.
|
||||
{"The list of all online users","La lista de todos los usuarios conectados"}.
|
||||
{"The list of all users","La lista de todos los usuarios"}.
|
||||
{"The list of JIDs that may associate leaf nodes with a collection","La lista de JIDs que pueden asociar nodos hijo con una colección"}.
|
||||
@@ -510,6 +530,7 @@
|
||||
{"The presence states for which an entity wants to receive notifications","Los estados de presencia para los cuales una entidad quiere recibir notificaciones"}.
|
||||
{"The query is only allowed from local users","La solicitud está permitida solo para usuarios locales"}.
|
||||
{"The query must not contain <item/> elements","La solicitud no debe contener elementos <item/>"}.
|
||||
{"The role","El rol"}.
|
||||
{"The room subject can be modified by participants","El asunto de la sala puede ser modificado por los participantes"}.
|
||||
{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","La información semántica de los datos del nodo, normalmente es especificada por el espacio de los nombres de la carga útil (si existe)"}.
|
||||
{"The sender of the last received message","El emisor del último mensaje recibido"}.
|
||||
|
||||
+192
-5
@@ -5,11 +5,14 @@
|
||||
|
||||
{" (Add * to the end of field to match substring)"," (Добавьте * в конец поля для поиска подстроки)"}.
|
||||
{" has set the subject to: "," установил(а) тему: "}.
|
||||
{"# participants","# участников"}.
|
||||
{"A description of the node","Описание узла"}.
|
||||
{"A friendly name for the node","Легко запоминаемое имя для узла"}.
|
||||
{"A password is required to enter this room","Чтобы войти в эту конференцию, нужен пароль"}.
|
||||
{"A Web Page","Веб-страница"}.
|
||||
{"Accept","Принять"}.
|
||||
{"Access denied by service policy","Доступ запрещён политикой службы"}.
|
||||
{"Access model","Правила доступа"}.
|
||||
{"Account doesn't exist","Учётная запись не существует"}.
|
||||
{"Action on user","Действие над пользователем"}.
|
||||
{"Add User","Добавить пользователя"}.
|
||||
@@ -18,6 +21,7 @@
|
||||
{"Administrator privileges required","Требуются права администратора"}.
|
||||
{"All activity","Вся статистика"}.
|
||||
{"All Users","Все пользователи"}.
|
||||
{"Allow subscription","Разрешить подписку"}.
|
||||
{"Allow this Jabber ID to subscribe to this pubsub node?","Разрешить этому Jabber ID подписаться на данный узел?"}.
|
||||
{"Allow this person to register with the room?","Разрешить пользователю зарегистрироваться в комнате?"}.
|
||||
{"Allow users to change the subject","Разрешить пользователям изменять тему"}.
|
||||
@@ -28,12 +32,27 @@
|
||||
{"Allow visitors to send private messages to","Разрешить посетителям посылать приватные сообщения"}.
|
||||
{"Allow visitors to send status text in presence updates","Разрешить посетителям вставлять текcт статуса в сообщения о присутствии"}.
|
||||
{"Allow visitors to send voice requests","Разрешить посетителям запрашивать право голоса"}.
|
||||
{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Связанная группа LDAP, которая определяет членство в комнате; это должно быть уникальное имя LDAP в соответствии с определением группы, зависящим от реализации или развертывания."}.
|
||||
{"Announcements","Объявления"}.
|
||||
{"Answer associated with a picture","Ответ, связанный с изображением"}.
|
||||
{"Answer associated with a video","Ответ, связанный с видео"}.
|
||||
{"Answer associated with speech","Ответ, связанный с речью"}.
|
||||
{"Answer to a question","Ответ на вопрос"}.
|
||||
{"Anyone in the specified roster group(s) may subscribe and retrieve items","Любой из указанных групп списка может подписываться и получать элементы"}.
|
||||
{"Anyone may associate leaf nodes with the collection","Любой может связывать конечные узлы с коллекцией"}.
|
||||
{"Anyone may publish","Любой может публиковать"}.
|
||||
{"Anyone may subscribe and retrieve items","Любой может подписываться и получать элементы"}.
|
||||
{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Любой с подпиской присутствия (или с) может подписываться и получать элементы"}.
|
||||
{"Anyone with Voice","Любой с голосовым доступом"}.
|
||||
{"Anyone","Любой"}.
|
||||
{"API Commands","Команды API"}.
|
||||
{"April","апреля"}.
|
||||
{"Arguments","Аргументы"}.
|
||||
{"Attribute 'channel' is required for this request","Атрибут 'channel' является обязательным для этого запроса"}.
|
||||
{"Attribute 'id' is mandatory for MIX messages","Атрибут 'id' является обязательным для MIX сообщений"}.
|
||||
{"Attribute 'jid' is not allowed here","Атрибут 'jid' здесь недопустим"}.
|
||||
{"Attribute 'node' is not allowed here","Атрибут 'node' здесь недопустим"}.
|
||||
{"Attribute 'to' of stanza that triggered challenge","Атрибут «кому» строфы, запросившей проверку"}.
|
||||
{"August","августа"}.
|
||||
{"Automatic node creation is not enabled","Автоматическое создание узлов недоступно"}.
|
||||
{"Backup Management","Управление резервным копированием"}.
|
||||
@@ -47,12 +66,14 @@
|
||||
{"Cannot remove active list","Невозможно удалить активный список"}.
|
||||
{"Cannot remove default list","Невозможно удалить список по умолчанию"}.
|
||||
{"CAPTCHA web page","Ссылка на капчу"}.
|
||||
{"Challenge ID","ИД проверки"}.
|
||||
{"Change Password","Сменить пароль"}.
|
||||
{"Change User Password","Изменить пароль пользователя"}.
|
||||
{"Changing password is not allowed","Изменение пароля не разрешено"}.
|
||||
{"Changing role/affiliation is not allowed","Изменение роли/ранга не разрешено"}.
|
||||
{"Channel already exists","Канал уже существует"}.
|
||||
{"Channel does not exist","Канал не существует"}.
|
||||
{"Channel JID","JID канала"}.
|
||||
{"Channels","Каналы"}.
|
||||
{"Characters not allowed:","Недопустимые символы:"}.
|
||||
{"Chatroom configuration modified","Конфигурация комнаты изменилась"}.
|
||||
@@ -66,11 +87,14 @@
|
||||
{"Choose whether to approve this entity's subscription.","Решите: предоставить ли подписку этому объекту."}.
|
||||
{"City","Город"}.
|
||||
{"Client acknowledged more stanzas than sent by server","Клиент подтвердил больше сообщений чем было отправлено сервером"}.
|
||||
{"Clustering","Кластеризация"}.
|
||||
{"Commands","Команды"}.
|
||||
{"Conference room does not exist","Конференция не существует"}.
|
||||
{"Configuration of room ~s","Конфигурация комнаты ~s"}.
|
||||
{"Configuration","Конфигурация"}.
|
||||
{"Contact Addresses (normally, room owner or owners)","Контактные адреса (обычно владелец или владельцы комнаты)"}.
|
||||
{"Country","Страна"}.
|
||||
{"Current Discussion Topic","Текущая тема обсуждения"}.
|
||||
{"Database failure","Ошибка базы данных"}.
|
||||
{"Database Tables Configuration at ","Конфигурация таблиц базы данных на "}.
|
||||
{"Database","База данных"}.
|
||||
@@ -82,10 +106,14 @@
|
||||
{"Deliver event notifications","Доставлять уведомления о событиях"}.
|
||||
{"Deliver payloads with event notifications","Доставлять вместе с уведомлениями o публикациях сами публикации"}.
|
||||
{"Disc only copy","только диск"}.
|
||||
{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Не говорите никому свой пароль, даже администраторам сервера."}.
|
||||
{"Dump Backup to Text File at ","Копирование в текстовый файл на "}.
|
||||
{"Dump to Text File","Копирование в текстовый файл"}.
|
||||
{"Duplicated groups are not allowed by RFC6121","Группы с одинаковыми названиями запрещены стандартом RFC6121"}.
|
||||
{"Dynamically specify a replyto of the item publisher","Динамическое указание кому ответить (replyto) издателя элемента"}.
|
||||
{"Edit Properties","Изменить параметры"}.
|
||||
{"Either approve or decline the voice request.","Подтвердите или отклоните право голоса."}.
|
||||
{"ejabberd HTTP Upload service","Служба закачки файлов по HTTP ejabberd"}.
|
||||
{"ejabberd MUC module","ejabberd MUC модуль"}.
|
||||
{"ejabberd Multicast service","ejabberd Multicast сервис"}.
|
||||
{"ejabberd Publish-Subscribe module","Модуль ejabberd Публикации-Подписки"}.
|
||||
@@ -93,7 +121,9 @@
|
||||
{"ejabberd vCard module","ejabberd vCard модуль"}.
|
||||
{"ejabberd Web Admin","Web-интерфейс администрирования ejabberd"}.
|
||||
{"ejabberd","ejabberd"}.
|
||||
{"Email Address","Адрес электронной почты"}.
|
||||
{"Email","Электронная почта"}.
|
||||
{"Enable hats","Включить шляпы"}.
|
||||
{"Enable logging","Включить журналирование"}.
|
||||
{"Enable message archiving","Включить хранение сообщений"}.
|
||||
{"Enabling push without 'node' attribute is not supported","Включение push-режима без атрибута 'node' не поддерживается"}.
|
||||
@@ -104,6 +134,7 @@
|
||||
{"Enter path to jabberd14 spool file","Введите путь к файлу из спула jabberd14"}.
|
||||
{"Enter path to text file","Введите путь к текстовому файлу"}.
|
||||
{"Enter the text you see","Введите увиденный текст"}.
|
||||
{"Erlang XMPP Server","Сервер XMPP на Erlang"}.
|
||||
{"Exclude Jabber IDs from CAPTCHA challenge","Исключить показ капчи для списка Jabber ID"}.
|
||||
{"Export all tables as SQL queries to a file:","Экспортировать все таблицы в виде SQL запросов в файл:"}.
|
||||
{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Экспорт данных всех пользователей сервера в файлы формата PIEFXIS (XEP-0227):"}.
|
||||
@@ -116,10 +147,17 @@
|
||||
{"Failed to parse HTTP response","Ошибка разбора HTTP ответа"}.
|
||||
{"Failed to process option '~s'","Ошибка обработки опции '~s'"}.
|
||||
{"Family Name","Фамилия"}.
|
||||
{"FAQ Entry","Часто задаваемые вопросы"}.
|
||||
{"February","февраля"}.
|
||||
{"File larger than ~w bytes","Файл больше ~w байт"}.
|
||||
{"Fill in the form to search for any matching XMPP User","Заполните форму для поиска совпадающих пользователей XMPP"}.
|
||||
{"Friday","Пятница"}.
|
||||
{"From ~ts","От ~ts"}.
|
||||
{"Full List of Room Admins","Полный список админов комнаты"}.
|
||||
{"Full List of Room Owners","Полный список владельцев комнаты"}.
|
||||
{"Full Name","Полное имя"}.
|
||||
{"Get List of Online Users","Получить список подключённых пользователей"}.
|
||||
{"Get List of Registered Users","Получить список зарегистрированных пользователей"}.
|
||||
{"Get Number of Online Users","Получить количество подключённых пользователей"}.
|
||||
{"Get Number of Registered Users","Получить количество зарегистрированных пользователей"}.
|
||||
{"Get Pending","Получить отложенные"}.
|
||||
@@ -132,6 +170,9 @@
|
||||
{"has been kicked because of an affiliation change","выгнали из комнаты вследствие смены ранга"}.
|
||||
{"has been kicked because the room has been changed to members-only","выгнали из комнаты потому что она стала только для членов"}.
|
||||
{"has been kicked","выгнали из комнаты"}.
|
||||
{"Hash of the vCard-temp avatar of this room","Хэш временного аватара vCard этой комнаты"}.
|
||||
{"Hat URI","URI шляпы"}.
|
||||
{"Hats limit exceeded","Превышено ограничение шляп"}.
|
||||
{"Host unknown","Неизвестное имя сервера"}.
|
||||
{"HTTP File Upload","Передача файлов по HTTP"}.
|
||||
{"Idle connection","Неиспользуемое соединение"}.
|
||||
@@ -152,6 +193,8 @@
|
||||
{"Incorrect value of 'action' attribute","Некорректное значение атрибута 'action'"}.
|
||||
{"Incorrect value of 'action' in data form","Некорректное значение 'action' в форме данных"}.
|
||||
{"Incorrect value of 'path' in data form","Некорректное значение 'path' в форме данных"}.
|
||||
{"Installed Modules:","Установленные модули:"}.
|
||||
{"Install","Установить"}.
|
||||
{"Insufficient privilege","Недостаточно прав"}.
|
||||
{"Internal server error","Внутренняя ошибка сервера"}.
|
||||
{"Invalid 'from' attribute in forwarded message","Некорректный атрибут 'from' в пересланном сообщении"}.
|
||||
@@ -165,14 +208,24 @@
|
||||
{"It is not allowed to send private messages to the conference","Не разрешается посылать частные сообщения прямо в конференцию"}.
|
||||
{"Jabber ID","Jabber ID"}.
|
||||
{"January","января"}.
|
||||
{"JID normalization denied by service policy","Нормализация JID запрещена правилами службы"}.
|
||||
{"JID normalization failed","Ошибка нормализации JID"}.
|
||||
{"Joined MIX channels of ~ts","Присоединился к MIX каналам ~ts"}.
|
||||
{"Joined MIX channels:","Присоединился к MIX каналам:"}.
|
||||
{"joins the room","вошёл(а) в комнату"}.
|
||||
{"July","июля"}.
|
||||
{"June","июня"}.
|
||||
{"Just created","Только что создано"}.
|
||||
{"Last Activity","Последнее подключение"}.
|
||||
{"Last login","Время последнего подключения"}.
|
||||
{"Last message","Последнее сообщение"}.
|
||||
{"Last month","За последний месяц"}.
|
||||
{"Last year","За последний год"}.
|
||||
{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Младшие значимые биты хэша SHA-256 текста должны быть равны шестнадцатеричной метке"}.
|
||||
{"leaves the room","вышел(а) из комнаты"}.
|
||||
{"List users with hats","Список пользователей с шляпами"}.
|
||||
{"Logged Out","Вышел"}.
|
||||
{"Logging","Ведение журнала"}.
|
||||
{"Make participants list public","Сделать список участников видимым всем"}.
|
||||
{"Make room CAPTCHA protected","Сделать комнату защищённой капчей"}.
|
||||
{"Make room members-only","Комната только для зарегистрированных участников"}.
|
||||
@@ -183,21 +236,34 @@
|
||||
{"Malformed username","Недопустимое имя пользователя"}.
|
||||
{"MAM preference modification denied by service policy","Изменение настроек архива сообщений запрещено политикой службы"}.
|
||||
{"March","марта"}.
|
||||
{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Максимальное количество сохраняемых элементов или `max` для отсутствия ограничений, кроме установленных сервером ограничений"}.
|
||||
{"Max payload size in bytes","Максимальный размер полезной нагрузки в байтах"}.
|
||||
{"Maximum file size","Максимальный размер файла"}.
|
||||
{"Maximum Number of History Messages Returned by Room","Максимальное количество сообщений истории, возвращаемых комнатой"}.
|
||||
{"Maximum number of items to persist","Максимальное число сохраняемых публикаций"}.
|
||||
{"Maximum Number of Occupants","Максимальное количество участников"}.
|
||||
{"May","мая"}.
|
||||
{"Membership is required to enter this room","В эту конференцию могут входить только её члены"}.
|
||||
{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Запомните пароль или запишите его на бумаге, которую сохраните в безопасном месте. В XMPP'е нет автоматизированного средства восстановления пароля в том случае, если вы его забудете."}.
|
||||
{"Mere Availability in XMPP (No Show Value)","Простая доступность в XMPP (значение «No Show»)"}.
|
||||
{"Message body","Тело сообщения"}.
|
||||
{"Message not found in forwarded payload","Сообщение не найдено в пересылаемом вложении"}.
|
||||
{"Messages from strangers are rejected","Сообщения от незнакомцев запрещены"}.
|
||||
{"Messages of type headline","Сообщения типа «заголовок»"}.
|
||||
{"Messages of type normal","Сообщения типа «обычный»"}.
|
||||
{"Middle Name","Отчество"}.
|
||||
{"Minimum interval between voice requests (in seconds)","Минимальный интервал между запросами на право голоса"}.
|
||||
{"Moderator privileges required","Требуются права модератора"}.
|
||||
{"Moderators Only","Только модераторам"}.
|
||||
{"Moderator","Модератор"}.
|
||||
{"Module failed to handle the query","Ошибка модуля при обработке запроса"}.
|
||||
{"Monday","Понедельник"}.
|
||||
{"Multicast","Мультикаст"}.
|
||||
{"Multiple <item/> elements are not allowed by RFC6121","Множественные элементы <item/> запрещены стандартом RFC6121"}.
|
||||
{"Multi-User Chat","Конференция"}.
|
||||
{"Name","Название"}.
|
||||
{"Natural Language for Room Discussions","Естественный язык для обсуждений комнаты"}.
|
||||
{"Natural-Language Room Name","Имя комнаты на естественном языке"}.
|
||||
{"Neither 'jid' nor 'nick' attribute found","Не найден атрибут 'jid' или 'nick'"}.
|
||||
{"Neither 'role' nor 'affiliation' attribute found","Не найден атрибут 'role' или 'affiliation'"}.
|
||||
{"Never","Никогда"}.
|
||||
@@ -233,6 +299,7 @@
|
||||
{"No services available","Нет доступных сервисов"}.
|
||||
{"No statistics found for this item","Не найдено статистики для этого элемента"}.
|
||||
{"No 'to' attribute found in the invitation","Не найден атрибут 'to' в этом приглашении"}.
|
||||
{"Nobody","Никто"}.
|
||||
{"Node already exists","Узел уже существует"}.
|
||||
{"Node ID","ID узла"}.
|
||||
{"Node index not found","Индекс узла не найден"}.
|
||||
@@ -240,6 +307,7 @@
|
||||
{"Node ~p","Узел ~p"}.
|
||||
{"Nodeprep has failed","Ошибка применения профиля Nodeprep"}.
|
||||
{"Nodes","Узлы"}.
|
||||
{"Node","Узел"}.
|
||||
{"None","Нет"}.
|
||||
{"Not allowed","Недопустимо"}.
|
||||
{"Not Found","Не Найдено"}.
|
||||
@@ -248,42 +316,59 @@
|
||||
{"Notify subscribers when the node configuration changes","Уведомлять подписчиков об изменении конфигурации сборника"}.
|
||||
{"Notify subscribers when the node is deleted","Уведомлять подписчиков об удалении сборника"}.
|
||||
{"November","ноября"}.
|
||||
{"Number of answers required","Количество требуемых ответов"}.
|
||||
{"Number of occupants","Число присутствующих"}.
|
||||
{"Number of Offline Messages","Количество недоставленных сообщений"}.
|
||||
{"Number of online users","Количество подключённых пользователей"}.
|
||||
{"Number of registered users","Количество зарегистрированных пользователей"}.
|
||||
{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Количество секунд, по истечении которых элементы автоматически удаляются, или `max` для отсутствия ограничений, кроме установленных сервером ограничений"}.
|
||||
{"Occupants are allowed to invite others","Участникам разрешается приглашать других"}.
|
||||
{"Occupants are allowed to query others","Участникам разрешено видеть друг друга"}.
|
||||
{"Occupants May Change the Subject","Разрешить участникам изменять тему"}.
|
||||
{"October","октября"}.
|
||||
{"OK","Продолжить"}.
|
||||
{"Old Password:","Старый пароль:"}.
|
||||
{"Online Users","Подключённые пользователи"}.
|
||||
{"Online","Подключён"}.
|
||||
{"Only collection node owners may associate leaf nodes with the collection","Только владельцы узлов коллекции могут связывать конечные узлы с коллекцией"}.
|
||||
{"Only deliver notifications to available users","Доставлять уведомления только доступным пользователям"}.
|
||||
{"Only <enable/> or <disable/> tags are allowed","Допустимы только тэги <enable/> или <disable/>"}.
|
||||
{"Only <list/> element is allowed in this query","Только элемент <list/> допустим в этом запросе"}.
|
||||
{"Only members may query archives of this room","Только члены могут запрашивать архивы этой комнаты"}.
|
||||
{"Only moderators and participants are allowed to change the subject in this room","Только модераторы и участники могут изменять тему в этой комнате"}.
|
||||
{"Only moderators are allowed to change the subject in this room","Только модераторы могут изменять тему в этой комнате"}.
|
||||
{"Only moderators are allowed to retract messages","Только модераторы могут удалять сообщения"}.
|
||||
{"Only moderators can approve voice requests","Только модераторы могут утверждать запросы на право голоса"}.
|
||||
{"Only occupants are allowed to send messages to the conference","Только присутствующим разрешается посылать сообщения в конференцию"}.
|
||||
{"Only occupants are allowed to send queries to the conference","Только присутствующим разрешается посылать запросы в конференцию"}.
|
||||
{"Only publishers may publish","Только издатели могут публиковать"}.
|
||||
{"Only service administrators are allowed to send service messages","Только администратор службы может посылать служебные сообщения"}.
|
||||
{"Only those on a whitelist may associate leaf nodes with the collection","Только участники из белого списка могут связывать конечные узлы с коллекцией"}.
|
||||
{"Only those on a whitelist may subscribe and retrieve items","Только участники из белого списка могут подписываться и получать элементы"}.
|
||||
{"Organization Name","Название организации"}.
|
||||
{"Organization Unit","Отдел организации"}.
|
||||
{"Other Modules Available:","Другие доступные модули:"}.
|
||||
{"Outgoing s2s Connections","Исходящие s2s-соединения"}.
|
||||
{"Owner privileges required","Требуются права владельца"}.
|
||||
{"Packet relay is denied by service policy","Пересылка пакетов запрещена политикой службы"}.
|
||||
{"Participant ID","ИД участника"}.
|
||||
{"Participant","Участник"}.
|
||||
{"Password Verification","Проверка пароля"}.
|
||||
{"Password Verification:","Проверка пароля:"}.
|
||||
{"Password","Пароль"}.
|
||||
{"Password:","Пароль:"}.
|
||||
{"Path to Dir","Путь к директории"}.
|
||||
{"Path to File","Путь к файлу"}.
|
||||
{"Period: ","Период"}.
|
||||
{"Payload semantic type information","Информация о семантическом типе полезной нагрузки"}.
|
||||
{"Period: ","Период: "}.
|
||||
{"Persist items to storage","Сохранять публикации в хранилище"}.
|
||||
{"Persistent","Постоянно"}.
|
||||
{"Ping query is incorrect","Некорректный пинг-запрос"}.
|
||||
{"Ping","Пинг"}.
|
||||
{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Заметьте, что здесь производится резервное копирование только встроенной базы данных Mnesia. Если Вы также используете другое хранилище данных (например с помощью модуля ODBC), то его резервное копирование следует осуществлять отдельно."}.
|
||||
{"Please, wait for a while before sending new voice request","Пожалуйста, подождите перед тем как подать новый запрос на право голоса"}.
|
||||
{"Pong","Понг"}.
|
||||
{"Possessing 'ask' attribute is not allowed by RFC6121","Наявность атрибута 'ask' запрещено стандартом RFC6121"}.
|
||||
{"Present real Jabber IDs to","Сделать реальные Jabber ID участников видимыми"}.
|
||||
{"Previous session not found","Предыдущая сессия не найдена"}.
|
||||
{"Previous session PID has been killed","Предыдущая сессия была убита"}.
|
||||
@@ -291,6 +376,8 @@
|
||||
{"Previous session PID is dead","Предыдущая сессия мертва"}.
|
||||
{"Previous session timed out","Предыдущая сессия не отвечает"}.
|
||||
{"private, ","приватная, "}.
|
||||
{"Public","Публичная"}.
|
||||
{"Publish model","Модель публикации"}.
|
||||
{"Publish-Subscribe","Публикация-Подписка"}.
|
||||
{"PubSub subscriber request","Запрос подписчика PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Очищать все записи автора публикации когда он отключается"}.
|
||||
@@ -300,12 +387,20 @@
|
||||
{"RAM and disc copy","ОЗУ и диск"}.
|
||||
{"RAM copy","ОЗУ"}.
|
||||
{"Really delete message of the day?","Действительно удалить сообщение дня?"}.
|
||||
{"Receive notification from all descendent nodes","Получать уведомления от всех дочерних узлов"}.
|
||||
{"Receive notification from direct child nodes only","Получать уведомления только от прямых дочерних узлов"}.
|
||||
{"Receive notification of new items only","Получать уведомления только о новых элементах"}.
|
||||
{"Receive notification of new nodes only","Получать уведомления только о новых узлах"}.
|
||||
{"Recipient is not in the conference room","Адресата нет в конференции"}.
|
||||
{"Register an XMPP account","Зарегистрировать учётную запись XMPP"}.
|
||||
{"Register","Зарегистрировать"}.
|
||||
{"Remote copy","не хранится локально"}.
|
||||
{"Remove a hat from a user","Снять шляпу с Пользователь"}.
|
||||
{"Remove User","Удалить пользователя"}.
|
||||
{"Replaced by new connection","Заменено новым соединением"}.
|
||||
{"Request has timed out","Истекло время ожидания запроса"}.
|
||||
{"Request is ignored","Запрос игнорируется"}.
|
||||
{"Requested role","Запрошенная роль"}.
|
||||
{"Resources","Ресурсы"}.
|
||||
{"Restart Service","Перезапустить службу"}.
|
||||
{"Restore Backup from File at ","Восстановление из резервной копии на "}.
|
||||
@@ -313,6 +408,10 @@
|
||||
{"Restore binary backup immediately:","Восстановить из бинарной резервной копии немедленно:"}.
|
||||
{"Restore plain text backup immediately:","Восстановить из текстовой резервной копии немедленно:"}.
|
||||
{"Restore","Восстановление из резервной копии"}.
|
||||
{"Result","Результат"}.
|
||||
{"Roles and Affiliations that May Retrieve Member List","Роли которые могут получать список участников"}.
|
||||
{"Roles for which Presence is Broadcasted","Роли которые видят присутствие"}.
|
||||
{"Roles that May Send Private Messages","Роли которым можно посылать личные сообщения"}.
|
||||
{"Room Configuration","Конфигурация комнаты"}.
|
||||
{"Room creation is denied by service policy","Cоздавать конференцию запрещено политикой службы"}.
|
||||
{"Room description","Описание комнаты"}.
|
||||
@@ -322,8 +421,12 @@
|
||||
{"Roster groups allowed to subscribe","Группы списка контактов, которым разрешена подписка"}.
|
||||
{"Roster size","Размер списка контактов"}.
|
||||
{"Running Nodes","Работающие узлы"}.
|
||||
{"~s invites you to the room ~s","~s приглашает вас в комнату ~s"}.
|
||||
{"Saturday","Суббота"}.
|
||||
{"Search from the date","Поиск с даты"}.
|
||||
{"Search Results for ","Результаты поиска в "}.
|
||||
{"Search the text","Поиск по тексту"}.
|
||||
{"Search until the date","Поиск до даты"}.
|
||||
{"Search users in ","Поиск пользователей в "}.
|
||||
{"Send announcement to all online users on all hosts","Разослать объявление всем подключённым пользователям на всех виртуальных серверах"}.
|
||||
{"Send announcement to all online users","Разослать объявление всем подключённым пользователям"}.
|
||||
@@ -331,17 +434,24 @@
|
||||
{"Send announcement to all users","Разослать объявление всем пользователям"}.
|
||||
{"September","сентября"}.
|
||||
{"Server:","Сервер:"}.
|
||||
{"Service list retrieval timed out","Истекло время ожидания запроса списка служб"}.
|
||||
{"Session state copying timed out","Таймаут копирования состояния сессии"}.
|
||||
{"Set message of the day and send to online users","Установить сообщение дня и разослать его подключённым пользователям"}.
|
||||
{"Set message of the day on all hosts and send to online users","Установить сообщение дня на всех виртуальных серверах и разослать его подключённым пользователям"}.
|
||||
{"Shared Roster Groups","Группы общих контактов"}.
|
||||
{"Show Integral Table","Показать интегральную таблицу"}.
|
||||
{"Show Occupants Join/Leave","Показать присоединение/выход участников"}.
|
||||
{"Show Ordinary Table","Показать обычную таблицу"}.
|
||||
{"Shut Down Service","Остановить службу"}.
|
||||
{"SOCKS5 Bytestreams","Передача файлов через SOCKS5"}.
|
||||
{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Некоторые XMPP-клиенты могут сохранять пароль на вашем компьютере, поэтому делайте это только на личном компьютере."}.
|
||||
{"Sources Specs:","Характеристики источников:"}.
|
||||
{"Specify the access model","Укажите механизм управления доступом"}.
|
||||
{"Specify the event message type","Укажите тип сообщения о событии"}.
|
||||
{"Specify the publisher model","Условия публикации"}.
|
||||
{"Stanza id is not valid","Строфа неправильная"}.
|
||||
{"Stanza ID","ИД строфы"}.
|
||||
{"Statically specify a replyto of the node owner(s)","Статически указать кому отчечать (replyto) владельца(ев) узла"}.
|
||||
{"Stopped Nodes","Остановленные узлы"}.
|
||||
{"Store binary backup:","Сохранить бинарную резервную копию:"}.
|
||||
{"Store plain text backup:","Сохранить текстовую резервную копию:"}.
|
||||
@@ -350,32 +460,73 @@
|
||||
{"Subject","Тема"}.
|
||||
{"Submitted","Отправлено"}.
|
||||
{"Subscriber Address","Адрес подписчика"}.
|
||||
{"Subscribers may publish","Подписчики могут публиковать"}.
|
||||
{"Subscription requests must be approved and only subscribers may retrieve items","Запросы на подписку должны быть одобрены, и только подписчики могут получать элементы"}.
|
||||
{"Subscriptions are not allowed","Подписки недопустимы"}.
|
||||
{"Sunday","Воскресенье"}.
|
||||
{"Text associated with a picture","Текст, связанный с изображением"}.
|
||||
{"Text associated with a sound","Текст, связанный со звуком"}.
|
||||
{"Text associated with a video","Текст, связанный с видео"}.
|
||||
{"Text associated with speech","Текст, связанный с речью"}.
|
||||
{"That nickname is already in use by another occupant","Этот псевдоним уже занят другим участником"}.
|
||||
{"That nickname is registered by another person","Этот псевдоним зарегистрирован кем-то другим"}.
|
||||
{"The account already exists","Учётная запись уже существует"}.
|
||||
{"The account was not unregistered","Учётная запись не была удалёна"}.
|
||||
{"The body text of the last received message","Текст последнего полученного сообщения"}.
|
||||
{"The CAPTCHA is valid.","Проверка капчи прошла успешно."}.
|
||||
{"The CAPTCHA verification has failed","Проверка капчи не пройдена"}.
|
||||
{"The captcha you entered is wrong","Неправильно введённое значение капчи"}.
|
||||
{"The child nodes (leaf or collection) associated with a collection","Дочерние узлы (листья или коллекция), связанные с коллекцией"}.
|
||||
{"The collections with which a node is affiliated","Имя коллекции, в которую входит узел"}.
|
||||
{"The DateTime at which a leased subscription will end or has ended","Дата и время окончания арендованной подписки"}.
|
||||
{"The datetime when the node was created","Дата и время создания узла"}.
|
||||
{"The default language of the node","Язык узла по умолчанию"}.
|
||||
{"The feature requested is not supported by the conference","Запрашиваемое свойство не поддерживается этой конференцией"}.
|
||||
{"The JID of the node creator","JID создателя узла"}.
|
||||
{"The JIDs of those to contact with questions","JID тех, к кому можно обратиться с вопросами"}.
|
||||
{"The JIDs of those with an affiliation of owner","JID тех, кто связан с владельцем"}.
|
||||
{"The JIDs of those with an affiliation of publisher","JID тех, кто связан с издателем"}.
|
||||
{"The list of all online users","Список всех подключённых пользователей"}.
|
||||
{"The list of all users","Список всех пользователей"}.
|
||||
{"The list of JIDs that may associate leaf nodes with a collection","Список JID, которые могут связывать листовые узлы с коллекцией"}.
|
||||
{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Максимальное количество дочерних узлов, которые можно связать с коллекцией, или `max` для отсутствия ограничений, кроме установленных сервером"}.
|
||||
{"The minimum number of milliseconds between sending any two notification digests","Минимальное время в миллисекундах между отправкой любых двух сводок уведомлений"}.
|
||||
{"The name of the node","Название узла"}.
|
||||
{"The node is a collection node","Узел является узлом коллекции"}.
|
||||
{"The node is a leaf node (default)","Узел является листовым узлом (по умолчанию)"}.
|
||||
{"The NodeID of the relevant node","NodeID соответствующего узла"}.
|
||||
{"The number of pending incoming presence subscription requests","Количество ожидающих входящих запросов на подписку на уведомления о присутствии"}.
|
||||
{"The number of subscribers to the node","Количество подписчиков на узел"}.
|
||||
{"The number of unread or undelivered messages","Количество непрочитанных или недоставленных сообщений"}.
|
||||
{"The password contains unacceptable characters","Пароль содержит недопустимые символы"}.
|
||||
{"The password is too weak","Слишком слабый пароль"}.
|
||||
{"the password is","пароль:"}.
|
||||
{"the password is","пароль это"}.
|
||||
{"The password of your XMPP account was successfully changed.","Пароль вашей учётной записи XMPP был успешно изменён."}.
|
||||
{"The password was not changed","Пароль не был изменён"}.
|
||||
{"The passwords are different","Пароли не совпадают"}.
|
||||
{"The presence states for which an entity wants to receive notifications","Состояния присутствия, для которых сущность хочет получать уведомления"}.
|
||||
{"The query is only allowed from local users","Запрос доступен только для локальных пользователей"}.
|
||||
{"The query must not contain <item/> elements","Запрос не должен содержать элементов <item/>"}.
|
||||
{"The room subject can be modified by participants","Тема комнаты может быть изменена участниками"}.
|
||||
{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Семантическая информация о типе данных в узле, обычно определяемая пространством имён нагрузки (если таковое имеется)"}.
|
||||
{"The sender of the last received message","Отправитель последнего полученного сообщения"}.
|
||||
{"The stanza MUST contain only one <active/> element, one <default/> element, or one <list/> element","Строфа может содержать только один элемент <active/>, один элемент <default/> или один элемент <list/>"}.
|
||||
{"There was an error creating the account: ","Ошибка при создании аккаунта:"}.
|
||||
{"There was an error deleting the account: ","Ошибка при удалении аккаунта:"}.
|
||||
{"The subscription identifier associated with the subscription request","Идентификатор подписки, связанный с запросом на подписку"}.
|
||||
{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL XSL-преобразования, которое можно применить к полезным данным для создания соответствующего элемента тела сообщения."}.
|
||||
{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL-адрес XSL-преобразования, которое можно применить к формату текста для генерации правильные формы данных, которые клиент может отобразить с помощью универсального механизма отображения Форм данных"}.
|
||||
{"There was an error changing the password: ","Ошибка при изменении пароля: "}.
|
||||
{"There was an error creating the account: ","Ошибка при создании учётной записи: "}.
|
||||
{"There was an error deleting the account: ","Ошибка при удалении учётной записи: "}.
|
||||
{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Регистр не имеет значения: \"маша\" и \"МАША\" будет считаться одним и тем же именем."}.
|
||||
{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Здесь вы можете завести учётную запись XMPP на этом сервере. Ваш адрес JID (Jabber-идентификатор) будет в виде: \"пользователь@сервер\". Пожалуйста, внимательно читайте инструкции для правильного заполнения полей."}.
|
||||
{"This page allows to unregister an XMPP account in this XMPP server.","Здесь вы можете удалить учётную запись XMPP с этого сервера."}.
|
||||
{"This room is not anonymous","Эта комната не анонимная"}.
|
||||
{"This service can not process the address: ~s","Сервер не может обработать адрес: ~s"}.
|
||||
{"Thursday","Четверг"}.
|
||||
{"Time delay","По истечение"}.
|
||||
{"Timed out waiting for stream resumption","Истекло время ожидания возобновления потока"}.
|
||||
{"To register, visit ~s","Для регистрации посетите ~s"}.
|
||||
{"To ~ts","Ко ~s"}.
|
||||
{"Token TTL","Токен TTL"}.
|
||||
{"Too many active bytestreams","Слишком много активных потоков данных"}.
|
||||
{"Too many CAPTCHA requests","Слишком много запросов капчи"}.
|
||||
@@ -387,24 +538,35 @@
|
||||
{"Too many unacked stanzas","Слишком много неподтверждённых пакетов"}.
|
||||
{"Too many users in this conference","Слишком много пользователей в этой конференции"}.
|
||||
{"Traffic rate limit is exceeded","Превышен лимит скорости посылки информации"}.
|
||||
{"~ts's MAM Archive","История сообщений ~ts"}.
|
||||
{"~ts's Offline Messages Queue","Oчередь офлайновых сообщений ~ts"}.
|
||||
{"Tuesday","Вторник"}.
|
||||
{"Unable to generate a CAPTCHA","Не получилось создать капчу"}.
|
||||
{"Unable to register route on existing local domain","Нельзя регистрировать маршруты на существующие локальные домены"}.
|
||||
{"Unauthorized","Не авторизован"}.
|
||||
{"Unexpected action","Неожиданное действие"}.
|
||||
{"Unexpected error condition: ~p","Неожиданная ошибка: ~p"}.
|
||||
{"Uninstall","Удалить"}.
|
||||
{"Unregister an XMPP account","Удалить учётную запись XMPP"}.
|
||||
{"Unregister","Удалить"}.
|
||||
{"Unsupported <index/> element","Элемент <index/> не поддерживается"}.
|
||||
{"Unsupported version","Неподдерживаемая версия"}.
|
||||
{"Update message of the day (don't send)","Обновить сообщение дня (не рассылать)"}.
|
||||
{"Update message of the day on all hosts (don't send)","Обновить сообщение дня на всех виртуальных серверах (не рассылать)"}.
|
||||
{"Update specs to get modules source, then install desired ones.","Обновите спецификации, чтобы получить исходный код модулей, а затем установите необходимые."}.
|
||||
{"Update Specs","Обновить характеристики"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","Обновление vCard не поддерживается сервером"}.
|
||||
{"Upgrade","Обновить"}.
|
||||
{"URL for Archived Discussion Logs","URL для архивных журналов обсуждений"}.
|
||||
{"User already exists","Пользователь уже существует"}.
|
||||
{"User JID","JID пользователя"}.
|
||||
{"User (jid)","Пользователь (XMPP адрес)"}.
|
||||
{"User Management","Управление пользователями"}.
|
||||
{"User not allowed to perform an IQ set on another user's vCard.","Пользователю запрещено изменять vCard другого пользователя."}.
|
||||
{"User removed","Пользователь удалён"}.
|
||||
{"User session not found","Сессия пользователя не найдена"}.
|
||||
{"User session terminated","Сессия пользователя завершена"}.
|
||||
{"User ~ts","Пользователь ~ts"}.
|
||||
{"Username:","Имя пользователя:"}.
|
||||
{"Users are not allowed to register accounts so quickly","Пользователи не могут регистрировать учётные записи так быстро"}.
|
||||
{"Users Last Activity","Статистика последнего подключения пользователей"}.
|
||||
@@ -416,18 +578,41 @@
|
||||
{"Value of '~s' should be integer","Значение '~s' должно быть целочисленным"}.
|
||||
{"Value 'set' of 'type' attribute is not allowed","Значение 'set' атрибута 'type' недопустимо"}.
|
||||
{"vCard User Search","Поиск пользователей по vCard"}.
|
||||
{"View joined MIX channels","Просмотреть присоединённые каналы MIX"}.
|
||||
{"Virtual Hosts","Виртуальные хосты"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Посетителям запрещено изменять свои псевдонимы в этой комнате"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Посетителям не разрешается посылать сообщения всем присутствующим"}.
|
||||
{"Visitor","Посетитель"}.
|
||||
{"Voice requests are disabled in this conference","Запросы на право голоса отключены в этой конференции"}.
|
||||
{"Voice request","Запрос на право голоса"}.
|
||||
{"Web client which allows to join the room anonymously","Веб-клиент, позволяющий анонимно присоединиться к комнате"}.
|
||||
{"Wednesday","Среда"}.
|
||||
{"When a new subscription is processed and whenever a subscriber comes online","При обработке новой подписки и при каждом появлении подписчика в сети"}.
|
||||
{"When a new subscription is processed","При обработке новой подписки"}.
|
||||
{"When to send the last published item","Когда посылать последний опубликованный элемент"}.
|
||||
{"Whether to allow subscriptions","Разрешить подписку"}.
|
||||
{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Хочет ли сущность получать тело сообщения XMPP в дополнение к формату текста"}.
|
||||
{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Хочет ли сущность получать сводки (агрегации) уведомлений или все уведомления по отдельности"}.
|
||||
{"Whether an entity wants to receive or disable notifications","Хочет ли сущность получать или отключать уведомления"}.
|
||||
{"Whether owners or publisher should receive replies to items","Должны ли владельцы или издатель получать ответы на элементы"}.
|
||||
{"Whether the node is a leaf (default) or a collection","Является ли узел листовым (по умолчанию) или коллекцией"}.
|
||||
{"Whether to allow subscriptions","Разрешить ли подписку"}.
|
||||
{"Whether to make all subscriptions temporary, based on subscriber presence","Сделать все подписки временными в зависимости от присутствия подписчика"}.
|
||||
{"Whether to notify owners about new subscribers and unsubscribes","Уведомлять ли владельцев о новых подписчиках и отписках"}.
|
||||
{"Who can send private messages","Кому можно отправлять личные сообщения"}.
|
||||
{"Who may associate leaf nodes with a collection","Кто может связывать листовые узлы с коллекцией"}.
|
||||
{"Wrong parameters in the web formulary","Недопустимые параметры веб-формы"}.
|
||||
{"Wrong xmlns","Неправильный xmlns"}.
|
||||
{"XMPP Account Registration","Регистрация учётной записи XMPP"}.
|
||||
{"XMPP Domains","Домены XMPP"}.
|
||||
{"XMPP Show Value of Away","XMPP: Отображение значения «Отошёл»"}.
|
||||
{"XMPP Show Value of Chat","XMPP: Отображение значения «Хочу пообщаться»"}.
|
||||
{"XMPP Show Value of DND (Do Not Disturb)","XMPP: Отображение значения «Не беспокоить» (DND)"}.
|
||||
{"XMPP Show Value of XA (Extended Away)","XMPP: Отображение значения «Не доступен» (XA)"}.
|
||||
{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI-адрес ассоциированного узла публикаций-подписок"}.
|
||||
{"You are being removed from the room because of a system shutdown","Вы покинули комнату из-за останова системы"}.
|
||||
{"You are not allowed to send private messages","Вам запрещено посылать личные сообщения"}.
|
||||
{"You are not joined to the channel","Вы не присоединены к каналу"}.
|
||||
{"You can later change your password using an XMPP client.","Позже вы сможете изменить пароль через XMPP-клиент."}.
|
||||
{"You have been banned from this room","Вам запрещено входить в эту конференцию"}.
|
||||
{"You have joined too many conferences","Вы присоединены к слишком большому количеству конференций"}.
|
||||
{"You must fill in field \"Nickname\" in the form","Вы должны заполнить поле \"Псевдоним\" в форме"}.
|
||||
@@ -437,4 +622,6 @@
|
||||
{"Your active privacy list has denied the routing of this stanza.","Маршрутизация этой строфы запрещена вашим активным списком приватности."}.
|
||||
{"Your contact offline message queue is full. The message has been discarded.","Очередь недоставленных сообщений Вашего адресата переполнена. Сообщение не было сохранено."}.
|
||||
{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Ваши запросы на добавление в контакт-лист, а также сообщения к ~s блокируются. Для снятия блокировки перейдите по ссылке ~s"}.
|
||||
{"Your XMPP account was successfully registered.","Ваша учётная запись XMPP была успешно добавлена."}.
|
||||
{"Your XMPP account was successfully unregistered.","Ваша учётная запись XMPP была успешно удалена."}.
|
||||
{"You're not allowed to create nodes","Вам не разрешается создавать узлы"}.
|
||||
|
||||
+7
-6
@@ -64,12 +64,12 @@
|
||||
{luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}}
|
||||
}},
|
||||
{mqtree, "~> 1.0.19", {git, "https://github.com/processone/mqtree", {tag, "1.0.19"}}},
|
||||
{p1_acme, "~> 1.0.28", {git, "https://github.com/processone/p1_acme", {tag, "1.0.28"}}},
|
||||
{p1_acme, "~> 1.0.29", {git, "https://github.com/processone/p1_acme", {tag, "1.0.29"}}},
|
||||
{if_var_true, mysql,
|
||||
{p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}},
|
||||
{p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}},
|
||||
{if_var_true, pgsql,
|
||||
{p1_pgsql, "~> 1.1.35", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.35"}}}},
|
||||
{p1_pgsql, "~> 1.1.36", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.36"}}}},
|
||||
{p1_utils, "~> 1.0.28", {git, "https://github.com/processone/p1_utils", {tag, "1.0.28"}}},
|
||||
{pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}},
|
||||
{if_var_true, sqlite,
|
||||
@@ -77,8 +77,8 @@
|
||||
{stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}},
|
||||
{if_var_true, stun,
|
||||
{stun, "~> 1.2.21", {git, "https://github.com/processone/stun", {tag, "1.2.21"}}}},
|
||||
{xmpp, "~> 1.11.1", {git, "https://github.com/processone/xmpp", {tag, "1.11.1"}}},
|
||||
{yconf, "~> 1.0.21", {git, "https://github.com/processone/yconf", {tag, "1.0.21"}}}
|
||||
{xmpp, "~> 1.11.2", {git, "https://github.com/processone/xmpp", {tag, "1.11.2"}}},
|
||||
{yconf, "~> 1.0.22", {git, "https://github.com/processone/yconf", {tag, "1.0.22"}}}
|
||||
]}.
|
||||
|
||||
{gitonly_deps, [ejabberd_po]}.
|
||||
@@ -126,7 +126,6 @@
|
||||
|
||||
{erl_opts, [nowarn_deprecated_function,
|
||||
{i, "include"},
|
||||
{if_version_above, "20", {d, 'DEPRECATED_GET_STACKTRACE'}},
|
||||
{if_version_above, "20", {d, 'HAVE_ERL_ERROR'}},
|
||||
{if_version_above, "20", {d, 'HAVE_URI_STRING'}},
|
||||
{if_version_below, "21", {d, 'USE_OLD_HTTP_URI'}},
|
||||
@@ -140,11 +139,12 @@
|
||||
{if_version_below, "25", {d, 'OTP_BELOW_25'}},
|
||||
{if_version_below, "26", {d, 'OTP_BELOW_26'}},
|
||||
{if_version_below, "27", {d, 'OTP_BELOW_27'}},
|
||||
{if_version_below, "27", {feature, maybe_expr, enable}},
|
||||
{if_version_below, "28", {d, 'OTP_BELOW_28'}},
|
||||
{if_var_false, debug, no_debug_info},
|
||||
{if_var_true, debug, debug_info},
|
||||
{if_var_true, elixir, {d, 'ELIXIR_ENABLED'}},
|
||||
{if_var_true, new_sql_schema, {d, 'NEW_SQL_SCHEMA'}},
|
||||
{if_var_true, multihost_sql_schema, {d, 'MULTIHOST_SQL_SCHEMA'}},
|
||||
{if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATEWAY_WORKAROUND'}},
|
||||
{if_var_true, sip, {d, 'SIP'}},
|
||||
{if_var_true, stun, {d, 'STUN'}},
|
||||
@@ -257,6 +257,7 @@
|
||||
{cover_export_enabled, true}.
|
||||
{cover_excl_mods, [eldap_filter_yecc]}.
|
||||
{coveralls_coverdata, "_build/test/cover/ct.coverdata"}.
|
||||
{coveralls_parallel, "true"}.
|
||||
{coveralls_service_name, "github"}.
|
||||
|
||||
%%%
|
||||
|
||||
+12
-12
@@ -14,18 +14,18 @@
|
||||
{<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0},
|
||||
{<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0},
|
||||
{<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.19">>},0},
|
||||
{<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.28">>},0},
|
||||
{<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.29">>},0},
|
||||
{<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0},
|
||||
{<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0},
|
||||
{<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.35">>},0},
|
||||
{<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.36">>},0},
|
||||
{<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.28">>},0},
|
||||
{<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0},
|
||||
{<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0},
|
||||
{<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.33">>},0},
|
||||
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.21">>},0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1},
|
||||
{<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.1">>},0},
|
||||
{<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.21">>},0}]}.
|
||||
{<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.2">>},0},
|
||||
{<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.22">>},0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>},
|
||||
@@ -43,18 +43,18 @@
|
||||
{<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>},
|
||||
{<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>},
|
||||
{<<"mqtree">>, <<"D769C25F898810725FC7DB0DBFFE5F72098647048B1BE2E6D772F1C2F31D8476">>},
|
||||
{<<"p1_acme">>, <<"64D9C17F5412AA92D75B29206B2B984D734A4FE1B7EACB66C3D7A7C697AC612C">>},
|
||||
{<<"p1_acme">>, <<"86372C34DE9CE7E498842828F761060C6B7726001E2853FF118893F6DD192E24">>},
|
||||
{<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>},
|
||||
{<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>},
|
||||
{<<"p1_pgsql">>, <<"E13D89F14D717553E85C88A152CE77461916B013D88FCB851E354A0B332D4218">>},
|
||||
{<<"p1_pgsql">>, <<"4BC0535F2F9A2355674DFDC9E1CFC18A722325FED588977FFF1C88C5915AE3EB">>},
|
||||
{<<"p1_utils">>, <<"9A7088A98D788B4C4880FD3C82D0C135650DB13F2E4EF7E10DB179791BC94D59">>},
|
||||
{<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>},
|
||||
{<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>},
|
||||
{<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>},
|
||||
{<<"stun">>, <<"735855314AD22CB7816B88597D2F5CA22E24AA5E4D6010A0EF3AFFB33CEED6A5">>},
|
||||
{<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>},
|
||||
{<<"xmpp">>, <<"60181E7D3E8E48AA3B23B2792075CDA37E2E507EC152490B866E61E5320CB1DA">>},
|
||||
{<<"yconf">>, <<"DBAE1589381E044529E112B7E0097C89D88DF89E446EAD53BD33E8D27E2BCC83">>}]},
|
||||
{<<"xmpp">>, <<"6EF43A6E5FB71506AF7ECCD05BBB6CCCB58EB1856C539613E76FD9A5C4E936BA">>},
|
||||
{<<"yconf">>, <<"52A435F9B60AB1E13950DFE3F7131ECDD8B3D1CA72C44BF66FC74B4571027124">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>},
|
||||
{<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>},
|
||||
@@ -71,16 +71,16 @@
|
||||
{<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>},
|
||||
{<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>},
|
||||
{<<"mqtree">>, <<"C81065715C49A1882812F80A5AE2D842E80DD3F2D130530DF35990248BF8CE3C">>},
|
||||
{<<"p1_acme">>, <<"CE686986DE3F9D5FD285AFE87523CB45329A349C6C6BE7ACC1ED916725D46423">>},
|
||||
{<<"p1_acme">>, <<"08FD38F7FBE2DC28A237AA1B38B306B73455695CC8881A3DDD6A11B7C51F7BC7">>},
|
||||
{<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>},
|
||||
{<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>},
|
||||
{<<"p1_pgsql">>, <<"E99594446C411C660696795B062336F5C4BD800451D8F620BB4D4CE304E255C2">>},
|
||||
{<<"p1_pgsql">>, <<"82BCA8B895C84F4600EB8D609A32CB5FDD72A7F5BD938DFB29179E08C643FD09">>},
|
||||
{<<"p1_utils">>, <<"C49BD44BC4A40AD996691AF826DD7E0AA56D4D0CD730817190A1F84D1A7F0033">>},
|
||||
{<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>},
|
||||
{<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>},
|
||||
{<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>},
|
||||
{<<"stun">>, <<"3D7FE8EFB9D05B240A6AA9A6BF8B8B7BFF2D802895D170443C588987DC1E12D9">>},
|
||||
{<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>},
|
||||
{<<"xmpp">>, <<"A5C933DF904AB3CEC15425DA334E410CE84EC3AE7B81EFE069E5DB368A7B3716">>},
|
||||
{<<"yconf">>, <<"C524A5F1FD86875D85B469CC2E368C204F97CCA1C3918736E21F5001C01D096C">>}]}
|
||||
{<<"xmpp">>, <<"BB681644E15E3EFC0008AB3A717944D67CF611A4B7E344382AA6367447BD52D2">>},
|
||||
{<<"yconf">>, <<"ACA83457CEABE70756484B5C87BA7B1955F511D499168687EAEAA7C300E857F1">>}]}
|
||||
].
|
||||
|
||||
+32
-7
@@ -34,6 +34,7 @@
|
||||
reopen_log/0, rotate_log/0,
|
||||
set_loglevel/1,
|
||||
evacuate_kindly/2,
|
||||
restart_kindly/2,
|
||||
stop_kindly/2, send_service_message_all_mucs/2,
|
||||
registered_vhosts/0,
|
||||
reload_config/0,
|
||||
@@ -181,7 +182,20 @@ get_commands_spec() ->
|
||||
args_example = [60, <<"Server will stop now.">>],
|
||||
args = [{delay, integer}, {announcement, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = stop_kindly, tags = [server],
|
||||
#ejabberd_commands{name = restart_kindly, tags = [server, async],
|
||||
desc = "Restart kindly the server",
|
||||
longdesc = "Inform users and rooms, wait, and restart the server.\n"
|
||||
"Provide the delay in seconds, and the "
|
||||
"announcement quoted, for example: \n"
|
||||
"`ejabberdctl restart_kindly 60 "
|
||||
"\\\"The server will stop in one minute.\\\"`",
|
||||
note = "added in 25.10",
|
||||
module = ?MODULE, function = restart_kindly,
|
||||
args_desc = ["Seconds to wait", "Announcement to send, with quotes"],
|
||||
args_example = [60, <<"Server will restart now.">>],
|
||||
args = [{delay, integer}, {announcement, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = stop_kindly, tags = [server, async],
|
||||
desc = "Stop kindly the server (informing users)",
|
||||
longdesc = "Inform users and rooms, wait, and stop the server.\n"
|
||||
"Provide the delay in seconds, and the "
|
||||
@@ -274,7 +288,9 @@ get_commands_spec() ->
|
||||
`ejabberdctl` (or some `CTL_ON_` container
|
||||
environment variables) to run more commands
|
||||
afterwards, you may want to precede them with
|
||||
the _`started`_ command to ensure the
|
||||
the `started`
|
||||
_`../../admin/guide/managing.md#ejabberdctl-commands|ejabberdctl command`_
|
||||
to ensure the
|
||||
clustering process has completed before
|
||||
proceeding. For example: `join_cluster
|
||||
ejabberd@main` > `started` > `list_cluster`.",
|
||||
@@ -704,6 +720,9 @@ set_loglevel(LogLevel) ->
|
||||
evacuate_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
perform_kindly(DelaySeconds, AnnouncementTextString, evacuate).
|
||||
|
||||
restart_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
perform_kindly(DelaySeconds, AnnouncementTextString, restart).
|
||||
|
||||
stop_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
perform_kindly(DelaySeconds, AnnouncementTextString, stop).
|
||||
|
||||
@@ -721,15 +740,19 @@ perform_kindly(DelaySeconds, AnnouncementTextString, Action) ->
|
||||
ejabberd_admin,
|
||||
send_service_message_all_mucs,
|
||||
[Subject, AnnouncementText]},
|
||||
{WaitingDesc, timer, sleep, [DelaySeconds * 1000]},
|
||||
{"Stopping ejabberd", application, stop, [ejabberd]}],
|
||||
{WaitingDesc, timer, sleep, [DelaySeconds * 1000]}
|
||||
],
|
||||
SpecificSteps =
|
||||
case Action of
|
||||
evacuate ->
|
||||
[{"Starting ejabberd", application, start, [ejabberd]},
|
||||
[{"Stopping ejabberd", application, stop, [ejabberd]},
|
||||
{"Starting ejabberd", application, start, [ejabberd]},
|
||||
{"Stopping ejabberd port listeners", ejabberd_listener, stop_listeners, []}];
|
||||
restart ->
|
||||
[{"Restarting Erlang node", init, restart, []}];
|
||||
stop ->
|
||||
[{"Stopping Mnesia", mnesia, stop, []}, {"Stopping Erlang node", init, stop, []}]
|
||||
[
|
||||
{"Stopping Erlang node", init, stop, []}]
|
||||
end,
|
||||
Steps = PreSteps ++ SpecificSteps,
|
||||
NumberLast = length(Steps),
|
||||
@@ -737,7 +760,9 @@ perform_kindly(DelaySeconds, AnnouncementTextString, Action) ->
|
||||
lists:foldl(fun({Desc, Mod, Func, Args}, NumberThis) ->
|
||||
SecondsDiff =
|
||||
calendar:datetime_to_gregorian_seconds({date(), time()}) - TimestampStart,
|
||||
io:format("[~p/~p ~ps] ~ts... ", [NumberThis, NumberLast, SecondsDiff, Desc]),
|
||||
io:format("~s[~p/~p ~ps]~s ~ts...~s ",
|
||||
[?CLEAD ++ ?CINFO, NumberThis, NumberLast, SecondsDiff,
|
||||
?CMID ++ ?CINFO, Desc, ?CCLEAN]),
|
||||
Result = (catch apply(Mod, Func, Args)),
|
||||
io:format("~p~n", [Result]),
|
||||
NumberThis + 1
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
-export([start/2, prep_stop/1, stop/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
%%%
|
||||
%%% Application API
|
||||
|
||||
@@ -97,6 +97,8 @@ perl_gen({Name, string}, Str, _Indent, HTMLOutput) ->
|
||||
[?ARG(Name), ?OP_L(" => "), ?STR(Str)];
|
||||
perl_gen({Name, binary}, Str, _Indent, HTMLOutput) ->
|
||||
[?ARG(Name), ?OP_L(" => "), ?STR(Str)];
|
||||
perl_gen({Name, binary_or_list}, Str, Indent, HTMLOutput) ->
|
||||
perl_gen({Name, {list, {Name, binary}}}, Str, Indent, HTMLOutput);
|
||||
perl_gen({Name, atom}, Atom, _Indent, HTMLOutput) ->
|
||||
[?ARG(Name), ?OP_L(" => "), ?STR_A(Atom)];
|
||||
perl_gen({Name, {tuple, Fields}}, Tuple, Indent, HTMLOutput) ->
|
||||
@@ -128,6 +130,8 @@ java_gen({Name, string}, Str, _Indent, HTMLOutput) ->
|
||||
[?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?STR(Str), ?OP_L(");")];
|
||||
java_gen({Name, binary}, Str, _Indent, HTMLOutput) ->
|
||||
[?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?STR(Str), ?OP_L(");")];
|
||||
java_gen({Name, binary_or_list}, Str, Indent, HTMLOutput) ->
|
||||
java_gen({Name, {list, {Name, binary}}}, Str, Indent, HTMLOutput);
|
||||
java_gen({Name, atom}, Atom, _Indent, HTMLOutput) ->
|
||||
[?ID_L("put"), ?OP_L("("), ?STR_A(Name), ?OP_L(", "), ?STR_A(Atom), ?OP_L(");")];
|
||||
java_gen({Name, {tuple, Fields}}, Tuple, Indent, HTMLOutput) ->
|
||||
@@ -178,6 +182,8 @@ xml_gen({Name, binary}, Str, Indent, HTMLOutput) ->
|
||||
[?XML_L(name, Indent, 1, ?ID_A(Name)),
|
||||
?XML(value, Indent, 1,
|
||||
[?XML_L(string, Indent, 2, ?ID(Str))])])];
|
||||
xml_gen({Name, binary_or_list}, Str, Indent, HTMLOutput) ->
|
||||
xml_gen({Name, {list, {Name, binary}}}, Str, Indent, HTMLOutput);
|
||||
xml_gen({Name, atom}, Atom, Indent, HTMLOutput) ->
|
||||
[?XML(member, Indent,
|
||||
[?XML_L(name, Indent, 1, ?ID_A(Name)),
|
||||
@@ -215,6 +221,8 @@ json_gen({_Name, string}, Str, _Indent, HTMLOutput) ->
|
||||
[?STR(Str)];
|
||||
json_gen({_Name, binary}, Str, _Indent, HTMLOutput) ->
|
||||
[?STR(Str)];
|
||||
json_gen({Name, binary_or_list}, Str, Indent, HTMLOutput) ->
|
||||
json_gen({Name, {list, {Name, binary}}}, Str, Indent, HTMLOutput);
|
||||
json_gen({_Name, atom}, Atom, _Indent, HTMLOutput) ->
|
||||
[?STR_A(Atom)];
|
||||
json_gen({_Name, rescode}, Val, _Indent, HTMLOutput) ->
|
||||
@@ -270,6 +278,8 @@ generate_example_input({_Name, string}, {LastStr, LastNum}) ->
|
||||
{string:chars(LastStr+1, 5), {LastStr+1, LastNum}};
|
||||
generate_example_input({_Name, binary}, {LastStr, LastNum}) ->
|
||||
{iolist_to_binary(string:chars(LastStr+1, 5)), {LastStr+1, LastNum}};
|
||||
generate_example_input({_Name, binary_or_list}, {LastStr, LastNum}) ->
|
||||
{[iolist_to_binary(string:chars(LastStr+1, 5))], {LastStr+1, LastNum}};
|
||||
generate_example_input({_Name, atom}, {LastStr, LastNum}) ->
|
||||
{list_to_atom(string:chars(LastStr+1, 5)), {LastStr+1, LastNum}};
|
||||
generate_example_input({_Name, rescode}, {LastStr, LastNum}) ->
|
||||
@@ -347,6 +357,8 @@ format_type({Name, Type}) ->
|
||||
io_lib:format("~ts::~ts", [Name, format_type(Type)]);
|
||||
format_type(binary) ->
|
||||
"string";
|
||||
format_type(binary_or_list) ->
|
||||
"string | [string]";
|
||||
format_type(atom) ->
|
||||
"string";
|
||||
format_type(Type) ->
|
||||
|
||||
+12
-11
@@ -52,7 +52,7 @@
|
||||
{get_lang, 1}]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-type option() :: atom() | {atom(), global | binary()}.
|
||||
-type error_reason() :: {merge_conflict, atom(), binary()} |
|
||||
@@ -169,14 +169,14 @@ get_option({O, Host} = Opt) ->
|
||||
T -> T
|
||||
end,
|
||||
try ets:lookup_element(Tab, Opt, 2)
|
||||
catch ?EX_RULE(error, badarg, St) when Host /= global ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
Val = get_option({O, global}),
|
||||
?DEBUG("Option '~ts' is not defined for virtual host '~ts'. "
|
||||
"This is a bug, please report it with the following "
|
||||
"stacktrace included:~n** ~ts",
|
||||
[O, Host, misc:format_exception(2, error, badarg, StackTrace)]),
|
||||
Val
|
||||
catch
|
||||
error:badarg:StackTrace when Host /= global ->
|
||||
Val = get_option({O, global}),
|
||||
?DEBUG("Option '~ts' is not defined for virtual host '~ts'. "
|
||||
"This is a bug, please report it with the following "
|
||||
"stacktrace included:~n** ~ts",
|
||||
[O, Host, misc:format_exception(2, error, badarg, StackTrace)]),
|
||||
Val
|
||||
end.
|
||||
|
||||
-spec set_option(option(), term()) -> ok.
|
||||
@@ -802,8 +802,9 @@ load_file(File) ->
|
||||
Err ->
|
||||
abort(Err)
|
||||
end
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
{error, {exception, Class, Reason, ?EX_STACK(St)}}
|
||||
catch
|
||||
Class:Reason:Stack ->
|
||||
{error, {exception, Class, Reason, Stack}}
|
||||
end.
|
||||
|
||||
-spec commit() -> ok.
|
||||
|
||||
@@ -232,6 +232,9 @@ filter(_Host, route_subdomains, _, _) ->
|
||||
false;
|
||||
filter(_Host, auth_password_types_hidden_in_scram1, Val, _) ->
|
||||
{true, {auth_password_types_hidden_in_sasl1, Val}};
|
||||
filter(_Host, new_sql_schema, Val, _) ->
|
||||
warn_replaced_option(new_sql_schema, sql_schema_multihost),
|
||||
{true, {sql_schema_multihost, Val}};
|
||||
filter(Host, modules, ModOpts, State) ->
|
||||
NoDialbackHosts = maps:get(remove_s2s_dialback, State, []),
|
||||
ModOpts1 = lists:filter(
|
||||
|
||||
+11
-10
@@ -39,7 +39,7 @@
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-define(DEFAULT_VERSION, 1000000).
|
||||
|
||||
@@ -331,11 +331,10 @@ try_call_command(Args, Auth, AccessCommands, Version) ->
|
||||
?STATUS_ERROR};
|
||||
throw:Error ->
|
||||
{io_lib:format("~p", [Error]), ?STATUS_ERROR};
|
||||
?EX_RULE(A, Why, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
{io_lib:format("Unhandled exception occurred executing the command:~n** ~ts",
|
||||
[misc:format_exception(2, A, Why, StackTrace)]),
|
||||
?STATUS_ERROR}
|
||||
A:Why:StackTrace ->
|
||||
{io_lib:format("Unhandled exception occurred executing the command:~n** ~ts",
|
||||
[misc:format_exception(2, A, Why, StackTrace)]),
|
||||
?STATUS_ERROR}
|
||||
end.
|
||||
|
||||
-spec call_command(Args::[string()],
|
||||
@@ -390,6 +389,8 @@ format_arg(Arg, integer) ->
|
||||
format_arg2(Arg, "~d");
|
||||
format_arg(Arg, binary) ->
|
||||
unicode:characters_to_binary(Arg, utf8);
|
||||
format_arg(Arg, binary_or_list) ->
|
||||
[unicode:characters_to_binary(Arg, utf8)];
|
||||
format_arg("", string) ->
|
||||
"";
|
||||
format_arg(Arg, string) ->
|
||||
@@ -1000,12 +1001,12 @@ format_usage_ctype1({Name, Type, Description}, Indentation, ShCode) ->
|
||||
|
||||
format_usage_ctype(Type, _Indentation)
|
||||
when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary)
|
||||
or (Type==rescode) or (Type==restuple) ->
|
||||
or (Type==rescode) or (Type==restuple) or (Type==binary_or_list) ->
|
||||
io_lib:format("~p", [Type]);
|
||||
|
||||
format_usage_ctype({Name, Type}, _Indentation)
|
||||
when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary)
|
||||
or (Type==rescode) or (Type==restuple)
|
||||
or (Type==rescode) or (Type==restuple) or (Type==binary_or_list)
|
||||
or (Type==any) ->
|
||||
io_lib:format("~p::~p", [Name, Type]);
|
||||
|
||||
@@ -1169,9 +1170,9 @@ get_commands_spec() ->
|
||||
longdesc = "This command is exclusive for the ejabberdctl command-line script, "
|
||||
"don't attempt to execute it using any other API frontend.",
|
||||
note = "added in 24.02",
|
||||
args = [{db_type, string}, {db_version, string}, {new_schema, string}],
|
||||
args = [{db_type, string}, {db_version, string}, {multihost_schema, string}],
|
||||
args_desc = ["Database type: pgsql | mysql | sqlite",
|
||||
"Your database version: 16.1, 8.2.0...",
|
||||
"Use new schema: 0, false, 1 or true"],
|
||||
"Use multihost schema: 0, false, 1 or true"],
|
||||
args_example = ["pgsql", "16.1", "true"]}
|
||||
].
|
||||
|
||||
@@ -437,7 +437,7 @@ run_a2x(Cwd, AsciiDocFile) ->
|
||||
{error, "a2x was not found: do you have 'asciidoc' installed?"};
|
||||
{true, Path} ->
|
||||
Cmd = lists:flatten(
|
||||
io_lib:format("~ts -f manpage ~ts -D ~ts",
|
||||
io_lib:format("~ts --no-xmllint -f manpage ~ts -D ~ts",
|
||||
[Path, AsciiDocFile, Cwd])),
|
||||
case os:cmd(Cmd) of
|
||||
"" -> ok;
|
||||
|
||||
+33
-32
@@ -60,7 +60,7 @@
|
||||
).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-record(state, {}).
|
||||
-type subscriber() :: {Module :: atom(), Function :: atom(), InitArg :: any()}.
|
||||
@@ -455,17 +455,19 @@ safe_apply(Hook, Module, Function, Args) ->
|
||||
true ->
|
||||
apply(Module, Function, Args)
|
||||
end
|
||||
catch ?EX_RULE(E, R, St) when E /= exit; R /= normal ->
|
||||
Stack = ?EX_STACK(St),
|
||||
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++
|
||||
string:join(
|
||||
["** ~ts"|
|
||||
["** Arg " ++ integer_to_list(I) ++ " = ~p"
|
||||
|| I <- lists:seq(1, length(Args))]],
|
||||
"~n"),
|
||||
[Hook, Module, Function, length(Args),
|
||||
misc:format_exception(2, E, R, Stack)|Args]),
|
||||
'EXIT'
|
||||
catch
|
||||
E:R:Stack when E /= exit; R /= normal ->
|
||||
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++
|
||||
string:join(
|
||||
["** ~ts" | [ "** Arg " ++ integer_to_list(I) ++ " = ~p"
|
||||
|| I <- lists:seq(1, length(Args)) ]],
|
||||
"~n"),
|
||||
[Hook,
|
||||
Module,
|
||||
Function,
|
||||
length(Args),
|
||||
misc:format_exception(2, E, R, Stack) | Args]),
|
||||
'EXIT'
|
||||
end.
|
||||
|
||||
-spec call_subscriber_list([subscriber()], binary() | global, atom(), {atom(), atom(), integer(), list()} | list(), subscriber_event(), [subscriber()]) -> any().
|
||||
@@ -480,18 +482,20 @@ call_subscriber_list([{Mod, Func, InitArg} | SubscriberList], Host, Hook, Callba
|
||||
try apply(Mod, Func, SubscriberArgs) of
|
||||
State ->
|
||||
call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, [{Mod, Func, State} | Result])
|
||||
catch ?EX_RULE(E, R, St) when E /= exit; R /= normal ->
|
||||
Stack = ?EX_STACK(St),
|
||||
?ERROR_MSG("Hook subscriber ~p crashed when running ~p:~p/~p:~n" ++
|
||||
string:join(
|
||||
["** ~ts"|
|
||||
["** Arg " ++ integer_to_list(I) ++ " = ~p"
|
||||
|| I <- lists:seq(1, length(SubscriberArgs))]],
|
||||
"~n"),
|
||||
[Hook, Mod, Func, length(SubscriberArgs),
|
||||
misc:format_exception(2, E, R, Stack)|SubscriberArgs]),
|
||||
%% Do not append subscriber for next calls:
|
||||
call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, Result)
|
||||
catch
|
||||
E:R:Stack when E /= exit; R /= normal ->
|
||||
?ERROR_MSG("Hook subscriber ~p crashed when running ~p:~p/~p:~n" ++
|
||||
string:join(
|
||||
["** ~ts" | [ "** Arg " ++ integer_to_list(I) ++ " = ~p"
|
||||
|| I <- lists:seq(1, length(SubscriberArgs)) ]],
|
||||
"~n"),
|
||||
[Hook,
|
||||
Mod,
|
||||
Func,
|
||||
length(SubscriberArgs),
|
||||
misc:format_exception(2, E, R, Stack) | SubscriberArgs]),
|
||||
%% Do not append subscriber for next calls:
|
||||
call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, Result)
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -709,13 +713,11 @@ run_event_handlers(TracingOpts, Hook, Host, Event, EventArgs, RunType) ->
|
||||
_ ->
|
||||
ok
|
||||
catch
|
||||
?EX_RULE(E, R, St) ->
|
||||
Stack = ?EX_STACK(St),
|
||||
?ERROR_MSG(
|
||||
E:R:Stack ->
|
||||
?ERROR_MSG(
|
||||
"(~0p|~ts|~0p) Tracing event '~0p' handler exception(~0p): ~0p: ~0p",
|
||||
[Hook, Host, erlang:self(), EventHandler, E, R, Stack]
|
||||
),
|
||||
ok
|
||||
[Hook, Host, erlang:self(), EventHandler, E, R, Stack]),
|
||||
ok
|
||||
end
|
||||
end,
|
||||
EventHandlerList
|
||||
@@ -885,8 +887,7 @@ tracing_output(#{output_function := OutputF}, Text, Args) ->
|
||||
_ ->
|
||||
ok
|
||||
catch
|
||||
?EX_RULE(E, R, St) ->
|
||||
Stack = ?EX_STACK(St),
|
||||
E:R:Stack ->
|
||||
?ERROR_MSG("Tracing output function exception(~0p): ~0p: ~0p", [E, R, Stack]),
|
||||
ok
|
||||
end;
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
-include("logger.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
-record(state, {sockmod,
|
||||
@@ -372,11 +372,11 @@ process(Handlers, Request) ->
|
||||
try
|
||||
HandlerModule:process(LocalPath, Request)
|
||||
catch
|
||||
?EX_RULE(Class, Reason, Stack) ->
|
||||
Class:Reason:Stack ->
|
||||
?ERROR_MSG(
|
||||
"HTTP handler crashed: ~s",
|
||||
[misc:format_exception(2, Class, Reason, ?EX_STACK(Stack))]),
|
||||
erlang:raise(Class, Reason, ?EX_STACK(Stack))
|
||||
"HTTP handler crashed: ~s",
|
||||
[misc:format_exception(2, Class, Reason, Stack)]),
|
||||
erlang:raise(Class, Reason, Stack)
|
||||
end
|
||||
end,
|
||||
ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]),
|
||||
|
||||
@@ -117,7 +117,7 @@ socket_handoff(LocalPath, Request, Opts) ->
|
||||
|
||||
%%% Internal
|
||||
|
||||
init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
|
||||
init([{#ws{ip = IP, http_opts = HOpts, headers = Headers}, _} = WS]) ->
|
||||
SOpts = lists:filtermap(fun({stream_management, _}) -> true;
|
||||
({max_ack_queue, _}) -> true;
|
||||
({ack_timeout, _}) -> true;
|
||||
@@ -128,7 +128,7 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
|
||||
({access, _}) -> true;
|
||||
(_) -> false
|
||||
end, HOpts),
|
||||
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
|
||||
Opts = ejabberd_c2s_config:get_c2s_limits() ++ [{http, [{headers, Headers}]}] ++ SOpts,
|
||||
PingInterval = ejabberd_option:websocket_ping_interval(),
|
||||
WSTimeout = ejabberd_option:websocket_timeout(),
|
||||
Socket = {http_ws, self(), IP},
|
||||
|
||||
+6
-6
@@ -36,7 +36,7 @@
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-record(state, {expire = infinity :: timeout()}).
|
||||
-type state() :: #state{}.
|
||||
@@ -174,11 +174,11 @@ calc_checksum(Data) ->
|
||||
-spec callback(atom() | pid(), #iq{} | timeout, term()) -> any().
|
||||
callback(undefined, IQRes, Fun) ->
|
||||
try Fun(IQRes)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to process iq response:~n~ts~n** ~ts",
|
||||
[xmpp:pp(IQRes),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to process iq response:~n~ts~n** ~ts",
|
||||
[xmpp:pp(IQRes),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end;
|
||||
callback(Proc, IQRes, Ctx) ->
|
||||
try
|
||||
|
||||
@@ -323,6 +323,8 @@ split_opts(Transport, Opts) ->
|
||||
{ModOpts, [{Opt, Val} | SockOpts]};
|
||||
{backlog, _} ->
|
||||
{ModOpts, SockOpts};
|
||||
{send_timeout, _} ->
|
||||
{ModOpts, [{Opt, Val} | SockOpts]};
|
||||
_ ->
|
||||
{ModOpts#{Opt => Val}, SockOpts}
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("translate.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
+10
-9
@@ -42,7 +42,7 @@
|
||||
-define(NEED_RESET, [local_content, type]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-record(state, {tables = #{} :: tables(),
|
||||
schema = [] :: [{atom(), custom_schema()}]}).
|
||||
@@ -377,14 +377,15 @@ do_transform(OldAttrs, Attrs, Old) ->
|
||||
transform_fun(Module, Name) ->
|
||||
fun(Obj) ->
|
||||
try Module:transform(Obj)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to transform Mnesia table ~ts:~n"
|
||||
"** Record: ~p~n"
|
||||
"** ~ts",
|
||||
[Name, Obj,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to transform Mnesia table ~ts:~n"
|
||||
"** Record: ~p~n"
|
||||
"** ~ts",
|
||||
[Name,
|
||||
Obj,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
-export([modules/0, modules/1]).
|
||||
-export([negotiation_timeout/0]).
|
||||
-export([net_ticktime/0]).
|
||||
-export([new_sql_schema/0]).
|
||||
-export([oauth_access/0, oauth_access/1]).
|
||||
-export([oauth_cache_life_time/0]).
|
||||
-export([oauth_cache_missed/0]).
|
||||
@@ -162,6 +161,7 @@
|
||||
-export([sql_prepared_statements/0, sql_prepared_statements/1]).
|
||||
-export([sql_query_timeout/0, sql_query_timeout/1]).
|
||||
-export([sql_queue_type/0, sql_queue_type/1]).
|
||||
-export([sql_schema_multihost/0]).
|
||||
-export([sql_server/0, sql_server/1]).
|
||||
-export([sql_ssl/0, sql_ssl/1]).
|
||||
-export([sql_ssl_cafile/0, sql_ssl_cafile/1]).
|
||||
@@ -686,10 +686,6 @@ negotiation_timeout() ->
|
||||
net_ticktime() ->
|
||||
ejabberd_config:get_option({net_ticktime, global}).
|
||||
|
||||
-spec new_sql_schema() -> boolean().
|
||||
new_sql_schema() ->
|
||||
ejabberd_config:get_option({new_sql_schema, global}).
|
||||
|
||||
-spec oauth_access() -> 'none' | acl:acl().
|
||||
oauth_access() ->
|
||||
oauth_access(global).
|
||||
@@ -1104,6 +1100,10 @@ sql_queue_type() ->
|
||||
sql_queue_type(Host) ->
|
||||
ejabberd_config:get_option({sql_queue_type, Host}).
|
||||
|
||||
-spec sql_schema_multihost() -> boolean().
|
||||
sql_schema_multihost() ->
|
||||
ejabberd_config:get_option({sql_schema_multihost, global}).
|
||||
|
||||
-spec sql_server() -> binary().
|
||||
sql_server() ->
|
||||
sql_server(global).
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
-export([opt_type/1, options/0, globals/0, doc/0]).
|
||||
|
||||
-ifdef(NEW_SQL_SCHEMA).
|
||||
-define(USE_NEW_SQL_SCHEMA_DEFAULT, true).
|
||||
-ifdef(MULTIHOST_SQL_SCHEMA).
|
||||
-define(USE_MULTIHOST_SQL_SCHEMA_DEFAULT, true).
|
||||
-else.
|
||||
-define(USE_NEW_SQL_SCHEMA_DEFAULT, false).
|
||||
-define(USE_MULTIHOST_SQL_SCHEMA_DEFAULT, false).
|
||||
-endif.
|
||||
|
||||
-include_lib("kernel/include/inet.hrl").
|
||||
@@ -266,7 +266,7 @@ opt_type(negotiation_timeout) ->
|
||||
econf:timeout(second);
|
||||
opt_type(net_ticktime) ->
|
||||
econf:timeout(second);
|
||||
opt_type(new_sql_schema) ->
|
||||
opt_type(sql_schema_multihost) ->
|
||||
econf:bool();
|
||||
opt_type(update_sql_schema) ->
|
||||
econf:bool();
|
||||
@@ -632,7 +632,7 @@ options() ->
|
||||
{modules, []},
|
||||
{negotiation_timeout, timer:seconds(120)},
|
||||
{net_ticktime, timer:seconds(60)},
|
||||
{new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT},
|
||||
{sql_schema_multihost, ?USE_MULTIHOST_SQL_SCHEMA_DEFAULT},
|
||||
{update_sql_schema, true},
|
||||
{update_sql_schema_timeout, timer:minutes(5)},
|
||||
{oauth_access, none},
|
||||
@@ -789,7 +789,6 @@ globals() ->
|
||||
log_modules_fully,
|
||||
negotiation_timeout,
|
||||
net_ticktime,
|
||||
new_sql_schema,
|
||||
update_sql_schema,
|
||||
node_start,
|
||||
oauth_cache_life_time,
|
||||
@@ -822,6 +821,7 @@ globals() ->
|
||||
sm_cache_life_time,
|
||||
sm_cache_missed,
|
||||
sm_cache_size,
|
||||
sql_schema_multihost,
|
||||
trusted_proxies,
|
||||
validate_stream,
|
||||
version,
|
||||
|
||||
@@ -992,23 +992,30 @@ doc() ->
|
||||
"bugs. Usually leaving default value of this is option is best, "
|
||||
"tweak it only if you know what you are doing. "
|
||||
"The default value is '1 minute'.")}},
|
||||
{new_sql_schema,
|
||||
{sql_schema_multihost,
|
||||
#{value => "true | false",
|
||||
note => "renamed in 25.10",
|
||||
desc =>
|
||||
{?T("Whether to use the "
|
||||
"_`database.md#default-and-new-schemas|new SQL schema`_. "
|
||||
"_`database.md#default-and-new-schemas|multihost SQL schema`_. "
|
||||
"All schemas are located "
|
||||
"at <https://github.com/processone/ejabberd/tree/~s/sql>. "
|
||||
"There are two schemas available. The default legacy schema "
|
||||
"There are two schemas available. The legacy 'singlehost' schema "
|
||||
"stores one XMPP domain into one ejabberd database. "
|
||||
"The 'new' schema can handle several XMPP domains in a "
|
||||
"single ejabberd database. Using this 'new' schema is best when "
|
||||
"The 'multihost' schema can handle several XMPP domains in a "
|
||||
"single ejabberd database. The 'multihost' schema is preferable when "
|
||||
"serving several XMPP domains and/or changing domains from "
|
||||
"time to time. This avoid need to manage several databases and "
|
||||
"handle complex configuration changes. The default depends on "
|
||||
"configuration flag '--enable-new-sql-schema' which is set "
|
||||
"configuration flag '--enable-sql-schema-multihost' which is set "
|
||||
"at compile time."),
|
||||
[binary:part(ejabberd_config:version(), {0,5})]}}},
|
||||
{new_sql_schema,
|
||||
#{value => "true | false",
|
||||
note => "obsoleted in 25.10",
|
||||
desc =>
|
||||
?T("This option was renamed to _`sql_schema_multihost`_ in ejabberd 25.10. "
|
||||
"Please update your configuration to use the new option name")}},
|
||||
{update_sql_schema,
|
||||
#{value => "true | false",
|
||||
note => "updated in 24.06",
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
-define(CALL_TIMEOUT, 60*1000). %% 60 seconds
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-record(state, {connection :: pid() | undefined,
|
||||
num :: pos_integer(),
|
||||
@@ -114,9 +114,10 @@ multi(F) ->
|
||||
{error, _} = Err -> Err;
|
||||
Result -> get_result(Result)
|
||||
end
|
||||
catch ?EX_RULE(E, R, St) ->
|
||||
erlang:erase(?TR_STACK),
|
||||
erlang:raise(E, R, ?EX_STACK(St))
|
||||
catch
|
||||
E:R:St ->
|
||||
erlang:erase(?TR_STACK),
|
||||
erlang:raise(E, R, St)
|
||||
end;
|
||||
_ ->
|
||||
erlang:error(nested_transaction)
|
||||
|
||||
@@ -70,7 +70,8 @@
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_router.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
|
||||
-callback init() -> any().
|
||||
-callback register_route(binary(), binary(), local_hint(),
|
||||
@@ -90,11 +91,11 @@ start_link() ->
|
||||
-spec route(stanza()) -> ok.
|
||||
route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end.
|
||||
|
||||
-spec route(jid(), jid(), xmlel() | stanza()) -> ok.
|
||||
|
||||
+11
-10
@@ -32,7 +32,8 @@
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
-include("ejabberd_router.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
@@ -141,13 +142,13 @@ row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) ->
|
||||
local_hint = dec_local_hint(LocalHintS)}]
|
||||
catch _:{bad_node, _} ->
|
||||
[];
|
||||
?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to decode row from 'route' table:~n"
|
||||
"** Row = ~p~n"
|
||||
"** Domain = ~ts~n"
|
||||
"** ~ts",
|
||||
[Row, Domain,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
[]
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to decode row from 'route' table:~n"
|
||||
"** Row = ~p~n"
|
||||
"** Domain = ~ts~n"
|
||||
"** ~ts",
|
||||
[Row,
|
||||
Domain,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
[]
|
||||
end.
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("translate.hrl").
|
||||
|
||||
-define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER, 1).
|
||||
@@ -249,11 +249,11 @@ handle_info({mnesia_system_event, {mnesia_up, Node}}, State) ->
|
||||
{noreply, State};
|
||||
handle_info({route, Packet}, State) ->
|
||||
try route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({'DOWN', _Ref, process, Pid, _Reason}, State) ->
|
||||
|
||||
+9
-8
@@ -92,7 +92,7 @@
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("translate.hrl").
|
||||
|
||||
-callback init() -> ok | {error, any()}.
|
||||
@@ -131,13 +131,14 @@ stop() ->
|
||||
%% @doc route arbitrary term to c2s process(es)
|
||||
route(To, Term) ->
|
||||
try do_route(To, Term), ok
|
||||
catch ?EX_RULE(E, R, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route term to ~ts:~n"
|
||||
"** Term = ~p~n"
|
||||
"** ~ts",
|
||||
[jid:encode(To), Term,
|
||||
misc:format_exception(2, E, R, StackTrace)])
|
||||
catch
|
||||
E:R:StackTrace ->
|
||||
?ERROR_MSG("Failed to route term to ~ts:~n"
|
||||
"** Term = ~p~n"
|
||||
"** ~ts",
|
||||
[jid:encode(To),
|
||||
Term,
|
||||
misc:format_exception(2, E, R, StackTrace)])
|
||||
end.
|
||||
|
||||
-spec route(stanza()) -> ok.
|
||||
|
||||
+26
-21
@@ -41,6 +41,7 @@
|
||||
abort/1,
|
||||
restart/1,
|
||||
use_new_schema/0,
|
||||
use_multihost_schema/0,
|
||||
sql_query_to_iolist/1,
|
||||
sql_query_to_iolist/2,
|
||||
escape/1,
|
||||
@@ -70,6 +71,8 @@
|
||||
-export([connecting/2, connecting/3,
|
||||
session_established/2, session_established/3]).
|
||||
|
||||
-deprecated({use_new_schema, 0}).
|
||||
|
||||
-ifdef(OTP_BELOW_28).
|
||||
-ifdef(OTP_BELOW_26).
|
||||
%% OTP 25 or lower
|
||||
@@ -89,7 +92,7 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-record(state,
|
||||
{db_ref :: undefined | db_ref_pid() | odbc_connection_reference(),
|
||||
@@ -353,8 +356,11 @@ sqlite_file(Host) ->
|
||||
binary_to_list(File)
|
||||
end.
|
||||
|
||||
use_multihost_schema() ->
|
||||
ejabberd_option:sql_schema_multihost().
|
||||
|
||||
use_new_schema() ->
|
||||
ejabberd_option:new_sql_schema().
|
||||
use_multihost_schema().
|
||||
|
||||
-spec get_worker(binary()) -> atom().
|
||||
get_worker(Host) ->
|
||||
@@ -616,19 +622,20 @@ outer_transaction(F, NRestarts, _Reason) ->
|
||||
{atomic, Res}
|
||||
end
|
||||
catch
|
||||
?EX_RULE(throw, {aborted, Reason}, _) when NRestarts > 0 ->
|
||||
maybe_restart_transaction(F, NRestarts, Reason, true);
|
||||
?EX_RULE(throw, {aborted, Reason}, Stack) when NRestarts =:= 0 ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
?ERROR_MSG("SQL transaction restarts exceeded~n** "
|
||||
"Restarts: ~p~n** Last abort reason: "
|
||||
"~p~n** Stacktrace: ~p~n** When State "
|
||||
"== ~p",
|
||||
[?MAX_TRANSACTION_RESTARTS, Reason,
|
||||
StackTrace, get(?STATE_KEY)]),
|
||||
maybe_restart_transaction(F, NRestarts, Reason, true);
|
||||
?EX_RULE(_, Reason, _) ->
|
||||
maybe_restart_transaction(F, 0, Reason, true)
|
||||
throw:{aborted, Reason}:_ when NRestarts > 0 ->
|
||||
maybe_restart_transaction(F, NRestarts, Reason, true);
|
||||
throw:{aborted, Reason}:StackTrace when NRestarts =:= 0 ->
|
||||
?ERROR_MSG("SQL transaction restarts exceeded~n** "
|
||||
"Restarts: ~p~n** Last abort reason: "
|
||||
"~p~n** Stacktrace: ~p~n** When State "
|
||||
"== ~p",
|
||||
[?MAX_TRANSACTION_RESTARTS,
|
||||
Reason,
|
||||
StackTrace,
|
||||
get(?STATE_KEY)]),
|
||||
maybe_restart_transaction(F, NRestarts, Reason, true);
|
||||
_:Reason:_ ->
|
||||
maybe_restart_transaction(F, 0, Reason, true)
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -742,10 +749,9 @@ sql_query_internal(#sql_query{} = Query) ->
|
||||
{error, <<"terminated unexpectedly">>};
|
||||
exit:{shutdown, _} ->
|
||||
{error, <<"shutdown">>};
|
||||
?EX_RULE(Class, Reason, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Internal error while processing SQL query:~n** ~ts",
|
||||
[misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
[misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
{error, <<"internal error">>}
|
||||
end,
|
||||
check_error(Res, Query);
|
||||
@@ -935,12 +941,11 @@ sql_query_format_res({selected, _, Rows}, SQLQuery) ->
|
||||
try
|
||||
[(SQLQuery#sql_query.format_res)(Row)]
|
||||
catch
|
||||
?EX_RULE(Class, Reason, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Error while processing SQL query result:~n"
|
||||
"** Row: ~p~n** ~ts",
|
||||
[Row,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
[]
|
||||
end
|
||||
end, Rows),
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
res_pos = 0,
|
||||
server_host_used = false,
|
||||
used_vars = [],
|
||||
use_new_schema,
|
||||
use_multihost_schema,
|
||||
need_timestamp_pass = false,
|
||||
need_array_pass = false,
|
||||
has_list = false}).
|
||||
@@ -245,13 +245,13 @@ transform_insert(Form, TableArg, FieldsArg) ->
|
||||
parse(S, Loc, UseNewSchema) ->
|
||||
parse1(S, [],
|
||||
#state{loc = Loc,
|
||||
use_new_schema = UseNewSchema}).
|
||||
use_multihost_schema = UseNewSchema}).
|
||||
|
||||
parse(S, ParamPos, Loc, UseNewSchema) ->
|
||||
parse1(S, [],
|
||||
#state{loc = Loc,
|
||||
param_pos = ParamPos,
|
||||
use_new_schema = UseNewSchema}).
|
||||
use_multihost_schema = UseNewSchema}).
|
||||
|
||||
parse1([], Acc, State) ->
|
||||
State1 = append_string(lists:reverse(Acc), State),
|
||||
@@ -300,7 +300,7 @@ parse1([$%, $( | S], Acc, State) ->
|
||||
State3 =
|
||||
State2#state{server_host_used = {true, Name},
|
||||
used_vars = [Name | State2#state.used_vars]},
|
||||
case State#state.use_new_schema of
|
||||
case State#state.use_multihost_schema of
|
||||
true ->
|
||||
Convert =
|
||||
erl_syntax:application(
|
||||
@@ -469,7 +469,7 @@ make_sql_query(State) ->
|
||||
make_sql_query(State, unknown).
|
||||
|
||||
make_sql_query(State, Type) ->
|
||||
Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}),
|
||||
Hash = erlang:phash2(State#state{loc = undefined, use_multihost_schema = true}),
|
||||
SHash = <<"Q", (integer_to_binary(Hash))/binary>>,
|
||||
Query = pack_query(State#state.'query'),
|
||||
Flags = case State#state.has_list of true -> 1; _ -> 0 end,
|
||||
@@ -938,7 +938,7 @@ make_schema_check(New, Old) ->
|
||||
erl_syntax:case_expr(
|
||||
erl_syntax:application(
|
||||
erl_syntax:atom(ejabberd_sql),
|
||||
erl_syntax:atom(use_new_schema),
|
||||
erl_syntax:atom(use_multihost_schema),
|
||||
[]),
|
||||
[erl_syntax:clause(
|
||||
[erl_syntax:abstract(true)],
|
||||
|
||||
+10
-10
@@ -49,7 +49,7 @@ start(Host) ->
|
||||
#sql_schema_info{
|
||||
db_type = DBType,
|
||||
db_version = DBVersion,
|
||||
new_schema = ejabberd_sql:use_new_schema()}
|
||||
multihost_schema = ejabberd_sql:use_multihost_schema()}
|
||||
end),
|
||||
Table = filter_table_sh(SchemaInfo, schema_table()),
|
||||
Res = create_table(Host, SchemaInfo, Table),
|
||||
@@ -268,7 +268,7 @@ table_exists(Host, Table) ->
|
||||
end).
|
||||
|
||||
filter_table_sh(SchemaInfo, Table) ->
|
||||
case {SchemaInfo#sql_schema_info.new_schema, Table#sql_table.name} of
|
||||
case {SchemaInfo#sql_schema_info.multihost_schema, Table#sql_table.name} of
|
||||
{true, _} ->
|
||||
Table;
|
||||
{_, <<"route">>} ->
|
||||
@@ -407,7 +407,7 @@ get_current_version(Host, Module, Schemas) ->
|
||||
|
||||
sqlite_table_copy_t(SchemaInfo, Table) ->
|
||||
TableName = Table#sql_table.name,
|
||||
NewTableName = <<"new_", TableName/binary>>,
|
||||
NewTableName = <<"multihost_", TableName/binary>>,
|
||||
NewTable = Table#sql_table{name = NewTableName},
|
||||
create_table_t(SchemaInfo, NewTable),
|
||||
Columns = lists:join(<<",">>,
|
||||
@@ -777,7 +777,7 @@ should_update_schema(Host) ->
|
||||
end,
|
||||
case ejabberd_option:update_sql_schema() andalso SupportedDB of
|
||||
true ->
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true ->
|
||||
lists:member(sql, ejabberd_option:auth_method(Host));
|
||||
false ->
|
||||
@@ -850,7 +850,7 @@ update_schema(Host, Module, RawSchemas) ->
|
||||
#sql_schema_info{
|
||||
db_type = DBType,
|
||||
db_version = DBVersion,
|
||||
new_schema = ejabberd_sql:use_new_schema()}
|
||||
multihost_schema = ejabberd_sql:use_multihost_schema()}
|
||||
end),
|
||||
Schemas = preprocess_schemas(SchemaInfo, RawSchemas),
|
||||
Version = get_current_version(Host, Module, Schemas),
|
||||
@@ -954,7 +954,7 @@ do_update_schema(Host, Module, SchemaInfo, Schema) ->
|
||||
end;
|
||||
({create_index, TableName, Columns1}) ->
|
||||
Columns =
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true ->
|
||||
Columns1;
|
||||
false ->
|
||||
@@ -1005,7 +1005,7 @@ do_update_schema(Host, Module, SchemaInfo, Schema) ->
|
||||
end;
|
||||
({update_primary_key, TableName, Columns1}) ->
|
||||
Columns =
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true ->
|
||||
Columns1;
|
||||
false ->
|
||||
@@ -1071,7 +1071,7 @@ do_update_schema(Host, Module, SchemaInfo, Schema) ->
|
||||
end;
|
||||
({drop_index, TableName, Columns1}) ->
|
||||
Columns =
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true ->
|
||||
Columns1;
|
||||
false ->
|
||||
@@ -1160,7 +1160,7 @@ print_schema(SDBType, SDBVersion, SNewSchema) ->
|
||||
"false" -> false;
|
||||
"true" -> true;
|
||||
_ ->
|
||||
io:format("new_schema must be one of the following: "
|
||||
io:format("multihost_schema must be one of the following: "
|
||||
"'0', '1', 'false', 'true'~n"),
|
||||
error
|
||||
end,
|
||||
@@ -1172,7 +1172,7 @@ print_schema(SDBType, SDBVersion, SNewSchema) ->
|
||||
#sql_schema_info{
|
||||
db_type = DBType,
|
||||
db_version = DBVersion,
|
||||
new_schema = NewSchema},
|
||||
multihost_schema = NewSchema},
|
||||
Mods = ejabberd_config:beams(all),
|
||||
lists:foreach(
|
||||
fun(Mod) ->
|
||||
|
||||
@@ -196,7 +196,7 @@ check_sqlite_db(Host) ->
|
||||
|
||||
create_sqlite_tables(DB) ->
|
||||
SqlDir = misc:sql_dir(),
|
||||
Filename = case ejabberd_sql:use_new_schema() of
|
||||
Filename = case ejabberd_sql:use_multihost_schema() of
|
||||
true -> "lite.new.sql";
|
||||
false -> "lite.sql"
|
||||
end,
|
||||
|
||||
@@ -2197,7 +2197,10 @@ make_command_result_element(_ArgumentsUsed,
|
||||
|| {ElementName, _ElementFormat} <- TupleElements])]),
|
||||
?XE(<<"tbody">>,
|
||||
[?XE(<<"tr">>,
|
||||
[?XC(<<"td">>, format_result(V, {ElementName, ElementFormat}))
|
||||
[?XE(<<"td">>,
|
||||
[?XAC(<<"span">>,
|
||||
[{<<"style">>, <<"white-space: pre-wrap;">>}],
|
||||
format_result(V, {ElementName, ElementFormat}))])
|
||||
|| {V, {ElementName, ElementFormat}}
|
||||
<- lists:zip(tuple_to_list(Values), TupleElements)])])]);
|
||||
make_command_result_element(ArgumentsUsed,
|
||||
|
||||
+11
-1
@@ -243,6 +243,7 @@ install(Package, Config) when is_binary(Package) ->
|
||||
ok ->
|
||||
code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]),
|
||||
ejabberd_config_reload(Config),
|
||||
maybe_print_module_status(Module),
|
||||
copy_commit_json(Package, Attrs),
|
||||
ModuleRuntime = get_runtime_module_name(Module),
|
||||
case erlang:function_exported(ModuleRuntime, post_install, 0) of
|
||||
@@ -263,6 +264,14 @@ ejabberd_config_reload(Config) when is_list(Config) ->
|
||||
ejabberd_config_reload(undefined) ->
|
||||
ejabberd_config:reload().
|
||||
|
||||
maybe_print_module_status(Module) ->
|
||||
case get_module_status_el(Module) of
|
||||
[_, {xmlcdata, String}] ->
|
||||
io:format("~ts~n", [String]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
uninstall(Module) when is_atom(Module) ->
|
||||
uninstall(misc:atom_to_binary(Module));
|
||||
uninstall(Package) when is_binary(Package) ->
|
||||
@@ -669,7 +678,8 @@ maybe_define_lager_macro() ->
|
||||
end.
|
||||
|
||||
compile_options() ->
|
||||
[verbose, report_errors, report_warnings, debug_info, ?ALL_DEFS]
|
||||
[verbose, report_errors, report_warnings, debug_info, ?ALL_DEFS,
|
||||
{feature, maybe_expr, enable}]
|
||||
++ maybe_define_lager_macro()
|
||||
++ [{i, filename:join(app_dir(App), "include")}
|
||||
|| App <- [fast_xml, xmpp, p1_utils, ejabberd]]
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
-include("logger.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-type component() :: ejabberd_sm | ejabberd_local.
|
||||
|
||||
@@ -111,14 +111,14 @@ process_iq(_Host, Module, Function, IQ) ->
|
||||
ejabberd_router:route(ResIQ);
|
||||
ignore ->
|
||||
ok
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to process iq:~n~ts~n** ~ts",
|
||||
[xmpp:pp(IQ),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
Txt = ?T("Module failed to handle the query"),
|
||||
Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang),
|
||||
ejabberd_router:route_error(IQ, Err)
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to process iq:~n~ts~n** ~ts",
|
||||
[xmpp:pp(IQ),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
Txt = ?T("Module failed to handle the query"),
|
||||
Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang),
|
||||
ejabberd_router:route_error(IQ, Err)
|
||||
end.
|
||||
|
||||
-spec process_iq(module(), atom(), iq()) -> ignore | iq().
|
||||
|
||||
+38
-28
@@ -44,7 +44,7 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
|
||||
-record(ejabberd_module,
|
||||
@@ -187,16 +187,20 @@ start_module(Host, Module, Opts, Order) ->
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
erlang:error({bad_return, Module, Err})
|
||||
end
|
||||
catch ?EX_RULE(Class, Reason, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ErrorText = format_module_error(
|
||||
Module, start, 2,
|
||||
Opts, Class, Reason,
|
||||
StackTrace),
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
maybe_halt_ejabberd(),
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ErrorText = format_module_error(
|
||||
Module,
|
||||
start,
|
||||
2,
|
||||
Opts,
|
||||
Class,
|
||||
Reason,
|
||||
StackTrace),
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
maybe_halt_ejabberd(),
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
end.
|
||||
|
||||
-spec reload_modules(binary()) -> ok.
|
||||
@@ -246,14 +250,18 @@ reload_module(Host, Module, NewOpts, OldOpts, Order) ->
|
||||
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
|
||||
Err -> erlang:error({bad_return, Module, Err})
|
||||
end
|
||||
catch ?EX_RULE(Class, Reason, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
ErrorText = format_module_error(
|
||||
Module, reload, 3,
|
||||
NewOpts, Class, Reason,
|
||||
StackTrace),
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
ErrorText = format_module_error(
|
||||
Module,
|
||||
reload,
|
||||
3,
|
||||
NewOpts,
|
||||
Class,
|
||||
Reason,
|
||||
StackTrace),
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
end;
|
||||
false ->
|
||||
?WARNING_MSG("Module ~ts doesn't support reloading "
|
||||
@@ -346,14 +354,15 @@ prep_stop_module_keep_config(Host, Module) ->
|
||||
try Module:prep_stop(Host) of
|
||||
_ ->
|
||||
ok
|
||||
catch ?EX_RULE(error, undef, _St) ->
|
||||
ok;
|
||||
?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
catch
|
||||
error:undef:_St ->
|
||||
ok;
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to prepare stop module ~ts at ~ts:~n** ~ts",
|
||||
[Module, Host,
|
||||
[Module,
|
||||
Host,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
error
|
||||
error
|
||||
end.
|
||||
|
||||
-spec stop_module_keep_config(binary(), atom()) -> error | ok.
|
||||
@@ -371,12 +380,13 @@ stop_module_keep_config(Host, Module) ->
|
||||
_ ->
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to stop module ~ts at ~ts:~n** ~ts",
|
||||
[Module, Host,
|
||||
[Module,
|
||||
Host,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
error
|
||||
error
|
||||
end.
|
||||
|
||||
-spec add_registrations(binary(), module(), [registration()]) -> ok.
|
||||
|
||||
+5
-7
@@ -126,18 +126,16 @@ json_encode(Term) ->
|
||||
json_decode(Bin) ->
|
||||
jiffy:decode(Bin, [return_maps]).
|
||||
-else.
|
||||
json_encode({[{_Key, _Value} | _]} = Term) ->
|
||||
json_encode({[]}) ->
|
||||
%% Jiffy was able to handle this case, but Json library does not
|
||||
<<"{}">>;
|
||||
json_encode(Term) ->
|
||||
iolist_to_binary(json:encode(Term,
|
||||
fun({Val}, Encoder) when is_list(Val) ->
|
||||
json:encode_key_value_list(Val, Encoder);
|
||||
(Val, Encoder) ->
|
||||
json:encode_value(Val, Encoder)
|
||||
end));
|
||||
json_encode({[]}) ->
|
||||
%% Jiffy was able to handle this case, but Json library does not
|
||||
<<"{}">>;
|
||||
json_encode(Term) ->
|
||||
iolist_to_binary(json:encode(Term)).
|
||||
end)).
|
||||
json_decode(Bin) ->
|
||||
json:decode(Bin).
|
||||
-endif.
|
||||
|
||||
+37
-10
@@ -452,7 +452,17 @@ set_form_api_command(From, Host, CommandNameBin, XData, _Lang) ->
|
||||
Instructions = get_instructions(Def),
|
||||
|
||||
%% Arguments
|
||||
FieldsArgs1 = [Field || Field <- XData#xdata.fields, Field#xdata_field.type /= fixed],
|
||||
FieldsArgs0 = [Field || Field <- XData#xdata.fields, Field#xdata_field.type /= fixed],
|
||||
FieldsArgs1 =
|
||||
lists:map(fun(Arg) ->
|
||||
case Arg#xdata_field.values of
|
||||
[_] ->
|
||||
Arg;
|
||||
_ ->
|
||||
Arg#xdata_field{type = 'text-multi'}
|
||||
end
|
||||
end,
|
||||
FieldsArgs0),
|
||||
|
||||
{Node, FieldsArgs} =
|
||||
case lists:keytake(<<"mod_adhoc_api_target_node">>, #xdata_field.var, FieldsArgs1) of
|
||||
@@ -474,13 +484,14 @@ set_form_api_command(From, Host, CommandNameBin, XData, _Lang) ->
|
||||
Arguments = api_extract_fields(FieldsArgs, Def#ejabberd_commands.args),
|
||||
ApiVersion = mod_adhoc_api_opt:default_version(Host),
|
||||
CallResult =
|
||||
ejabberd_cluster:call(Node,
|
||||
mod_http_api,
|
||||
handle,
|
||||
[binary_to_existing_atom(CommandNameBin, latin1),
|
||||
get_caller_info(From),
|
||||
Arguments,
|
||||
ApiVersion]),
|
||||
execute(Def,
|
||||
[Node,
|
||||
mod_http_api,
|
||||
handle,
|
||||
[binary_to_existing_atom(CommandNameBin, latin1),
|
||||
get_caller_info(From),
|
||||
Arguments,
|
||||
ApiVersion]]),
|
||||
|
||||
%% Command result
|
||||
FieldsResult2 =
|
||||
@@ -515,6 +526,15 @@ set_form_api_command(From, Host, CommandNameBin, XData, _Lang) ->
|
||||
instructions = Instructions,
|
||||
fields = FieldsArgsWithHeads ++ FieldsResultWithHeads}}.
|
||||
|
||||
execute(Def, Arguments) ->
|
||||
case lists:member(async, Def#ejabberd_commands.tags) of
|
||||
true ->
|
||||
spawn(ejabberd_cluster, call, Arguments),
|
||||
{200, 0};
|
||||
false ->
|
||||
apply(ejabberd_cluster, call, Arguments)
|
||||
end.
|
||||
|
||||
api_extract_fields(Fields, ArgsDef) ->
|
||||
lists:map(fun(#xdata_field{values = Values, var = ANameBin}) ->
|
||||
ArgDef = proplists:get_value(binary_to_existing_atom(ANameBin, latin1), ArgsDef),
|
||||
@@ -608,15 +628,22 @@ build_fields(NameTypes, Descs, Examples, Policy, Replacements, Required) ->
|
||||
end,
|
||||
build_fields2(NameTypes2, Descs2, Examples2, Replacements, Required).
|
||||
|
||||
build_fields2([{_ArgName, {list, _ArgNameType}}] = NameTypes,
|
||||
build_fields2([{_ArgName, {list, ArgNameType}}] = NameTypes,
|
||||
Descs,
|
||||
Examples,
|
||||
_Replacements,
|
||||
Required) ->
|
||||
ArgNameType2 =
|
||||
case ArgNameType of
|
||||
{jid, _} ->
|
||||
'jid-multi';
|
||||
{_, _} ->
|
||||
'text-multi'
|
||||
end,
|
||||
Args = lists_zip3_pad(NameTypes, Descs, Examples),
|
||||
lists:map(fun({{AName, AType}, ADesc, AExample}) ->
|
||||
ANameBin = list_to_binary(atom_to_list(AName)),
|
||||
#xdata_field{type = 'text-multi',
|
||||
#xdata_field{type = ArgNameType2,
|
||||
label = ANameBin,
|
||||
desc = list_to_binary(ADesc),
|
||||
values = encode(AExample, AType),
|
||||
|
||||
+44
-8
@@ -51,6 +51,7 @@
|
||||
% Accounts
|
||||
set_password/3, check_password_hash/4, delete_old_users/1,
|
||||
delete_old_users_vhost/2, check_password/3,
|
||||
list_banned/1, count_banned/1,
|
||||
ban_account/3, ban_account_v2/3, get_ban_details/2, unban_account/2,
|
||||
|
||||
% vCard
|
||||
@@ -255,6 +256,28 @@ get_commands_spec() ->
|
||||
result = {res, rescode},
|
||||
result_example = ok},
|
||||
|
||||
#ejabberd_commands{name = list_banned, tags = [accounts],
|
||||
desc = "List banned accounts",
|
||||
longdesc = "The HOST argument can be `all` to query all vhosts.",
|
||||
note = "added in 25.10",
|
||||
module = ?MODULE, function = list_banned,
|
||||
args = [{host, binary}],
|
||||
args_example = [<<"myserver.com">>],
|
||||
args_desc = ["Server name"],
|
||||
result = {banned, {list, {jid, string}}},
|
||||
result_desc = "The list of accounts that are banned",
|
||||
result_example = ["attacker@example.com", "user3@example.com"]},
|
||||
#ejabberd_commands{name = count_banned, tags = [accounts],
|
||||
desc = "Count number of banned accounts",
|
||||
longdesc = "The HOST argument can be `all` to query all vhosts.",
|
||||
note = "added in 25.10",
|
||||
module = ?MODULE, function = count_banned,
|
||||
args = [{host, binary}],
|
||||
args_example = [<<"myserver.com">>],
|
||||
args_desc = ["Server name"],
|
||||
result_example = 6,
|
||||
result_desc = "Number of banned accounts",
|
||||
result = {banned, integer}},
|
||||
#ejabberd_commands{name = ban_account, tags = [accounts],
|
||||
desc = "Ban an account: kick sessions and set random password",
|
||||
longdesc = "This simply sets a random password.",
|
||||
@@ -290,7 +313,7 @@ get_commands_spec() ->
|
||||
note = "added in 24.06",
|
||||
args = [{user, binary}, {host, binary}],
|
||||
args_example = [<<"attacker">>, <<"myserver.com">>],
|
||||
args_desc = ["User name to unban", "Server name"],
|
||||
args_desc = ["Name of a user to check ban information", "Server name"],
|
||||
result = {ban_details, {list,
|
||||
{detail, {tuple, [{name, string},
|
||||
{value, string}
|
||||
@@ -612,6 +635,7 @@ get_commands_spec() ->
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = add_rosteritem, tags = [roster],
|
||||
desc = "Add an item to a user's roster (supports ODBC)",
|
||||
longdesc = "The client will receive a `jabber:iq:roster` IQ notifying them of the added entry.",
|
||||
module = ?MODULE, function = add_rosteritem,
|
||||
version = 1,
|
||||
note = "updated in 24.02",
|
||||
@@ -630,6 +654,7 @@ get_commands_spec() ->
|
||||
%%{"", "will add mike@server.com to peter@localhost roster"},
|
||||
#ejabberd_commands{name = delete_rosteritem, tags = [roster],
|
||||
desc = "Delete an item from a user's roster (supports ODBC)",
|
||||
longdesc = "The client will receive a `jabber:iq:roster` IQ notifying them of the removed entry.",
|
||||
module = ?MODULE, function = delete_rosteritem,
|
||||
args = [{localuser, binary}, {localhost, binary},
|
||||
{user, binary}, {host, binary}],
|
||||
@@ -1151,6 +1176,18 @@ delete_or_not(LUser, LServer, TimeStamp_oldest) ->
|
||||
%%
|
||||
%% Ban account v0
|
||||
|
||||
-define(NS_BANNED, <<"jabber:ejabberd:banned">>).
|
||||
|
||||
list_banned(<<"all">>) ->
|
||||
lists:flatten([list_banned(Host) || Host <- ejabberd_option:hosts()]);
|
||||
list_banned(Host) ->
|
||||
[jid:encode(Jid) || Jid <- mod_private:get_users_with_data(Host, ?NS_BANNED)].
|
||||
|
||||
count_banned(<<"all">>) ->
|
||||
lists:sum([count_banned(Host) || Host <- ejabberd_option:hosts()]);
|
||||
count_banned(Host) ->
|
||||
mod_private:count_users_with_data(Host, ?NS_BANNED).
|
||||
|
||||
ban_account(User, Host, ReasonText) ->
|
||||
Reason = prepare_reason(ReasonText),
|
||||
kick_sessions(User, Host, Reason),
|
||||
@@ -1222,7 +1259,7 @@ get_hash_value(User, Host) ->
|
||||
|
||||
build_ban_xmlel(Reason, {LastDate, LastReason}, BanDate, Hash) ->
|
||||
#xmlel{name = <<"banned">>,
|
||||
attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}],
|
||||
attrs = [{<<"xmlns">>, ?NS_BANNED}],
|
||||
children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]},
|
||||
#xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]},
|
||||
#xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]},
|
||||
@@ -1234,7 +1271,7 @@ build_ban_xmlel(Reason, {LastDate, LastReason}, BanDate, Hash) ->
|
||||
%% Get ban details
|
||||
|
||||
get_ban_details(User, Host) ->
|
||||
case private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>) of
|
||||
case private_get2(User, Host, <<"banned">>, ?NS_BANNED) of
|
||||
[El] ->
|
||||
get_ban_details(User, Host, El);
|
||||
[] ->
|
||||
@@ -1286,11 +1323,8 @@ unban_account(User, Host) ->
|
||||
end.
|
||||
|
||||
unban_account2(User, Host) ->
|
||||
UnBanPrivateXml = build_unban_xmlel(),
|
||||
private_set2(User, Host, UnBanPrivateXml).
|
||||
|
||||
build_unban_xmlel() ->
|
||||
#xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}]}.
|
||||
mod_private:del_data(jid:nodeprep(User), jid:nameprep(Host), ?NS_BANNED),
|
||||
ok.
|
||||
|
||||
%%%
|
||||
%%% Sessions
|
||||
@@ -1356,6 +1390,8 @@ get_status_list(Host, Status_required) ->
|
||||
Fstatus = case Status_required of
|
||||
<<"all">> ->
|
||||
fun(_, _) -> true end;
|
||||
StatusList when is_list(StatusList) ->
|
||||
fun(A, B) -> lists:member(A, B) end;
|
||||
_ ->
|
||||
fun(A, B) -> A == B end
|
||||
end,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_admin_update_sql.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Convert SQL DB to the new format
|
||||
%%% Purpose : Convert the SQL database from singlehost to multihost
|
||||
%%% Created : 9 Aug 2017 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
@@ -65,7 +65,7 @@ depends(_Host, _Opts) ->
|
||||
|
||||
get_commands_spec() ->
|
||||
[#ejabberd_commands{name = update_sql, tags = [sql],
|
||||
desc = "Convert MS SQL, MySQL or PostgreSQL DB to the new format",
|
||||
desc = "Convert SQL database from singlehost to multihost (MS SQL, MySQL, PostgreSQL)",
|
||||
note = "improved in 23.04",
|
||||
module = ?MODULE, function = update_sql,
|
||||
args = [],
|
||||
@@ -119,11 +119,11 @@ update_sql(Host) ->
|
||||
end.
|
||||
|
||||
check_config() ->
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true -> ok;
|
||||
false ->
|
||||
ejabberd_config:set_option(new_sql_schema, true),
|
||||
io:format('~nNOTE: you must add "new_sql_schema: true" to ejabberd.yml before next restart~n~n', [])
|
||||
ejabberd_config:set_option(sql_schema_multihost, true),
|
||||
io:format('~nNOTE: you must add "sql_schema_multihost: true" to ejabberd.yml before next restart~n~n', [])
|
||||
end.
|
||||
|
||||
update_tables(State) ->
|
||||
@@ -150,8 +150,8 @@ update_tables(State) ->
|
||||
drop_index(State, "rosterusers", "i_rosteru_user_jid"),
|
||||
drop_index(State, "rosterusers", "i_rosteru_username"),
|
||||
drop_index(State, "rosterusers", "i_rosteru_jid"),
|
||||
create_unique_index(State, "rosterusers", "i_rosteru_sh_user_jid", ["server_host", "username", "jid"]),
|
||||
create_index(State, "rosterusers", "i_rosteru_sh_jid", ["server_host", "jid"]),
|
||||
create_unique_index(State, "rosterusers", "i_rosteru_server_host_user_jid", ["server_host", "username", "jid"]),
|
||||
create_index(State, "rosterusers", "i_rosteru_server_host_jid", ["server_host", "jid"]),
|
||||
drop_sh_default(State, "rosterusers");
|
||||
false ->
|
||||
ok
|
||||
@@ -160,7 +160,7 @@ update_tables(State) ->
|
||||
case add_sh_column(State, "rostergroups") of
|
||||
true ->
|
||||
drop_index(State, "rostergroups", "pk_rosterg_user_jid"),
|
||||
create_index(State, "rostergroups", "i_rosterg_sh_user_jid", ["server_host", "username", "jid"]),
|
||||
create_index(State, "rostergroups", "i_rosterg_server_host_user_jid", ["server_host", "username", "jid"]),
|
||||
drop_sh_default(State, "rostergroups");
|
||||
false ->
|
||||
ok
|
||||
@@ -169,7 +169,7 @@ update_tables(State) ->
|
||||
case add_sh_column(State, "sr_group") of
|
||||
true ->
|
||||
drop_index(State, "sr_group", "i_sr_group_name"),
|
||||
create_unique_index(State, "sr_group", "i_sr_group_sh_name", ["server_host", "name"]),
|
||||
create_unique_index(State, "sr_group", "i_sr_group_server_host_name", ["server_host", "name"]),
|
||||
drop_sh_default(State, "sr_group");
|
||||
false ->
|
||||
ok
|
||||
@@ -180,8 +180,8 @@ update_tables(State) ->
|
||||
drop_index(State, "sr_user", "i_sr_user_jid_grp"),
|
||||
drop_index(State, "sr_user", "i_sr_user_jid"),
|
||||
drop_index(State, "sr_user", "i_sr_user_grp"),
|
||||
create_unique_index(State, "sr_user", "i_sr_user_sh_jid_grp", ["server_host", "jid", "grp"]),
|
||||
create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]),
|
||||
create_unique_index(State, "sr_user", "i_sr_user_server_host_jid_grp", ["server_host", "jid", "grp"]),
|
||||
create_index(State, "sr_user", "i_sr_user_server_host_grp", ["server_host", "grp"]),
|
||||
drop_sh_default(State, "sr_user");
|
||||
false ->
|
||||
ok
|
||||
@@ -190,7 +190,7 @@ update_tables(State) ->
|
||||
case add_sh_column(State, "spool") of
|
||||
true ->
|
||||
drop_index(State, "spool", "i_despool"),
|
||||
create_index(State, "spool", "i_spool_sh_username", ["server_host", "username"]),
|
||||
create_index(State, "spool", "i_spool_server_host_username", ["server_host", "username"]),
|
||||
drop_sh_default(State, "spool");
|
||||
false ->
|
||||
ok
|
||||
@@ -205,10 +205,10 @@ update_tables(State) ->
|
||||
drop_index(State, "archive", "i_bare_peer"),
|
||||
drop_index(State, "archive", "i_username_peer"),
|
||||
drop_index(State, "archive", "i_username_bare_peer"),
|
||||
create_index(State, "archive", "i_archive_sh_username_timestamp", ["server_host", "username", "timestamp"]),
|
||||
create_index(State, "archive", "i_archive_sh_timestamp", ["server_host", "timestamp"]),
|
||||
create_index(State, "archive", "i_archive_sh_username_peer", ["server_host", "username", "peer"]),
|
||||
create_index(State, "archive", "i_archive_sh_username_bare_peer", ["server_host", "username", "bare_peer"]),
|
||||
create_index(State, "archive", "i_archive_server_host_username_timestamp", ["server_host", "username", "timestamp"]),
|
||||
create_index(State, "archive", "i_archive_server_host_timestamp", ["server_host", "timestamp"]),
|
||||
create_index(State, "archive", "i_archive_server_host_username_peer", ["server_host", "username", "peer"]),
|
||||
create_index(State, "archive", "i_archive_server_host_username_bare_peer", ["server_host", "username", "bare_peer"]),
|
||||
drop_sh_default(State, "archive");
|
||||
false ->
|
||||
ok
|
||||
@@ -247,17 +247,17 @@ update_tables(State) ->
|
||||
drop_index(State, "vcard_search", "i_vcard_search_lorgname"),
|
||||
drop_index(State, "vcard_search", "i_vcard_search_lorgunit"),
|
||||
add_pkey(State, "vcard_search", ["server_host", "lusername"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lfn", ["server_host", "lfn"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lfamily", ["server_host", "lfamily"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lgiven", ["server_host", "lgiven"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lmiddle", ["server_host", "lmiddle"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lnickname", ["server_host", "lnickname"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lbday", ["server_host", "lbday"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lctry", ["server_host", "lctry"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_llocality", ["server_host", "llocality"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lemail", ["server_host", "lemail"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lorgname", ["server_host", "lorgname"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_sh_lorgunit", ["server_host", "lorgunit"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lfn", ["server_host", "lfn"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lfamily", ["server_host", "lfamily"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lgiven", ["server_host", "lgiven"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lmiddle", ["server_host", "lmiddle"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lnickname", ["server_host", "lnickname"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lbday", ["server_host", "lbday"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lctry", ["server_host", "lctry"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_llocality", ["server_host", "llocality"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lemail", ["server_host", "lemail"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lorgname", ["server_host", "lorgname"]),
|
||||
create_index(State, "vcard_search", "i_vcard_search_server_host_lorgunit", ["server_host", "lorgunit"]),
|
||||
drop_sh_default(State, "vcard_search");
|
||||
false ->
|
||||
ok
|
||||
@@ -276,7 +276,7 @@ update_tables(State) ->
|
||||
true ->
|
||||
drop_index(State, "privacy_list", "i_privacy_list_username"),
|
||||
drop_index(State, "privacy_list", "i_privacy_list_username_name"),
|
||||
create_unique_index(State, "privacy_list", "i_privacy_list_sh_username_name", ["server_host", "username", "name"]),
|
||||
create_unique_index(State, "privacy_list", "i_privacy_list_server_host_username_name", ["server_host", "username", "name"]),
|
||||
drop_sh_default(State, "privacy_list");
|
||||
false ->
|
||||
ok
|
||||
@@ -345,7 +345,7 @@ update_tables(State) ->
|
||||
drop_index(State, "sm", "i_sm_username"),
|
||||
drop_pkey(State, "sm"),
|
||||
add_pkey(State, "sm", ["usec", "pid"]),
|
||||
create_index(State, "sm", "i_sm_sh_username", ["server_host", "username"]),
|
||||
create_index(State, "sm", "i_sm_server_host_username", ["server_host", "username"]),
|
||||
drop_sh_default(State, "sm");
|
||||
false ->
|
||||
ok
|
||||
@@ -356,7 +356,7 @@ update_tables(State) ->
|
||||
drop_index(State, "push_session", "i_push_usn"),
|
||||
drop_index(State, "push_session", "i_push_ut"),
|
||||
create_unique_index(State, "push_session", "i_push_session_susn", ["server_host", "username", "service", "node"]),
|
||||
create_index(State, "push_session", "i_push_session_sh_username_timestamp", ["server_host", "username", "timestamp"]),
|
||||
create_index(State, "push_session", "i_push_session_server_host_username_timestamp", ["server_host", "username", "timestamp"]),
|
||||
drop_sh_default(State, "push_session");
|
||||
false ->
|
||||
ok
|
||||
@@ -523,7 +523,7 @@ create_unique_index(#state{dbtype = pgsql} = State, Table, Index, Cols) ->
|
||||
State#state.host,
|
||||
["CREATE UNIQUE INDEX ", Index, " ON ", Table, " USING btree (",
|
||||
SCols, ");"]);
|
||||
create_unique_index(#state{dbtype = mssql} = State, Table, "i_privacy_list_sh_username_name" = Index, Cols) ->
|
||||
create_unique_index(#state{dbtype = mssql} = State, Table, "i_privacy_list_server_host_username_name" = Index, Cols) ->
|
||||
create_index(State, Table, Index, Cols);
|
||||
create_unique_index(#state{dbtype = mssql} = State, Table, Index, Cols) ->
|
||||
SCols = string:join(Cols, ", "),
|
||||
@@ -581,10 +581,10 @@ old_index_name(mssql, "i_sr_user_jid_grp") -> "sr_user_jid_group";
|
||||
old_index_name(mssql, Index) -> string:substr(Index, 3);
|
||||
old_index_name(_Type, Index) -> Index.
|
||||
|
||||
new_index_name(mssql, "i_rosterg_sh_user_jid") -> "rostergroups_sh_username_jid";
|
||||
new_index_name(mssql, "i_rosteru_sh_jid") -> "rosterusers_sh_jid";
|
||||
new_index_name(mssql, "i_rosteru_sh_user_jid") -> "rosterusers_sh_username_jid";
|
||||
new_index_name(mssql, "i_sr_user_sh_jid_grp") -> "sr_user_sh_jid_group";
|
||||
new_index_name(mssql, "i_rosterg_server_host_user_jid") -> "rostergroups_server_host_username_jid";
|
||||
new_index_name(mssql, "i_rosteru_server_host_jid") -> "rosterusers_server_host_jid";
|
||||
new_index_name(mssql, "i_rosteru_server_host_user_jid") -> "rosterusers_server_host_username_jid";
|
||||
new_index_name(mssql, "i_sr_user_server_host_jid_grp") -> "sr_user_server_host_jid_group";
|
||||
new_index_name(mssql, Index) -> string:substr(Index, 3);
|
||||
new_index_name(_Type, Index) -> Index.
|
||||
|
||||
@@ -617,9 +617,9 @@ mysql_keylen(_, "pid") -> "(75)";
|
||||
mysql_keylen(_, "server_host") -> "(191)";
|
||||
mysql_keylen(_, "service") -> "(191)";
|
||||
mysql_keylen(_, "topic") -> "(191)";
|
||||
mysql_keylen("i_privacy_list_sh_username_name", "username") -> "(75)";
|
||||
mysql_keylen("i_rosterg_sh_user_jid", "username") -> "(75)";
|
||||
mysql_keylen("i_rosteru_sh_user_jid", "username") -> "(75)";
|
||||
mysql_keylen("i_privacy_list_server_host_username_name", "username") -> "(75)";
|
||||
mysql_keylen("i_rosterg_server_host_user_jid", "username") -> "(75)";
|
||||
mysql_keylen("i_rosteru_server_host_user_jid", "username") -> "(75)";
|
||||
mysql_keylen(_, "username") -> "(191)";
|
||||
mysql_keylen(_, _) -> "".
|
||||
|
||||
|
||||
+216
-14
@@ -23,6 +23,8 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%; definitions
|
||||
|
||||
%%% Implements a small subset of XEP-0133: Service Administration
|
||||
%%% Version 1.1 (2005-08-19)
|
||||
|
||||
@@ -49,9 +51,24 @@
|
||||
announce_all_hosts_motd_update/1,
|
||||
announce_motd_delete/1,
|
||||
announce_all_hosts_motd_delete/1]).
|
||||
%% ejabberd_commands
|
||||
-export([announce_send_all/3,
|
||||
announce_send_online/3,
|
||||
get_stored_motd/1,
|
||||
announce_motd_set_online/3,
|
||||
announce_motd_update/3,
|
||||
announce_motd_delete_api/1,
|
||||
get_commands_spec/0]).
|
||||
%% WebAdmin
|
||||
-export([webadmin_menu/3, webadmin_page/3]).
|
||||
|
||||
-import(ejabberd_web_admin, [make_command/4, make_command/2]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
-include("mod_announce.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
@@ -77,12 +94,28 @@
|
||||
tokenize(Node) -> str:tokens(Node, <<"/#">>).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_mod callbacks
|
||||
%%====================================================================
|
||||
%%; gen_mod callbacks
|
||||
|
||||
start(Host, Opts) ->
|
||||
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
|
||||
false ->
|
||||
ejabberd_commands:register_commands(?MODULE, get_commands_spec());
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, webadmin_menu, 50),
|
||||
ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, webadmin_page, 50),
|
||||
gen_mod:start_child(?MODULE, Host, Opts).
|
||||
|
||||
stop(Host) ->
|
||||
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
|
||||
false ->
|
||||
ejabberd_commands:unregister_commands(get_commands_spec());
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
ejabberd_hooks:delete(webadmin_menu_host, Host, ?MODULE, webadmin_menu, 50),
|
||||
ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, webadmin_page, 50),
|
||||
gen_mod:stop_child(?MODULE, Host).
|
||||
|
||||
reload(Host, NewOpts, OldOpts) ->
|
||||
@@ -99,8 +132,8 @@ depends(_Host, _Opts) ->
|
||||
[{mod_adhoc, hard}].
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
%%; gen_server callbacks
|
||||
|
||||
init([Host|_]) ->
|
||||
process_flag(trap_exit, true),
|
||||
Opts = gen_mod:get_module_opts(Host, ?MODULE),
|
||||
@@ -165,7 +198,9 @@ terminate(_Reason, #state{host = Host}) ->
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%% Announcing via messages to a custom resource
|
||||
%%====================================================================
|
||||
%%; Announcing via messages to a custom resource
|
||||
|
||||
-spec announce(stanza()) -> ok | stop.
|
||||
announce(#message{to = #jid{luser = <<>>} = To} = Packet) ->
|
||||
Proc = gen_mod:get_module_proc(To#jid.lserver, ?MODULE),
|
||||
@@ -200,8 +235,12 @@ announce(#message{to = #jid{luser = <<>>} = To} = Packet) ->
|
||||
announce(_Packet) ->
|
||||
ok.
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
%% Announcing via ad-hoc commands
|
||||
%%====================================================================
|
||||
%%; Announcing via ad-hoc commands
|
||||
|
||||
%%====================================================================
|
||||
%%; -- disco identity
|
||||
|
||||
-define(INFO_COMMAND(Lang, Node),
|
||||
[#identity{category = <<"automation">>,
|
||||
type = <<"command-node">>,
|
||||
@@ -234,7 +273,8 @@ disco_identity(Acc, _From, _To, Node, Lang) ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
%%====================================================================
|
||||
%%; -- disco features
|
||||
|
||||
-define(INFO_RESULT(Allow, Feats, Lang),
|
||||
case Allow of
|
||||
@@ -296,7 +336,9 @@ disco_features(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
%%====================================================================
|
||||
%%; -- disco items
|
||||
|
||||
-define(NODE_TO_ITEM(Lang, Server, Node),
|
||||
#disco_item{jid = jid:make(Server),
|
||||
node = Node,
|
||||
@@ -374,7 +416,9 @@ disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, Lang) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
%%====================================================================
|
||||
%%; -- announce items
|
||||
|
||||
-spec announce_items(empty | {error, stanza_error()} | {result, [disco_item()]},
|
||||
jid(), jid(), binary()) -> {error, stanza_error()} |
|
||||
{result, [disco_item()]} |
|
||||
@@ -413,7 +457,8 @@ announce_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, Lang)
|
||||
{result, Items ++ Nodes1 ++ Nodes2}
|
||||
end.
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
%%====================================================================
|
||||
%%; -- commands
|
||||
|
||||
commands_result(Allow, From, To, Request) ->
|
||||
case Allow of
|
||||
@@ -500,6 +545,9 @@ vvaluel(Val) ->
|
||||
_ -> [Val]
|
||||
end.
|
||||
|
||||
%%====================================================================
|
||||
%%; -- adhoc form
|
||||
|
||||
generate_adhoc_form(Lang, Node, ServerHost) ->
|
||||
LNode = tokenize(Node),
|
||||
{OldSubject, OldBody} = if (LNode == ?NS_ADMINL("edit-motd"))
|
||||
@@ -623,7 +671,8 @@ get_title(Lang, ?NS_ADMIN_DELETE_MOTD) ->
|
||||
get_title(Lang, ?NS_ADMIN_DELETE_MOTD_ALLHOSTS) ->
|
||||
translate:translate(Lang, ?T("Delete message of the day on all hosts")).
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
%%====================================================================
|
||||
%%; -- ad-hoc commands implementation
|
||||
|
||||
announce_all(#message{to = To} = Packet) ->
|
||||
Local = jid:make(To#jid.server),
|
||||
@@ -841,6 +890,151 @@ route_forbidden_error(Packet) ->
|
||||
Err = xmpp:err_forbidden(?T("Access denied by service policy"), Lang),
|
||||
ejabberd_router:route_error(Packet, Err).
|
||||
|
||||
|
||||
%%====================================================================
|
||||
%%; Announcing via API commands
|
||||
|
||||
%% @format-begin
|
||||
|
||||
-spec get_commands_spec() -> [ejabberd_commands()].
|
||||
get_commands_spec() ->
|
||||
HostAll = "If HOST is `all`, send to all hosts. ",
|
||||
BodyNew = "You can use ' \\n ' in the message body to write a newline.",
|
||||
[#ejabberd_commands{name = announce_send_all,
|
||||
tags = [announce],
|
||||
desc = "Send announcement to all users",
|
||||
longdesc = HostAll ++ BodyNew,
|
||||
module = ?MODULE,
|
||||
function = announce_send_all,
|
||||
note = "added in 25.10",
|
||||
args = [{host, binary}, {subject, binary}, {body, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = announce_send_online,
|
||||
tags = [announce],
|
||||
desc = "Send announcement to online users",
|
||||
longdesc = HostAll ++ BodyNew,
|
||||
module = ?MODULE,
|
||||
function = announce_send_online,
|
||||
note = "added in 25.10",
|
||||
args = [{host, binary}, {subject, binary}, {body, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = announce_motd_get,
|
||||
tags = [announce],
|
||||
desc = "Get Message Of The Day",
|
||||
longdesc = BodyNew,
|
||||
module = ?MODULE,
|
||||
function = get_stored_motd,
|
||||
note = "added in 25.10",
|
||||
args = [{host, binary}],
|
||||
result = {motd, {tuple, [{subject, string}, {body, string}]}}},
|
||||
#ejabberd_commands{name = announce_motd_set_online,
|
||||
tags = [announce],
|
||||
desc = "Set Message Of The Day and send to online users",
|
||||
longdesc = HostAll ++ BodyNew,
|
||||
module = ?MODULE,
|
||||
function = announce_motd_set_online,
|
||||
note = "added in 25.10",
|
||||
args = [{host, binary}, {subject, binary}, {body, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = announce_motd_update,
|
||||
tags = [announce],
|
||||
desc = "Update Message Of The Day",
|
||||
longdesc = HostAll ++ BodyNew,
|
||||
module = ?MODULE,
|
||||
function = announce_motd_update,
|
||||
note = "added in 25.10",
|
||||
args = [{host, binary}, {subject, binary}, {body, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = announce_motd_delete,
|
||||
tags = [announce],
|
||||
desc = "Delete Message Of The Day",
|
||||
longdesc = HostAll,
|
||||
module = ?MODULE,
|
||||
function = announce_motd_delete_api,
|
||||
note = "added in 25.10",
|
||||
args = [{host, binary}],
|
||||
result = {res, rescode}}].
|
||||
|
||||
announce_send_all(<<"all">>, Subject, Body) ->
|
||||
Host = hd(ejabberd_option:hosts()),
|
||||
announce_all_hosts_all(make_packet(Host, Body, Subject));
|
||||
announce_send_all(Host, Subject, Body) ->
|
||||
announce_all(make_packet(Host, Body, Subject)).
|
||||
|
||||
announce_send_online(<<"all">>, Subject, Body) ->
|
||||
Host = hd(ejabberd_option:hosts()),
|
||||
announce_all_hosts_online(make_packet(Host, Body, Subject));
|
||||
announce_send_online(Host, Subject, Body) ->
|
||||
announce_online(make_packet(Host, Body, Subject)).
|
||||
|
||||
announce_motd_set_online(<<"all">>, Subject, Body) ->
|
||||
Host = hd(ejabberd_option:hosts()),
|
||||
announce_all_hosts_motd(make_packet(Host, Body, Subject));
|
||||
announce_motd_set_online(Host, Subject, Body) ->
|
||||
announce_motd(make_packet(Host, Body, Subject)).
|
||||
|
||||
announce_motd_update(<<"all">>, Subject, Body) ->
|
||||
Host = hd(ejabberd_option:hosts()),
|
||||
[{ok, _} | _] = announce_all_hosts_motd_update(make_packet(Host, Body, Subject)),
|
||||
ok;
|
||||
announce_motd_update(Host, Subject, Body) ->
|
||||
{ok, _} = announce_motd_update(make_packet(Host, Body, Subject)),
|
||||
ok.
|
||||
|
||||
announce_motd_delete_api(<<"all">>) ->
|
||||
Host = hd(ejabberd_option:hosts()),
|
||||
announce_all_hosts_motd_delete(make_packet(Host));
|
||||
announce_motd_delete_api(Host) ->
|
||||
announce_motd_delete(make_packet(Host)).
|
||||
|
||||
make_packet(Host) ->
|
||||
From = To = jid:make(Host),
|
||||
#message{from = From, to = To}.
|
||||
|
||||
make_packet(Host, Body, Subject) ->
|
||||
From = To = jid:make(Host),
|
||||
Body2 = binary:replace(Body, <<"\\n">>, <<"\n">>, [global]),
|
||||
#message{from = From,
|
||||
to = To,
|
||||
type = headline,
|
||||
body = xmpp:mk_text(Body2),
|
||||
subject = xmpp:mk_text(Subject)}.
|
||||
|
||||
%%====================================================================
|
||||
%%; Announcing via WebAdmin
|
||||
|
||||
webadmin_menu(Acc, _Host, Lang) ->
|
||||
[{<<"announce">>, translate:translate(Lang, ?T("Announcements"))} | Acc].
|
||||
|
||||
webadmin_page(_,
|
||||
Host,
|
||||
#request{us = _US,
|
||||
path = [<<"announce">>],
|
||||
lang = Lang} =
|
||||
R) ->
|
||||
PageTitle = translate:translate(Lang, ?T("Announcements")),
|
||||
Head = ?H1GL(PageTitle, <<"modules/#mod_announce">>, <<"mod_announce">>),
|
||||
Ann = [?X(<<"hr">>),
|
||||
?XE(<<"blockquote">>,
|
||||
[make_command(announce_send_all, R, [{<<"host">>, Host}], []),
|
||||
make_command(announce_send_online, R, [{<<"host">>, Host}], [])])],
|
||||
SetMotd =
|
||||
[make_command(announce_motd_set_online, R, [{<<"host">>, Host}], []),
|
||||
make_command(announce_motd_update, R, [{<<"host">>, Host}], []),
|
||||
make_command(announce_motd_delete, R, [{<<"host">>, Host}], [{style, danger}])],
|
||||
GetMotd = [make_command(announce_motd_get, R, [{<<"host">>, Host}], [])],
|
||||
Motd =
|
||||
[?X(<<"hr">>),
|
||||
?XAC(<<"h2">>, [{<<"id">>, <<"motd">>}], <<"Message Of The Day">>),
|
||||
?XE(<<"blockquote">>, GetMotd ++ SetMotd)],
|
||||
{stop, Head ++ Ann ++ Motd};
|
||||
webadmin_page(Acc, _, _) ->
|
||||
Acc.
|
||||
%% @format-end
|
||||
|
||||
%%====================================================================
|
||||
%%; Cache management
|
||||
|
||||
-spec init_cache(module(), binary(), gen_mod:opts()) -> ok.
|
||||
init_cache(Mod, Host, Opts) ->
|
||||
case use_cache(Mod, Host) of
|
||||
@@ -878,7 +1072,10 @@ clean_cache(LServer) ->
|
||||
?MOTD_CACHE,
|
||||
fun({_, S}, _) -> S /= LServer end).
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
|
||||
%%====================================================================
|
||||
%%; ejd2sql callbacks
|
||||
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
@@ -894,6 +1091,9 @@ import(LServer, {sql, _}, DBType, Tab, List) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, Tab, List).
|
||||
|
||||
%%====================================================================
|
||||
%%; Options and Documentation
|
||||
|
||||
mod_opt_type(access) ->
|
||||
econf:acl();
|
||||
mod_opt_type(db_type) ->
|
||||
@@ -921,7 +1121,7 @@ mod_doc() ->
|
||||
"announcements and to set the message of the day (MOTD). "
|
||||
"Configured users can perform these actions with an XMPP "
|
||||
"client either using Ad-Hoc Commands or sending messages "
|
||||
"to specific JIDs."), "",
|
||||
"to specific JIDs. Equivalent API commands are also available."), "",
|
||||
?T("NOTE: This module can be resource intensive on large "
|
||||
"deployments as it may broadcast a lot of messages. This module "
|
||||
"should be disabled for instances of ejabberd with hundreds of "
|
||||
@@ -984,3 +1184,5 @@ mod_doc() ->
|
||||
#{value => "timeout()",
|
||||
desc =>
|
||||
?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}.
|
||||
|
||||
%%% vim: set foldmethod=marker foldmarker=%%;,%%=:
|
||||
|
||||
@@ -281,9 +281,9 @@ mod_doc() ->
|
||||
#{value => ?T("AccessName"),
|
||||
desc =>
|
||||
?T("The option is supposed to be used when 'allow_local_users' "
|
||||
"and 'allow_transports' are not enough. It's an ACL where "
|
||||
"'deny' means the message will be rejected (or a CAPTCHA "
|
||||
"would be generated for a presence, if configured), and "
|
||||
"and 'allow_transports' are not enough. It's an Access Rule where "
|
||||
"'deny' means the stanza will be rejected; there's an exception "
|
||||
"if option 'captcha' is configured. And "
|
||||
"'allow' means the sender is whitelisted and the stanza "
|
||||
"will pass through. The default value is 'none', which "
|
||||
"means nothing is whitelisted.")}},
|
||||
@@ -314,8 +314,8 @@ mod_doc() ->
|
||||
{captcha,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Whether to generate CAPTCHA or not in response to "
|
||||
"messages from strangers. See also section "
|
||||
"_`basic.md#captcha|CAPTCHA`_"
|
||||
?T("Whether to generate CAPTCHA challenges in response to "
|
||||
"incoming presence subscription requests from strangers. "
|
||||
"See also section _`basic.md#captcha|CAPTCHA`_"
|
||||
" of the Configuration Guide. "
|
||||
"The default value is 'false'.")}}]}.
|
||||
|
||||
+331
-39
@@ -27,7 +27,7 @@
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 133, '1.3.0', '13.10', "partial", ""}).
|
||||
-protocol({xep, 133, '1.3.1', '13.10', "partial", ""}).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
@@ -136,24 +136,48 @@ get_local_identity(Acc, _From, _To, Node, Lang) ->
|
||||
?INFO_COMMAND(?T("Add User"), Lang);
|
||||
?NS_ADMINL(<<"delete-user">>) ->
|
||||
?INFO_COMMAND(?T("Delete User"), Lang);
|
||||
?NS_ADMINL(<<"disable-user">>) ->
|
||||
?INFO_COMMAND(?T("Disable User"), Lang);
|
||||
?NS_ADMINL(<<"reenable-user">>) ->
|
||||
?INFO_COMMAND(?T("Re-Enable User"), Lang);
|
||||
?NS_ADMINL(<<"end-user-session">>) ->
|
||||
?INFO_COMMAND(?T("End User Session"), Lang);
|
||||
?NS_ADMINL(<<"change-user-password">>) ->
|
||||
?INFO_COMMAND(?T("Change User Password"), Lang);
|
||||
?NS_ADMINL(<<"get-user-roster">>) ->
|
||||
?INFO_COMMAND(?T("Get User Roster"), Lang);
|
||||
?NS_ADMINL(<<"get-user-lastlogin">>) ->
|
||||
?INFO_COMMAND(?T("Get User Last Login Time"), Lang);
|
||||
?NS_ADMINL(<<"user-stats">>) ->
|
||||
?INFO_COMMAND(?T("Get User Statistics"), Lang);
|
||||
?NS_ADMINL(<<"get-registered-users-list">>) ->
|
||||
?INFO_COMMAND(?T("Get List of Registered Users"),
|
||||
Lang);
|
||||
?NS_ADMINL(<<"get-registered-users-num">>) ->
|
||||
?INFO_COMMAND(?T("Get Number of Registered Users"),
|
||||
Lang);
|
||||
?NS_ADMINL(<<"get-online-users-list">>) ->
|
||||
?INFO_COMMAND(?T("Get List of Online Users"), Lang);
|
||||
?NS_ADMINL(<<"get-disabled-users-num">>) ->
|
||||
?INFO_COMMAND(?T("Get Number of Disabled Users"),
|
||||
Lang);
|
||||
?NS_ADMINL(<<"get-online-users-num">>) ->
|
||||
?INFO_COMMAND(?T("Get Number of Online Users"), Lang);
|
||||
?NS_ADMINL(<<"get-active-users-num">>) ->
|
||||
?INFO_COMMAND(?T("Get Number of Active Users"), Lang);
|
||||
?NS_ADMINL(<<"get-idle-users-num">>) ->
|
||||
?INFO_COMMAND(?T("Get Number of Idle Users"), Lang);
|
||||
?NS_ADMINL(<<"get-registered-users-list">>) ->
|
||||
?INFO_COMMAND(?T("Get List of Registered Users"),
|
||||
Lang);
|
||||
?NS_ADMINL(<<"get-disabled-users-list">>) ->
|
||||
?INFO_COMMAND(?T("Get List of Disabled Users"),
|
||||
Lang);
|
||||
?NS_ADMINL(<<"get-online-users-list">>) ->
|
||||
?INFO_COMMAND(?T("Get List of Online Users"), Lang);
|
||||
?NS_ADMINL(<<"get-active-users">>) ->
|
||||
?INFO_COMMAND(?T("Get List of Active Users"), Lang);
|
||||
?NS_ADMINL(<<"get-idle-users">>) ->
|
||||
?INFO_COMMAND(?T("Get List of Idle Users"), Lang);
|
||||
?NS_ADMINL(<<"restart">>) ->
|
||||
?INFO_COMMAND(?T("Restart Service"), Lang);
|
||||
?NS_ADMINL(<<"shutdown">>) ->
|
||||
?INFO_COMMAND(?T("Shut Down Service"), Lang);
|
||||
_ -> Acc
|
||||
end.
|
||||
|
||||
@@ -220,21 +244,43 @@ get_local_features(Acc, From,
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"delete-user">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"disable-user">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"reenable-user">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"end-user-session">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"change-user-password">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-user-roster">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-user-lastlogin">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"user-stats">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-registered-users-num">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-disabled-users-num">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-online-users-num">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-active-users-num">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-idle-users-num">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-registered-users-list">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-registered-users-num">>) ->
|
||||
?NS_ADMINL(<<"get-disabled-users-list">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-online-users-list">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-online-users-num">>) ->
|
||||
?NS_ADMINL(<<"get-active-users">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"get-idle-users">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"restart">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
?NS_ADMINL(<<"shutdown">>) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
||||
_ -> Acc
|
||||
end
|
||||
@@ -447,21 +493,43 @@ get_local_items(Acc, From, #jid{lserver = LServer} = To,
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"delete-user">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"disable-user">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"reenable-user">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"end-user-session">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"change-user-password">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-user-roster">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-user-lastlogin">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"user-stats">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-registered-users-num">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-disabled-users-num">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-online-users-num">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-active-users-num">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-idle-users-num">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-registered-users-list">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-registered-users-num">>) ->
|
||||
?NS_ADMINL(<<"get-disabled-users-list">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-online-users-list">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-online-users-num">>) ->
|
||||
?NS_ADMINL(<<"get-active-users">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"get-idle-users">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"restart">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
?NS_ADMINL(<<"shutdown">>) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
||||
_ -> Acc
|
||||
end
|
||||
@@ -488,22 +556,41 @@ get_local_items(_Host, [<<"user">>], Server, Lang) ->
|
||||
[?NODE(?T("Add User"), (?NS_ADMINX(<<"add-user">>))),
|
||||
?NODE(?T("Delete User"),
|
||||
(?NS_ADMINX(<<"delete-user">>))),
|
||||
?NODE(?T("Disable User"),
|
||||
(?NS_ADMINX(<<"disable-user">>))),
|
||||
?NODE(?T("Re-Enable User"),
|
||||
(?NS_ADMINX(<<"reenable-user">>))),
|
||||
?NODE(?T("End User Session"),
|
||||
(?NS_ADMINX(<<"end-user-session">>))),
|
||||
?NODE(?T("Change User Password"),
|
||||
(?NS_ADMINX(<<"change-user-password">>))),
|
||||
?NODE(?T("Get User Roster"),
|
||||
(?NS_ADMINX(<<"get-user-roster">>))),
|
||||
?NODE(?T("Get User Last Login Time"),
|
||||
(?NS_ADMINX(<<"get-user-lastlogin">>))),
|
||||
?NODE(?T("Get User Statistics"),
|
||||
(?NS_ADMINX(<<"user-stats">>))),
|
||||
?NODE(?T("Get List of Registered Users"),
|
||||
(?NS_ADMINX(<<"get-registered-users-list">>))),
|
||||
?NODE(?T("Get Number of Registered Users"),
|
||||
(?NS_ADMINX(<<"get-registered-users-num">>))),
|
||||
?NODE(?T("Get Number of Disabled Users"),
|
||||
(?NS_ADMINX(<<"get-disabled-users-num">>))),
|
||||
?NODE(?T("Get Number of Online Users"),
|
||||
(?NS_ADMINX(<<"get-online-users-num">>))),
|
||||
?NODE(?T("Get Number of Active Users"),
|
||||
(?NS_ADMINX(<<"get-active-users-num">>))),
|
||||
?NODE(?T("Get Number of Idle Users"),
|
||||
(?NS_ADMINX(<<"get-idle-users-num">>))),
|
||||
?NODE(?T("Get List of Registered Users"),
|
||||
(?NS_ADMINX(<<"get-registered-users-list">>))),
|
||||
?NODE(?T("Get List of Disabled Users"),
|
||||
(?NS_ADMINX(<<"get-disabled-users-list">>))),
|
||||
?NODE(?T("Get List of Online Users"),
|
||||
(?NS_ADMINX(<<"get-online-users-list">>))),
|
||||
?NODE(?T("Get Number of Online Users"),
|
||||
(?NS_ADMINX(<<"get-online-users-num">>)))]};
|
||||
?NODE(?T("Get List of Active Users"),
|
||||
(?NS_ADMINX(<<"get-active-users">>))),
|
||||
?NODE(?T("Get List of Idle Users"),
|
||||
(?NS_ADMINX(<<"get-idle-users">>)))
|
||||
]};
|
||||
get_local_items(_Host, [<<"http:">> | _], _Server,
|
||||
_Lang) ->
|
||||
{result, []};
|
||||
@@ -552,6 +639,10 @@ get_local_items({global, _Host},
|
||||
<<"running nodes/", ENode/binary, "/backup">>),
|
||||
?NODE(?T("Import Users From jabberd14 Spool Files"),
|
||||
<<"running nodes/", ENode/binary, "/import">>),
|
||||
?NODE(?T("Restart Service"),
|
||||
(?NS_ADMINX(<<"restart">>))),
|
||||
?NODE(?T("Shut Down Service"),
|
||||
(?NS_ADMINX(<<"shutdown">>))),
|
||||
?NODE(?T("Restart Service"),
|
||||
<<"running nodes/", ENode/binary, "/restart">>),
|
||||
?NODE(?T("Shut Down Service"),
|
||||
@@ -997,6 +1088,24 @@ get_form(_Host, ?NS_ADMINL(<<"delete-user">>), Lang) ->
|
||||
label = tr(Lang, ?T("Jabber ID")),
|
||||
required = true,
|
||||
var = <<"accountjids">>}]}};
|
||||
get_form(_Host, ?NS_ADMINL(<<"disable-user">>), Lang) ->
|
||||
{result,
|
||||
#xdata{title = tr(Lang, ?T("Disable User")),
|
||||
type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("Jabber ID")),
|
||||
required = true,
|
||||
var = <<"accountjids">>}]}};
|
||||
get_form(_Host, ?NS_ADMINL(<<"reenable-user">>), Lang) ->
|
||||
{result,
|
||||
#xdata{title = tr(Lang, ?T("Re-Enable User")),
|
||||
type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("Jabber ID")),
|
||||
required = true,
|
||||
var = <<"accountjids">>}]}};
|
||||
get_form(_Host, ?NS_ADMINL(<<"end-user-session">>),
|
||||
Lang) ->
|
||||
{result,
|
||||
@@ -1021,6 +1130,16 @@ get_form(_Host, ?NS_ADMINL(<<"change-user-password">>),
|
||||
label = tr(Lang, ?T("Password")),
|
||||
required = true,
|
||||
var = <<"password">>}]}};
|
||||
get_form(_Host, ?NS_ADMINL(<<"get-user-roster">>),
|
||||
Lang) ->
|
||||
{result,
|
||||
#xdata{title = tr(Lang, ?T("Get User Roster")),
|
||||
type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("Jabber ID")),
|
||||
required = true,
|
||||
var = <<"accountjids">>}]}};
|
||||
get_form(_Host, ?NS_ADMINL(<<"get-user-lastlogin">>),
|
||||
Lang) ->
|
||||
{result,
|
||||
@@ -1040,16 +1159,6 @@ get_form(_Host, ?NS_ADMINL(<<"user-stats">>), Lang) ->
|
||||
label = tr(Lang, ?T("Jabber ID")),
|
||||
var = <<"accountjid">>,
|
||||
required = true}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-registered-users-list">>), Lang) ->
|
||||
Values = [jid:encode(jid:make(U, Host))
|
||||
|| {U, _} <- ejabberd_auth:get_users(Host)],
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("The list of all users")),
|
||||
var = <<"registereduserjids">>,
|
||||
values = Values}]}};
|
||||
get_form(Host,
|
||||
?NS_ADMINL(<<"get-registered-users-num">>), Lang) ->
|
||||
Num = integer_to_binary(ejabberd_auth:count_users(Host)),
|
||||
@@ -1060,6 +1169,68 @@ get_form(Host,
|
||||
label = tr(Lang, ?T("Number of registered users")),
|
||||
var = <<"registeredusersnum">>,
|
||||
values = [Num]}]}};
|
||||
get_form(Host,
|
||||
?NS_ADMINL(<<"get-disabled-users-num">>), Lang) ->
|
||||
Num = integer_to_binary(mod_admin_extra:count_banned(Host)),
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'text-single',
|
||||
label = tr(Lang, ?T("Number of disabled users")),
|
||||
var = <<"disabledusersnum">>,
|
||||
values = [Num]}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-online-users-num">>),
|
||||
Lang) ->
|
||||
Num = integer_to_binary(ejabberd_sm:get_vh_session_number(Host)),
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'text-single',
|
||||
label = tr(Lang, ?T("Number of online users")),
|
||||
var = <<"onlineusersnum">>,
|
||||
values = [Num]}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-active-users-num">>),
|
||||
Lang) ->
|
||||
Num = integer_to_binary(mod_admin_extra:status_num(Host, [<<"available">>,
|
||||
<<"chat">>,
|
||||
<<"dnd">>])),
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'text-single',
|
||||
label = tr(Lang, ?T("Number of active users")),
|
||||
var = <<"activeusersnum">>,
|
||||
values = [Num]}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-idle-users-num">>),
|
||||
Lang) ->
|
||||
Num = integer_to_binary(mod_admin_extra:status_num(Host, [<<"away">>,
|
||||
<<"xa">>])),
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'text-single',
|
||||
label = tr(Lang, ?T("Number of idle users")),
|
||||
var = <<"idleusersnum">>,
|
||||
values = [Num]}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-registered-users-list">>), Lang) ->
|
||||
Values = [jid:encode(jid:make(U, Host))
|
||||
|| {U, _} <- ejabberd_auth:get_users(Host)],
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("The list of all users")),
|
||||
var = <<"registereduserjids">>,
|
||||
values = Values}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-disabled-users-list">>), Lang) ->
|
||||
Values = mod_admin_extra:list_banned(Host),
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("The list of all disabled users")),
|
||||
var = <<"disableduserjids">>,
|
||||
values = Values}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-online-users-list">>), Lang) ->
|
||||
Accounts = [jid:encode(jid:make(U, Host))
|
||||
|| {U, _, _} <- ejabberd_sm:get_vh_session_list(Host)],
|
||||
@@ -1071,16 +1242,36 @@ get_form(Host, ?NS_ADMINL(<<"get-online-users-list">>), Lang) ->
|
||||
label = tr(Lang, ?T("The list of all online users")),
|
||||
var = <<"onlineuserjids">>,
|
||||
values = Values}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-online-users-num">>),
|
||||
Lang) ->
|
||||
Num = integer_to_binary(ejabberd_sm:get_vh_session_number(Host)),
|
||||
get_form(Host, ?NS_ADMINL(<<"get-active-users">>), Lang) ->
|
||||
RR = mod_admin_extra:status_list(Host, [<<"available">>, <<"chat">>, <<"dnd">>]),
|
||||
Accounts = [jid:encode(jid:make(U, S))
|
||||
|| {U, S, _Resource, _Priority, _StatusText} <- RR],
|
||||
Values = lists:usort(Accounts),
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'text-single',
|
||||
label = tr(Lang, ?T("Number of online users")),
|
||||
var = <<"onlineusersnum">>,
|
||||
values = [Num]}]}};
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("The list of all active users")),
|
||||
var = <<"activeuserjids">>,
|
||||
values = Values}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"get-idle-users">>), Lang) ->
|
||||
RR = mod_admin_extra:status_list(Host, [<<"away">>, <<"xa">>]),
|
||||
Accounts = [jid:encode(jid:make(U, S))
|
||||
|| {U, S, _Resource, _Priority, _StatusText} <- RR],
|
||||
Values = lists:usort(Accounts),
|
||||
{result, completed,
|
||||
#xdata{type = form,
|
||||
fields = [?HFIELD(),
|
||||
#xdata_field{type = 'jid-multi',
|
||||
label = tr(Lang, ?T("The list of all idle users")),
|
||||
var = <<"idleuserjids">>,
|
||||
values = Values}]}};
|
||||
get_form(Host, ?NS_ADMINL(<<"restart">>), Lang) ->
|
||||
get_form(Host,
|
||||
[<<"running nodes">>, misc:atom_to_binary(node()), <<"restart">>], Lang);
|
||||
get_form(Host, ?NS_ADMINL(<<"shutdown">>), Lang) ->
|
||||
get_form(Host,
|
||||
[<<"running nodes">>, misc:atom_to_binary(node()), <<"shutdown">>], Lang);
|
||||
get_form(_Host, _, _Lang) ->
|
||||
{error, xmpp:err_service_unavailable()}.
|
||||
|
||||
@@ -1298,6 +1489,42 @@ set_form(From, Host, ?NS_ADMINL(<<"delete-user">>),
|
||||
[ejabberd_auth:remove_user(User, Server)
|
||||
|| {User, Server} <- ASL2],
|
||||
{result, undefined};
|
||||
set_form(From, Host, ?NS_ADMINL(<<"disable-user">>),
|
||||
_Lang, XData) ->
|
||||
AccountStringList = get_values(<<"accountjids">>,
|
||||
XData),
|
||||
[_ | _] = AccountStringList,
|
||||
ASL2 = lists:map(fun (AccountString) ->
|
||||
JID = jid:decode(AccountString),
|
||||
User = JID#jid.luser,
|
||||
Server = JID#jid.lserver,
|
||||
true = Server == Host orelse
|
||||
get_permission_level(From, Host) == global,
|
||||
true = ejabberd_auth:user_exists(User, Server),
|
||||
{User, Server}
|
||||
end,
|
||||
AccountStringList),
|
||||
[mod_admin_extra:ban_account_v2(User, Server, <<"">>)
|
||||
|| {User, Server} <- ASL2],
|
||||
{result, undefined};
|
||||
set_form(From, Host, ?NS_ADMINL(<<"reenable-user">>),
|
||||
_Lang, XData) ->
|
||||
AccountStringList = get_values(<<"accountjids">>,
|
||||
XData),
|
||||
[_ | _] = AccountStringList,
|
||||
ASL2 = lists:map(fun (AccountString) ->
|
||||
JID = jid:decode(AccountString),
|
||||
User = JID#jid.luser,
|
||||
Server = JID#jid.lserver,
|
||||
true = Server == Host orelse
|
||||
get_permission_level(From, Host) == global,
|
||||
true = ejabberd_auth:user_exists(User, Server),
|
||||
{User, Server}
|
||||
end,
|
||||
AccountStringList),
|
||||
[mod_admin_extra:unban_account(User, Server)
|
||||
|| {User, Server} <- ASL2],
|
||||
{result, undefined};
|
||||
set_form(From, Host, ?NS_ADMINL(<<"end-user-session">>),
|
||||
_Lang, XData) ->
|
||||
AccountString = get_value(<<"accountjid">>, XData),
|
||||
@@ -1324,6 +1551,31 @@ set_form(From, Host,
|
||||
true = ejabberd_auth:user_exists(User, Server),
|
||||
ejabberd_auth:set_password(User, Server, Password),
|
||||
{result, undefined};
|
||||
set_form(From, Host,
|
||||
?NS_ADMINL(<<"get-user-roster">>), Lang, XData) ->
|
||||
AccountStringList = get_values(<<"accountjids">>,
|
||||
XData),
|
||||
[_ | _] = AccountStringList,
|
||||
ASL2 = lists:map(fun (AccountString) ->
|
||||
JID = jid:decode(AccountString),
|
||||
User = JID#jid.luser,
|
||||
Server = JID#jid.lserver,
|
||||
true = Server == Host orelse
|
||||
get_permission_level(From, Host) == global,
|
||||
true = ejabberd_auth:user_exists(User, Server),
|
||||
{User, Server}
|
||||
end,
|
||||
AccountStringList),
|
||||
Contacts = [mod_admin_extra:get_roster(User, Server) || {User, Server} <- ASL2],
|
||||
Jids = [lists:join(<<"; ">>, [Jid, Name, Subscription, lists:join(<<", ">>, Groups)])
|
||||
|| {Jid, Name, Subscription, _, Groups} <- lists:flatten(Contacts)],
|
||||
{result,
|
||||
#xdata{type = result,
|
||||
fields = [?HFIELD(),
|
||||
?XMFIELD('jid-multi', ?T("Jabber ID"),
|
||||
<<"accountjids">>, AccountStringList),
|
||||
?XMFIELD('text-multi', ?T("Contacts"),
|
||||
<<"contacts">>, Jids)]}};
|
||||
set_form(From, Host,
|
||||
?NS_ADMINL(<<"get-user-lastlogin">>), Lang, XData) ->
|
||||
AccountString = get_value(<<"accountjid">>, XData),
|
||||
@@ -1350,7 +1602,7 @@ set_form(From, Host,
|
||||
_ -> tr(Lang, ?T("Online"))
|
||||
end,
|
||||
{result,
|
||||
#xdata{type = form,
|
||||
#xdata{type = result,
|
||||
fields = [?HFIELD(),
|
||||
?XFIELD('jid-single', ?T("Jabber ID"),
|
||||
<<"accountjid">>, AccountString),
|
||||
@@ -1375,7 +1627,7 @@ set_form(From, Host, ?NS_ADMINL(<<"user-stats">>), Lang,
|
||||
[{User, Server}]),
|
||||
Rostersize = integer_to_binary(erlang:length(Items)),
|
||||
{result,
|
||||
#xdata{type = form,
|
||||
#xdata{type = result,
|
||||
fields = [?HFIELD(),
|
||||
?XFIELD('jid-single', ?T("Jabber ID"),
|
||||
<<"accountjid">>, AccountString),
|
||||
@@ -1385,6 +1637,14 @@ set_form(From, Host, ?NS_ADMINL(<<"user-stats">>), Lang,
|
||||
<<"ipaddresses">>, IPs),
|
||||
?XMFIELD('text-multi', ?T("Resources"),
|
||||
<<"onlineresources">>, Resources)]}};
|
||||
set_form(From, Host, ?NS_ADMINL(<<"restart">>), Lang,
|
||||
XData) ->
|
||||
set_form(From, Host,
|
||||
[<<"running nodes">>, misc:atom_to_binary(node()), <<"restart">>], Lang, XData);
|
||||
set_form(From, Host, ?NS_ADMINL(<<"shutdown">>), Lang,
|
||||
XData) ->
|
||||
set_form(From, Host,
|
||||
[<<"running nodes">>, misc:atom_to_binary(node()), <<"shutdown">>], Lang, XData);
|
||||
set_form(_From, _Host, _, _Lang, _XData) ->
|
||||
{error, xmpp:err_service_unavailable()}.
|
||||
|
||||
@@ -1544,28 +1804,40 @@ mod_opt_type(access) ->
|
||||
mod_options(_Host) ->
|
||||
[{access, configure}].
|
||||
|
||||
%% @format-begin
|
||||
|
||||
%% All ad-hoc commands implemented by mod_configure are available as API Commands:
|
||||
%% - add-user -> register
|
||||
%% - delete-user -> unregister
|
||||
%% - disable-user -> ban_account
|
||||
%% - reenable-user -> unban_account
|
||||
%% - end-user-session -> kick_session / kick_user
|
||||
%% - change-user-password -> change_password
|
||||
%% - get-user-lastlogin -> get_last
|
||||
%% - get-user-roster -> get_roster
|
||||
%% - get-user-lastlogin -> get_last
|
||||
%% - user-stats -> user_sessions_info
|
||||
%% - get-registered-users-list -> registered_users
|
||||
%% - edit-blacklist -> not ad-hoc or API command available !!!
|
||||
%% - edit-whitelist -> not ad-hoc or API command available !!!
|
||||
%% - get-registered-users-num -> stats
|
||||
%% - get-online-users-list -> connected_users
|
||||
%% - get-disabled-users-num -> count_banned
|
||||
%% - get-online-users-num -> stats
|
||||
%% - get-active-users-num -> status_num
|
||||
%% - get-idle-users-num -> status_num
|
||||
%% - get-registered-users-list -> registered_users
|
||||
%% - get-disabled-users-list -> list_banned
|
||||
%% - get-online-users-list -> connected_users
|
||||
%% - get-active-users -> status_list
|
||||
%% - get-idle-users -> status_list
|
||||
%% - stopped nodes -> list_cluster_detailed
|
||||
%% - DB -> mnesia_list_tables and mnesia_table_change_storage
|
||||
%% - restart -> stop_kindly / restart
|
||||
%% - edit-admin -> not ad-hoc or API command available !!!
|
||||
%% - restart -> restart_kindly
|
||||
%% - shutdown -> stop_kindly
|
||||
%% - backup -> backup
|
||||
%% - restore -> restore
|
||||
%% - textfile -> dump
|
||||
%% - import/file -> import_file
|
||||
%% - import/dir -> import_dir
|
||||
|
||||
%%
|
||||
%% An exclusive feature available only in this module is to list items and discover them:
|
||||
%% - outgoing s2s
|
||||
@@ -1579,9 +1851,28 @@ mod_doc() ->
|
||||
"https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]:"),
|
||||
"",
|
||||
"- List and discover outgoing s2s, online client sessions and all registered accounts",
|
||||
"- Most of the ad-hoc commands defined in https://xmpp.org/extensions/xep-0133.html[XEP-0133: Service Administration]",
|
||||
"- Most of the ad-hoc commands defined in "
|
||||
" https://xmpp.org/extensions/xep-0133.html[XEP-0133: Service Administration]",
|
||||
"- Additional custom ad-hoc commands specific to ejabberd",
|
||||
"",
|
||||
?T("Ad-hoc commands from XEP-0133 that behave differently to the XEP:"),
|
||||
"",
|
||||
" - `get-user-roster`: returns standard fields instead of roster items that client cannot display",
|
||||
"",
|
||||
?T("Those ad-hoc commands from XEP-0133 do not include in the response "
|
||||
"the client that executed the command:"),
|
||||
"",
|
||||
" - `get-active-users-num`",
|
||||
" - `get-idle-users-num`",
|
||||
" - `get-active-users`",
|
||||
" - `get-idle-users`",
|
||||
"",
|
||||
?T("Those ad-hoc commands from XEP-0133 are not implemented:"),
|
||||
"",
|
||||
" - `edit-blacklist`",
|
||||
" - `edit-whitelist`",
|
||||
" - `edit-admin`",
|
||||
"",
|
||||
?T("This module requires _`mod_adhoc`_ (to execute the commands), "
|
||||
"and recommends _`mod_disco`_ (to discover the commands). "),
|
||||
"",
|
||||
@@ -1589,6 +1880,7 @@ mod_doc() ->
|
||||
"have an equivalent "
|
||||
"https://docs.ejabberd.im/developer/ejabberd-api/[API Command] "
|
||||
"that you can execute using _`mod_adhoc_api`_ or any other API frontend.")],
|
||||
note => "improved in 25.10",
|
||||
opts =>
|
||||
[{access,
|
||||
#{value => ?T("AccessName"),
|
||||
|
||||
+24
-18
@@ -36,7 +36,7 @@
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("translate.hrl").
|
||||
|
||||
-define(DEFAULT_API_VERSION, 1000000).
|
||||
@@ -145,12 +145,11 @@ process([Call | _], #request{method = 'POST', data = Data, ip = IPPort} = Req) -
|
||||
%% TODO We need to refactor to remove redundant error return formatting
|
||||
throw:{error, unknown_command} ->
|
||||
json_format({404, 44, <<"Command not found.">>});
|
||||
_:{error,{_,invalid_json}} = _Err ->
|
||||
?DEBUG("Bad Request: ~p", [_Err]),
|
||||
_:{error,{_,invalid_json}} = Err ->
|
||||
?DEBUG("Bad Request: ~p", [Err]),
|
||||
badrequest_response(<<"Invalid JSON input">>);
|
||||
?EX_RULE(_Class, _Error, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
?DEBUG("Bad Request: ~p ~p", [_Error, StackTrace]),
|
||||
_Class:Error:StackTrace ->
|
||||
?DEBUG("Bad Request: ~p ~p", [Error, StackTrace]),
|
||||
badrequest_response()
|
||||
end;
|
||||
process([Call | _], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) ->
|
||||
@@ -166,9 +165,8 @@ process([Call | _], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) ->
|
||||
%% TODO We need to refactor to remove redundant error return formatting
|
||||
throw:{error, unknown_command} ->
|
||||
json_format({404, 44, <<"Command not found.">>});
|
||||
?EX_RULE(_, _Error, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
?DEBUG("Bad Request: ~p ~p", [_Error, StackTrace]),
|
||||
_:Error:StackTrace ->
|
||||
?DEBUG("Bad Request: ~p ~p", [Error, StackTrace]),
|
||||
badrequest_response()
|
||||
end;
|
||||
process([_Call], #request{method = 'OPTIONS', data = <<>>}) ->
|
||||
@@ -257,13 +255,15 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
|
||||
{400, misc:atom_to_binary(Error)};
|
||||
throw:Msg when is_list(Msg); is_binary(Msg) ->
|
||||
{400, iolist_to_binary(Msg)};
|
||||
?EX_RULE(Class, Error, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
?ERROR_MSG("REST API Error: "
|
||||
"~ts(~p) -> ~p:~p ~p",
|
||||
[Call, hide_sensitive_args(Args),
|
||||
Class, Error, StackTrace]),
|
||||
{500, <<"internal_error">>}
|
||||
Class:Error:StackTrace ->
|
||||
?ERROR_MSG("REST API Error: "
|
||||
"~ts(~p) -> ~p:~p ~p",
|
||||
[Call,
|
||||
hide_sensitive_args(Args),
|
||||
Class,
|
||||
Error,
|
||||
StackTrace]),
|
||||
{500, <<"internal_error">>}
|
||||
end.
|
||||
|
||||
handle2(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
|
||||
@@ -372,8 +372,8 @@ format_arg({[{Name, Value}]},
|
||||
format_arg(Elements,
|
||||
{tuple, ElementsDef})
|
||||
when is_map(Elements) ->
|
||||
list_to_tuple([element(2, maps:find(atom_to_binary(Name, latin1), Elements))
|
||||
|| {Name, _Format} <- ElementsDef]);
|
||||
list_to_tuple([format_arg(element(2, maps:find(atom_to_binary(Name, latin1), Elements)), Format)
|
||||
|| {Name, Format} <- ElementsDef]);
|
||||
|
||||
format_arg({Elements},
|
||||
{tuple, ElementsDef})
|
||||
@@ -401,9 +401,15 @@ format_arg(Arg, integer) when is_integer(Arg) -> Arg;
|
||||
format_arg(Arg, integer) when is_binary(Arg) -> binary_to_integer(Arg);
|
||||
format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg);
|
||||
format_arg(Arg, binary) when is_binary(Arg) -> Arg;
|
||||
format_arg([], binary_or_list) -> [];
|
||||
format_arg([First | _] = Arg, binary_or_list) when is_binary(First) -> Arg;
|
||||
format_arg([First | _] = Arg, binary_or_list) when is_integer(First) ->
|
||||
[process_unicode_codepoints(Arg)];
|
||||
format_arg(Arg, binary_or_list) when is_binary(Arg) -> [Arg];
|
||||
format_arg(Arg, string) when is_list(Arg) -> Arg;
|
||||
format_arg(Arg, string) when is_binary(Arg) -> binary_to_list(Arg);
|
||||
format_arg(undefined, binary) -> <<>>;
|
||||
format_arg(undefined, binary_or_list) -> [];
|
||||
format_arg(undefined, string) -> "";
|
||||
format_arg(Arg, Format) ->
|
||||
?ERROR_MSG("Don't know how to format Arg ~p for format ~p", [Arg, Format]),
|
||||
|
||||
@@ -921,7 +921,7 @@ mk_slot(PutURL, GetURL, XMLNS, Query) ->
|
||||
end.
|
||||
|
||||
reencode_url(UrlString) ->
|
||||
{ok, _, _, Host, _, _, _} = yconf:parse_uri(UrlString),
|
||||
{ok, _, _, Host, _, _, _} = yconf:parse_uri(misc:url_encode(UrlString)),
|
||||
HostDecoded = misc:uri_decode(Host),
|
||||
HostIdna = idna:encode(HostDecoded),
|
||||
re:replace(UrlString, Host, HostIdna, [{return, binary}]).
|
||||
|
||||
+48
-1
@@ -914,6 +914,13 @@ process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang,
|
||||
-spec should_archive(message(), binary()) -> boolean().
|
||||
should_archive(#message{type = error}, _LServer) ->
|
||||
false;
|
||||
should_archive(#message{type = groupchat, meta = #{is_muc_subscriber := true}} = Msg, LServer) ->
|
||||
case mod_mam_opt:archive_muc_as_mucsub(LServer) of
|
||||
true ->
|
||||
should_archive(Msg#message{type = chat}, LServer);
|
||||
false ->
|
||||
false
|
||||
end;
|
||||
should_archive(#message{type = groupchat}, _LServer) ->
|
||||
false;
|
||||
should_archive(#message{meta = #{from_offline := true}}, _LServer) ->
|
||||
@@ -1101,6 +1108,31 @@ may_enter_room(From, MUCState) ->
|
||||
|
||||
-spec store_msg(message(), binary(), binary(), jid(), send | recv)
|
||||
-> ok | pass | any().
|
||||
store_msg(#message{type = groupchat, from = From, to = To, meta = #{is_muc_subscriber := true}} = Pkt, LUser, LServer, _Peer, Dir) ->
|
||||
BarePeer = jid:remove_resource(From),
|
||||
StanzaId = xmpp:get_subtag(Pkt, #stanza_id{by = #jid{}}),
|
||||
Id = case StanzaId of
|
||||
#stanza_id{id = Id2} ->
|
||||
Id2;
|
||||
_ ->
|
||||
p1_rand:get_string()
|
||||
end,
|
||||
Pkt2 = #message{
|
||||
to = To,
|
||||
from = BarePeer,
|
||||
id = Id,
|
||||
sub_els = [#ps_event{
|
||||
items = #ps_items{
|
||||
node = ?NS_MUCSUB_NODES_MESSAGES,
|
||||
items = [#ps_item{
|
||||
id = Id,
|
||||
sub_els = [Pkt]
|
||||
}]
|
||||
}
|
||||
}]
|
||||
},
|
||||
Pkt3 = xmpp:put_meta(Pkt2, stanza_id, binary_to_integer(Id)),
|
||||
store_msg(Pkt3, LUser, LServer, BarePeer, Dir);
|
||||
store_msg(Pkt, LUser, LServer, Peer, Dir) ->
|
||||
case get_prefs(LUser, LServer) of
|
||||
{ok, Prefs} ->
|
||||
@@ -1738,6 +1770,8 @@ mod_opt_type(clear_archive_on_room_destroy) ->
|
||||
econf:bool();
|
||||
mod_opt_type(user_mucsub_from_muc_archive) ->
|
||||
econf:bool();
|
||||
mod_opt_type(archive_muc_as_mucsub) ->
|
||||
econf:bool();
|
||||
mod_opt_type(access_preferences) ->
|
||||
econf:acl();
|
||||
mod_opt_type(db_type) ->
|
||||
@@ -1759,6 +1793,7 @@ mod_options(Host) ->
|
||||
{clear_archive_on_room_destroy, true},
|
||||
{access_preferences, all},
|
||||
{user_mucsub_from_muc_archive, false},
|
||||
{archive_muc_as_mucsub, false},
|
||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_option:use_cache(Host)},
|
||||
{cache_size, ejabberd_option:cache_size(Host)},
|
||||
@@ -1856,4 +1891,16 @@ mod_doc() ->
|
||||
"subscriber a separate mucsub message is stored. With this "
|
||||
"option enabled, when a user fetches archive virtual "
|
||||
"mucsub, messages are generated from muc archives. "
|
||||
"The default value is 'false'.")}}]}.
|
||||
"The default value is 'false'.")
|
||||
}},
|
||||
{archive_muc_as_mucsub,
|
||||
#{value => "true | false",
|
||||
note => "added in 25.10",
|
||||
desc =>
|
||||
?T("When this option is enabled incoming groupchat messages "
|
||||
"for users that have mucsub subscription to a room from which "
|
||||
"message originated will have those messages archived after being "
|
||||
"converted to mucsub event messages."
|
||||
"The default value is 'false'.")
|
||||
}}]
|
||||
}.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
-module(mod_mam_opt).
|
||||
|
||||
-export([access_preferences/1]).
|
||||
-export([archive_muc_as_mucsub/1]).
|
||||
-export([assume_mam_usage/1]).
|
||||
-export([cache_life_time/1]).
|
||||
-export([cache_missed/1]).
|
||||
@@ -22,6 +23,12 @@ access_preferences(Opts) when is_map(Opts) ->
|
||||
access_preferences(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_mam, access_preferences).
|
||||
|
||||
-spec archive_muc_as_mucsub(gen_mod:opts() | global | binary()) -> boolean().
|
||||
archive_muc_as_mucsub(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(archive_muc_as_mucsub, Opts);
|
||||
archive_muc_as_mucsub(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_mam, archive_muc_as_mucsub).
|
||||
|
||||
-spec assume_mam_usage(gen_mod:opts() | global | binary()) -> boolean().
|
||||
assume_mam_usage(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(assume_mam_usage, Opts);
|
||||
|
||||
+1
-1
@@ -656,7 +656,7 @@ make_sql_query(User, LServer, MAMQuery, RSM, ExtraUsernames) ->
|
||||
SUser = ToString(User),
|
||||
SServer = ToString(LServer),
|
||||
|
||||
HostMatch = case ejabberd_sql:use_new_schema() of
|
||||
HostMatch = case ejabberd_sql:use_multihost_schema() of
|
||||
true ->
|
||||
[<<" and server_host=", SServer/binary>>];
|
||||
_ ->
|
||||
|
||||
+7
-6
@@ -44,7 +44,8 @@
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
|
||||
-callback init(binary(), gen_mod:opts()) -> ok | {error, db_failure}.
|
||||
-callback set_channel(binary(), binary(), binary(),
|
||||
@@ -319,11 +320,11 @@ handle_cast(Request, State) ->
|
||||
|
||||
handle_info({route, Packet}, State) ->
|
||||
try route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info(Info, State) ->
|
||||
|
||||
+100
-55
@@ -67,6 +67,8 @@
|
||||
count_online_rooms/1,
|
||||
register_online_user/4,
|
||||
unregister_online_user/4,
|
||||
get_register_nick/3,
|
||||
get_register_nicks/2,
|
||||
iq_set_register_info/5,
|
||||
count_online_rooms_by_user/3,
|
||||
get_online_rooms_by_user/3,
|
||||
@@ -85,7 +87,7 @@
|
||||
-include("mod_muc.hrl").
|
||||
-include("mod_muc_room.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-type state() :: #{hosts := [binary()],
|
||||
server_host := binary(),
|
||||
@@ -102,6 +104,7 @@
|
||||
-callback can_use_nick(binary(), binary(), jid(), binary()) -> boolean().
|
||||
-callback get_rooms(binary(), binary()) -> [#muc_room{}].
|
||||
-callback get_nick(binary(), binary(), jid()) -> binary() | error.
|
||||
-callback get_nicks(binary(), binary()) -> [{binary(), binary(), binary()}] | error.
|
||||
-callback set_nick(binary(), binary(), jid(), binary()) -> {atomic, ok | false}.
|
||||
-callback register_online_room(binary(), binary(), binary(), pid()) -> any().
|
||||
-callback unregister_online_room(binary(), binary(), binary(), pid()) -> any().
|
||||
@@ -414,10 +417,10 @@ init([Host, Worker]) ->
|
||||
{stop, normal, ok, state()}.
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, State};
|
||||
handle_call({unhibernate, Room, Host, ResetHibernationTime}, _From,
|
||||
handle_call({unhibernate, Room, Host, ResetHibernationTime, Opts}, _From,
|
||||
#{server_host := ServerHost} = State) ->
|
||||
RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
|
||||
{reply, load_room(RMod, Host, ServerHost, Room, ResetHibernationTime), State};
|
||||
{reply, do_restore_room(RMod, Host, ServerHost, Room, ResetHibernationTime, Opts), State};
|
||||
handle_call({create, Room, Host, Opts}, _From,
|
||||
#{server_host := ServerHost} = State) ->
|
||||
?DEBUG("MUC: create new room '~ts'~n", [Room]),
|
||||
@@ -454,11 +457,11 @@ handle_call({create, Room, Host, From, Nick, Opts}, _From,
|
||||
-spec handle_cast(term(), state()) -> {noreply, state()}.
|
||||
handle_cast({route_to_room, Packet}, #{server_host := ServerHost} = State) ->
|
||||
try route_to_room(Packet, ServerHost)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_cast({room_destroyed, {Room, Host}, Pid},
|
||||
@@ -483,11 +486,11 @@ handle_info({route, Packet}, #{server_host := ServerHost} = State) ->
|
||||
%% where mod_muc is not loaded. Such configuration
|
||||
%% is *highly* discouraged
|
||||
try route(Packet, ServerHost)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({room_destroyed, {Room, Host}, Pid}, State) ->
|
||||
@@ -599,10 +602,17 @@ unhibernate_room(ServerHost, Host, Room) ->
|
||||
unhibernate_room(ServerHost, Host, Room, ResetHibernationTime) ->
|
||||
RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
|
||||
case RMod:find_online_room(ServerHost, Room, Host) of
|
||||
error ->
|
||||
Proc = procname(ServerHost, {Room, Host}),
|
||||
?GEN_SERVER:call(Proc, {unhibernate, Room, Host, ResetHibernationTime}, 20000);
|
||||
{ok, _} = R2 -> R2
|
||||
error ->
|
||||
case RMod:restore_room(ServerHost, Host, Room) of
|
||||
error ->
|
||||
{error, notfound};
|
||||
{error, _} = Err ->
|
||||
Err;
|
||||
Opts ->
|
||||
Proc = procname(ServerHost, {Room, Host}),
|
||||
?GEN_SERVER:call(Proc, {unhibernate, Room, Host, ResetHibernationTime, Opts}, 20000)
|
||||
end;
|
||||
{ok, _} = R2 -> R2
|
||||
end.
|
||||
|
||||
-spec route_to_room(stanza(), binary()) -> ok.
|
||||
@@ -883,35 +893,38 @@ load_permanent_rooms(Hosts, ServerHost, Opts) ->
|
||||
{ok, pid()} | {error, notfound | term()}.
|
||||
load_room(RMod, Host, ServerHost, Room, ResetHibernationTime) ->
|
||||
case restore_room(ServerHost, Host, Room) of
|
||||
error ->
|
||||
{error, notfound};
|
||||
error ->
|
||||
{error, notfound};
|
||||
{error, _} = Err ->
|
||||
Err;
|
||||
Opts0 ->
|
||||
Mod = gen_mod:db_mod(ServerHost, mod_muc),
|
||||
case proplists:get_bool(persistent, Opts0) of
|
||||
true ->
|
||||
?DEBUG("Restore room: ~ts", [Room]),
|
||||
Res2 = start_room(RMod, Host, ServerHost, Room, Opts0),
|
||||
case {Res2, ResetHibernationTime} of
|
||||
{{ok, _}, true} ->
|
||||
NewOpts = lists:keyreplace(hibernation_time, 1, Opts0, {hibernation_time, undefined}),
|
||||
store_room(ServerHost, Host, Room, NewOpts, []);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
Res2;
|
||||
Opts ->
|
||||
do_restore_room(RMod, Host, ServerHost, Room, ResetHibernationTime, Opts)
|
||||
end.
|
||||
|
||||
do_restore_room(RMod, Host, ServerHost, Room, ResetHibernationTime, Opts) ->
|
||||
Mod = gen_mod:db_mod(ServerHost, mod_muc),
|
||||
case proplists:get_bool(persistent, Opts) of
|
||||
true ->
|
||||
?DEBUG("Restore room: ~ts", [Room]),
|
||||
Res2 = start_room(RMod, Host, ServerHost, Room, Opts),
|
||||
case {Res2, ResetHibernationTime} of
|
||||
{{ok, _}, true} ->
|
||||
NewOpts = lists:keyreplace(hibernation_time, 1, Opts, {hibernation_time, undefined}),
|
||||
store_room(ServerHost, Host, Room, NewOpts, []);
|
||||
_ ->
|
||||
?DEBUG("Restore hibernated non-persistent room: ~ts", [Room]),
|
||||
Res = start_room(RMod, Host, ServerHost, Room, Opts0),
|
||||
case erlang:function_exported(Mod, get_subscribed_rooms, 3) of
|
||||
true ->
|
||||
ok;
|
||||
_ ->
|
||||
forget_room(ServerHost, Host, Room)
|
||||
end,
|
||||
Res
|
||||
end
|
||||
ok
|
||||
end,
|
||||
Res2;
|
||||
_ ->
|
||||
?DEBUG("Restore hibernated non-persistent room: ~ts", [Room]),
|
||||
Res = start_room(RMod, Host, ServerHost, Room, Opts),
|
||||
case erlang:function_exported(Mod, get_subscribed_rooms, 3) of
|
||||
true ->
|
||||
ok;
|
||||
_ ->
|
||||
forget_room(ServerHost, Host, Room)
|
||||
end,
|
||||
Res
|
||||
end.
|
||||
|
||||
start_new_room(RMod, Host, ServerHost, Room, Pass, From, Nick) ->
|
||||
@@ -1097,6 +1110,16 @@ get_nick(ServerHost, Host, From) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_nick(LServer, Host, From).
|
||||
|
||||
-spec get_register_nick(binary(), binary(), jid()) -> binary() | error.
|
||||
get_register_nick(ServerHost, Host, From) ->
|
||||
get_nick(ServerHost, Host, From).
|
||||
|
||||
-spec get_register_nicks(binary(), binary()) -> [{binary(), binary(), binary()}].
|
||||
get_register_nicks(ServerHost, Host) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_nicks(LServer, Host).
|
||||
|
||||
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||
{Nick, Registered} = case get_nick(ServerHost, Host, From) of
|
||||
error -> {<<"">>, false};
|
||||
@@ -1210,28 +1233,28 @@ remove_user(User, Server) ->
|
||||
ok.
|
||||
|
||||
opts_to_binary(Opts) ->
|
||||
lists:map(
|
||||
lists:flatmap(
|
||||
fun({title, Title}) ->
|
||||
{title, iolist_to_binary(Title)};
|
||||
[{title, iolist_to_binary(Title)}];
|
||||
({description, Desc}) ->
|
||||
{description, iolist_to_binary(Desc)};
|
||||
[{description, iolist_to_binary(Desc)}];
|
||||
({password, Pass}) ->
|
||||
{password, iolist_to_binary(Pass)};
|
||||
[{password, iolist_to_binary(Pass)}];
|
||||
({subject, [C|_] = Subj}) when is_integer(C), C >= 0, C =< 255 ->
|
||||
{subject, iolist_to_binary(Subj)};
|
||||
[{subject, iolist_to_binary(Subj)}];
|
||||
({subject_author, {AuthorNick, AuthorJID}}) ->
|
||||
{subject_author, {iolist_to_binary(AuthorNick), AuthorJID}};
|
||||
[{subject_author, {iolist_to_binary(AuthorNick), AuthorJID}}];
|
||||
({subject_author, AuthorNick}) -> % ejabberd 23.04 or older
|
||||
{subject_author, {iolist_to_binary(AuthorNick), #jid{}}};
|
||||
[{subject_author, {iolist_to_binary(AuthorNick), #jid{}}}];
|
||||
({allow_private_messages, Value}) -> % ejabberd 23.04 or older
|
||||
Value2 = case Value of
|
||||
true -> anyone;
|
||||
false -> none;
|
||||
_ -> Value
|
||||
end,
|
||||
{allowpm, Value2};
|
||||
[{allowpm, Value2}];
|
||||
({AffOrRole, Affs}) when (AffOrRole == affiliation) or (AffOrRole == role) ->
|
||||
{affiliations, lists:map(
|
||||
[{affiliations, lists:map(
|
||||
fun({{U, S, R}, Aff}) ->
|
||||
NewAff =
|
||||
case Aff of
|
||||
@@ -1244,16 +1267,38 @@ opts_to_binary(Opts) ->
|
||||
iolist_to_binary(S),
|
||||
iolist_to_binary(R)},
|
||||
NewAff}
|
||||
end, Affs)};
|
||||
end, Affs)}];
|
||||
({captcha_whitelist, CWList}) ->
|
||||
{captcha_whitelist, lists:map(
|
||||
[{captcha_whitelist, lists:map(
|
||||
fun({U, S, R}) ->
|
||||
{iolist_to_binary(U),
|
||||
iolist_to_binary(S),
|
||||
iolist_to_binary(R)}
|
||||
end, CWList)};
|
||||
end, CWList)}];
|
||||
({hats_users, HatsUsers}) -> % Update hats definitions
|
||||
case lists:keymember(hats_defs, 1, Opts) of
|
||||
true ->
|
||||
[{hats_users, HatsUsers}];
|
||||
_ ->
|
||||
{HatsDefs, HatsUsers2} =
|
||||
lists:foldl(fun({Jid, UriTitleList}, {Defs, Assigns}) ->
|
||||
Defs2 =
|
||||
lists:foldl(fun({Uri, Title}, AccDef) ->
|
||||
AccDef#{Uri => {Title, <<"">>}}
|
||||
end,
|
||||
Defs,
|
||||
UriTitleList),
|
||||
Assigns2 =
|
||||
Assigns#{Jid => [ Uri || {Uri, _Title} <- UriTitleList ]},
|
||||
{Defs2, Assigns2}
|
||||
end,
|
||||
{maps:new(), maps:new()},
|
||||
HatsUsers),
|
||||
[{hats_users, maps:to_list(HatsUsers2)},
|
||||
{hats_defs, maps:to_list(HatsDefs)}]
|
||||
end;
|
||||
(Opt) ->
|
||||
Opt
|
||||
[Opt]
|
||||
end, Opts).
|
||||
|
||||
export(LServer) ->
|
||||
@@ -1766,7 +1811,7 @@ mod_doc() ->
|
||||
"The default value is an empty string.")}},
|
||||
{enable_hats,
|
||||
#{value => "true | false",
|
||||
note => "improved in 25.03",
|
||||
note => "improved in 25.10",
|
||||
desc =>
|
||||
?T("Allow extended roles as defined in XEP-0317 Hats. "
|
||||
"Check the _`../../tutorials/muc-hats.md|MUC Hats`_ tutorial. "
|
||||
|
||||
+92
-3
@@ -32,6 +32,8 @@
|
||||
muc_online_rooms/1, muc_online_rooms_by_regex/2,
|
||||
muc_register_nick/3, muc_register_nick/4,
|
||||
muc_unregister_nick/2, muc_unregister_nick/3,
|
||||
muc_get_registered_nick/3,
|
||||
muc_get_registered_nicks/1,
|
||||
create_room_with_opts/4, create_room/3, destroy_room/2,
|
||||
create_rooms_file/1, destroy_rooms_file/1,
|
||||
rooms_unused_list/2, rooms_unused_destroy/2,
|
||||
@@ -159,6 +161,30 @@ get_commands_spec() ->
|
||||
args = [{user, binary}, {host, binary}, {service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = muc_get_registered_nick, tags = [muc],
|
||||
desc = "Get nick registered for that account in the MUC service",
|
||||
module = ?MODULE, function = muc_get_registered_nick,
|
||||
note = "added in 25.10",
|
||||
args_desc = ["user name", "user host", "MUC service"],
|
||||
args_example = [<<"tim">>, <<"example.org">>, <<"conference.example.org">>],
|
||||
args = [{user, binary}, {host, binary}, {service, binary}],
|
||||
result_desc = "nick registered",
|
||||
result_example = ["Tim"],
|
||||
result = {nick, string}},
|
||||
#ejabberd_commands{name = muc_get_registered_nicks, tags = [muc],
|
||||
desc = "List all nicks registered in the MUC service",
|
||||
module = ?MODULE, function = muc_get_registered_nicks,
|
||||
note = "added in 25.10",
|
||||
args_desc = ["MUC service"],
|
||||
args_example = [<<"conference.example.org">>],
|
||||
args = [{service, binary}],
|
||||
result_example = [{"Tim", "timexa", "example.com"},
|
||||
{"Laia", "laia001", "example2.org"}],
|
||||
result = {registrations, {list, {registration, {tuple,
|
||||
[{user, string},
|
||||
{host, string},
|
||||
{nick, string}
|
||||
]}}}}},
|
||||
|
||||
#ejabberd_commands{name = create_room, tags = [muc_room],
|
||||
desc = "Create a MUC room name@service in host",
|
||||
@@ -696,6 +722,17 @@ muc_unregister_nick(User, Host, Service) ->
|
||||
muc_unregister_nick(FromBinary, Service) ->
|
||||
muc_register_nick(<<"">>, FromBinary, Service).
|
||||
|
||||
muc_get_registered_nick(User, Host, Service) ->
|
||||
MucServerHost = get_room_serverhost(Service),
|
||||
case mod_muc:get_register_nick(MucServerHost, Service, jid:make(User, Host)) of
|
||||
error -> <<"">>;
|
||||
N -> N
|
||||
end.
|
||||
|
||||
muc_get_registered_nicks(Service) ->
|
||||
MucServerHost = get_room_serverhost(Service),
|
||||
mod_muc:get_register_nicks(MucServerHost, Service).
|
||||
|
||||
get_user_rooms(User, Server) ->
|
||||
lists:flatmap(
|
||||
fun(ServerHost) ->
|
||||
@@ -790,9 +827,22 @@ webadmin_muc_host(_Host,
|
||||
Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>),
|
||||
Breadcrumb =
|
||||
make_breadcrumb({service_section, Level, Service, <<"Nick Register">>, RPath}),
|
||||
Set = [make_command(muc_register_nick, R, [{<<"service">>, Service}], []),
|
||||
make_command(muc_unregister_nick, R, [{<<"service">>, Service}], [])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
|
||||
Set = [make_command(muc_register_nick, R, [{<<"service">>, Service}], [])],
|
||||
%% Execute twice: first to perform the action, the second to get new roster
|
||||
_ = make_webadmin_roster_table(Service, R, RPath),
|
||||
RV2 = make_webadmin_roster_table(Service, R, RPath),
|
||||
Get = [make_command(muc_get_registered_nicks,
|
||||
R,
|
||||
[{<<"service">>, Service}],
|
||||
[{only, presentation}]),
|
||||
?XE(<<"blockquote">>,
|
||||
[make_command(muc_unregister_nick,
|
||||
R,
|
||||
[{<<"service">>, Service}],
|
||||
[{only, presentation}])]),
|
||||
RV2],
|
||||
Title ++ Breadcrumb ++ Set ++ Get;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
[<<"rooms-empty">> | RPath],
|
||||
@@ -1263,6 +1313,45 @@ web_page_hostuser(Acc, _, _, _) ->
|
||||
Acc.
|
||||
%% @format-end
|
||||
|
||||
%%--------------------
|
||||
%% Web Admin Nick Register
|
||||
|
||||
%% @format-begin
|
||||
make_webadmin_roster_table(Service, R, RPath) ->
|
||||
Nicks =
|
||||
case make_command_raw_value(muc_get_registered_nicks, R, [{<<"service">>, Service}]) of
|
||||
Ns when is_list(Ns) ->
|
||||
Ns;
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
Level = 4 + length(RPath),
|
||||
Columns = [<<"user@host">>, <<"nick">>, <<"">>],
|
||||
Rows =
|
||||
lists:map(fun({User, Host, Nick}) ->
|
||||
{make_command(echo,
|
||||
R,
|
||||
[{<<"sentence">>,
|
||||
jid:encode(
|
||||
jid:make(User, Host))}],
|
||||
[{only, raw_and_value},
|
||||
{result_links, [{sentence, user, Level, <<"">>}]}]),
|
||||
?C(Nick),
|
||||
make_command(muc_unregister_nick,
|
||||
R,
|
||||
[{<<"user">>, User},
|
||||
{<<"host">>, Host},
|
||||
{<<"service">>, Service}],
|
||||
[{only, button},
|
||||
{style, danger},
|
||||
{input_name_append, [User, Host, Service]}])}
|
||||
end,
|
||||
lists:keysort(1, Nicks)),
|
||||
Table = make_table(20, RPath, Columns, Rows),
|
||||
?XE(<<"blockquote">>, [Table]).
|
||||
%% @format-end
|
||||
|
||||
|
||||
%%----------------------------
|
||||
%% Create/Delete Room
|
||||
%%----------------------------
|
||||
|
||||
+43
-5
@@ -29,7 +29,7 @@
|
||||
|
||||
%% API
|
||||
-export([init/2, import/3, store_room/5, restore_room/3, forget_room/3,
|
||||
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]).
|
||||
can_use_nick/4, get_rooms/2, get_nick/3, get_nicks/2, set_nick/4]).
|
||||
-export([register_online_room/4, unregister_online_room/4, find_online_room/3,
|
||||
get_online_rooms/3, count_online_rooms/2, rsm_supported/0,
|
||||
register_online_user/4, unregister_online_user/4,
|
||||
@@ -122,6 +122,14 @@ get_nick(_LServer, Host, From) ->
|
||||
[#muc_registered{nick = Nick}] -> Nick
|
||||
end.
|
||||
|
||||
get_nicks(_LServer, Host) ->
|
||||
mnesia:dirty_select(muc_registered,
|
||||
[{#muc_registered{us_host = {{'$1', '$2'}, Host},
|
||||
nick = '$3', _ = '_'},
|
||||
[],
|
||||
[{{'$1', '$2', '$3'}}]
|
||||
}]).
|
||||
|
||||
set_nick(_LServer, ServiceOrRoom, From, Nick) ->
|
||||
{LUser, LServer, _} = jid:tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
@@ -429,11 +437,15 @@ need_transform({muc_room, {N, H}, _})
|
||||
?INFO_MSG("Mnesia table 'muc_room' will be converted to binary", []),
|
||||
true;
|
||||
need_transform({muc_room, {_N, _H}, Opts}) ->
|
||||
case lists:keymember(allow_private_messages, 1, Opts) of
|
||||
true ->
|
||||
case {lists:keymember(allow_private_messages, 1, Opts),
|
||||
lists:keymember(hats_defs, 1, Opts)} of
|
||||
{true, _} ->
|
||||
?INFO_MSG("Mnesia table 'muc_room' will be converted to allowpm", []),
|
||||
true;
|
||||
false ->
|
||||
{false, false} ->
|
||||
?INFO_MSG("Mnesia table 'muc_room' will be converted to Hats 0.3.0", []),
|
||||
true;
|
||||
{false, true} ->
|
||||
false
|
||||
end;
|
||||
|
||||
@@ -459,7 +471,33 @@ transform(#muc_room{opts = Opts} = R) ->
|
||||
_ ->
|
||||
Opts
|
||||
end,
|
||||
R#muc_room{opts = Opts2};
|
||||
Opts4 =
|
||||
case lists:keyfind(hats_defs, 1, Opts2) of
|
||||
false ->
|
||||
{hats_users, HatsUsers} = lists:keyfind(hats_users, 1, Opts2),
|
||||
{HatsDefs, HatsUsers2} =
|
||||
lists:foldl(fun({Jid, UriTitleList}, {Defs, Assigns}) ->
|
||||
Defs2 =
|
||||
lists:foldl(fun({Uri, Title}, AccDef) ->
|
||||
maps:put(Uri, {Title, <<"">>}, AccDef)
|
||||
end,
|
||||
Defs,
|
||||
UriTitleList),
|
||||
Assigns2 =
|
||||
maps:put(Jid,
|
||||
[Uri || {Uri, _Title} <- UriTitleList],
|
||||
Assigns),
|
||||
{Defs2, Assigns2}
|
||||
end,
|
||||
{maps:new(), maps:new()},
|
||||
HatsUsers),
|
||||
Opts3 =
|
||||
lists:keyreplace(hats_users, 1, Opts2, {hats_users, maps:to_list(HatsUsers2)}),
|
||||
[{hats_defs, maps:to_list(HatsDefs)} | Opts3];
|
||||
{_, _} ->
|
||||
Opts2
|
||||
end,
|
||||
R#muc_room{opts = Opts4};
|
||||
transform(#muc_registered{us_host = {{U, S}, H}, nick = Nick} = R) ->
|
||||
R#muc_registered{us_host = {{iolist_to_binary(U), iolist_to_binary(S)},
|
||||
iolist_to_binary(H)},
|
||||
|
||||
+538
-330
File diff suppressed because it is too large
Load Diff
+18
-1
@@ -31,7 +31,7 @@
|
||||
%% API
|
||||
-export([init/2, store_room/5, store_changes/4,
|
||||
restore_room/3, forget_room/3,
|
||||
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4,
|
||||
can_use_nick/4, get_rooms/2, get_nick/3, get_nicks/2, set_nick/4,
|
||||
import/3, export/1]).
|
||||
-export([register_online_room/4, unregister_online_room/4, find_online_room/3,
|
||||
get_online_rooms/3, count_online_rooms/2, rsm_supported/0,
|
||||
@@ -344,6 +344,23 @@ get_nick(LServer, Host, From) ->
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
get_nicks(LServer, Host) ->
|
||||
case catch ejabberd_sql:sql_query(LServer,
|
||||
?SQL("select @(jid)s, @(nick)s from muc_registered where"
|
||||
" host=%(Host)s"))
|
||||
of
|
||||
{selected, Results} ->
|
||||
lists:map(fun({JidBin, Nick}) ->
|
||||
{User, Server, _Resource} =
|
||||
jid:tolower(
|
||||
jid:decode(JidBin)),
|
||||
{User, Server, Nick}
|
||||
end,
|
||||
Results);
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
set_nick(LServer, ServiceOrRoom, From, Nick) ->
|
||||
JID = jid:encode(jid:tolower(jid:remove_resource(From))),
|
||||
F = fun () ->
|
||||
|
||||
+36
-1
@@ -37,7 +37,8 @@
|
||||
remove_user/2, get_data/2, get_data/3, export/1, mod_doc/0,
|
||||
import/5, import_start/2, mod_opt_type/1, set_data/2,
|
||||
mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6,
|
||||
pubsub_delete_item/5, pubsub_tree_call/4]).
|
||||
pubsub_delete_item/5, pubsub_tree_call/4,
|
||||
del_data/3, get_users_with_data/2, count_users_with_data/2]).
|
||||
|
||||
-export([get_commands_spec/0, bookmarks_to_pep/2]).
|
||||
|
||||
@@ -64,6 +65,9 @@
|
||||
-callback del_data(binary(), binary()) -> ok | {error, any()}.
|
||||
-callback use_cache(binary()) -> boolean().
|
||||
-callback cache_nodes(binary()) -> [node()].
|
||||
-callback del_data(binary(), binary(), binary()) -> ok | {error, any()}.
|
||||
-callback get_users_with_data(binary(), binary()) -> {ok, [binary()]} | {error, any()}.
|
||||
-callback count_users_with_data(binary(), binary()) -> {ok, integer()} | {error, any()}.
|
||||
|
||||
-optional_callbacks([use_cache/1, cache_nodes/1]).
|
||||
|
||||
@@ -273,6 +277,37 @@ get_data(LUser, LServer) ->
|
||||
{error, _} = Err -> Err
|
||||
end.
|
||||
|
||||
-spec del_data(binary(), binary(), binary()) -> ok | {error, _}.
|
||||
del_data(LUser, LServer, NS) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:del_data(LUser, LServer, NS) of
|
||||
ok ->
|
||||
case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
delete_cache(Mod, LUser, LServer, [{NS, #xmlel{}}]);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
-spec get_users_with_data(binary(), binary()) -> [jid()] | {error, any()}.
|
||||
get_users_with_data(LServer, NS) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:get_users_with_data(LServer, NS) of
|
||||
{ok, Users} ->
|
||||
[jid:make(User, LServer) || User <- Users];
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
-spec count_users_with_data(binary(), binary()) -> integer() | {error, any()}.
|
||||
count_users_with_data(LServer, NS) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:count_users_with_data(LServer, NS) of
|
||||
{ok, Num} -> Num;
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
-spec remove_user(binary(), binary()) -> ok.
|
||||
remove_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
|
||||
%% API
|
||||
-export([init/2, set_data/3, get_data/3, get_all_data/2, del_data/2,
|
||||
use_cache/1, import/3]).
|
||||
del_data/3, get_users_with_data/2, count_users_with_data/2,
|
||||
use_cache/1, import/3]).
|
||||
-export([need_transform/1, transform/1]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
@@ -101,6 +102,29 @@ del_data(LUser, LServer) ->
|
||||
end,
|
||||
transaction(F).
|
||||
|
||||
-spec del_data(binary(), binary(), binary()) -> ok | {error, any()}.
|
||||
del_data(LUser, LServer, NS) ->
|
||||
F = fun () ->
|
||||
mnesia:delete({private_storage, {LUser, LServer, NS}})
|
||||
end,
|
||||
transaction(F).
|
||||
|
||||
-spec get_users_with_data(binary(), binary()) -> {ok, [binary()]} | {error, any()}.
|
||||
get_users_with_data(LServer, NS) ->
|
||||
Val = mnesia:dirty_select(private_storage,
|
||||
[{#private_storage{usns =
|
||||
{'$1',
|
||||
LServer,
|
||||
NS},
|
||||
_ = '_'},
|
||||
[], ['$1']}]),
|
||||
{ok, Val}.
|
||||
|
||||
-spec count_users_with_data(binary(), binary()) -> {ok, integer()} | {error, any()}.
|
||||
count_users_with_data(LServer, NS) ->
|
||||
{ok, Val} = get_users_with_data(LServer, NS),
|
||||
{ok, length(Val)}.
|
||||
|
||||
import(LServer, <<"private_storage">>,
|
||||
[LUser, XMLNS, XML, _TimeStamp]) ->
|
||||
El = #xmlel{} = fxml_stream:parse_element(XML),
|
||||
|
||||
+38
-1
@@ -27,7 +27,8 @@
|
||||
|
||||
%% API
|
||||
-export([init/2, set_data/3, get_data/3, get_all_data/2, del_data/2,
|
||||
import/3, export/1]).
|
||||
del_data/3, get_users_with_data/2, count_users_with_data/2,
|
||||
import/3, export/1]).
|
||||
-export([sql_schemas/0]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
@@ -124,6 +125,42 @@ del_data(LUser, LServer) ->
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
-spec del_data(binary(), binary(), binary()) -> ok | {error, any()}.
|
||||
del_data(LUser, LServer, NS) ->
|
||||
case ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("delete from private_storage"
|
||||
" where username=%(LUser)s and namespace=%(NS)s and %(LServer)H")) of
|
||||
{updated, _} ->
|
||||
ok;
|
||||
_ ->
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
-spec get_users_with_data(binary(), binary()) -> {ok, [binary()]} | {error, any()}.
|
||||
get_users_with_data(LServer, NS) ->
|
||||
case ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(username)s from private_storage"
|
||||
" where namespace=%(NS)s and %(LServer)H")) of
|
||||
{selected, Value} ->
|
||||
{ok, Value};
|
||||
_ ->
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
-spec count_users_with_data(binary(), binary()) -> {ok, integer()} | {error, any()}.
|
||||
count_users_with_data(LServer, NS) ->
|
||||
case ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(count(*))d from private_storage"
|
||||
" where namespace=%(NS)s and %(LServer)H")) of
|
||||
{selected, Value} ->
|
||||
{ok, Value};
|
||||
_ ->
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
export(_Server) ->
|
||||
[{private_storage,
|
||||
fun(Host, #private_storage{usns = {LUser, LServer, XMLNS},
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
-include("logger.hrl").
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_proxy65_service).
|
||||
|
||||
@@ -86,8 +86,8 @@ terminate(_Reason, #state{myhosts = MyHosts}) ->
|
||||
|
||||
handle_info({route, Packet}, State) ->
|
||||
try route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
|
||||
+20
-18
@@ -46,7 +46,7 @@
|
||||
-include("pubsub.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
|
||||
-define(STDTREE, <<"tree">>).
|
||||
@@ -748,11 +748,11 @@ handle_cast(Msg, State) ->
|
||||
|
||||
handle_info({route, Packet}, State) ->
|
||||
try route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info(Info, State) ->
|
||||
@@ -3819,9 +3819,9 @@ tree_action(Host, Function, Args) ->
|
||||
DBType = mod_pubsub_opt:db_type(ServerHost),
|
||||
Fun = fun () ->
|
||||
try tree_call(Host, Function, Args)
|
||||
catch ?EX_RULE(Class, Reason, St) when DBType == sql ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
ejabberd_sql:abort({exception, Class, Reason, StackTrace})
|
||||
catch
|
||||
Class:Reason:StackTrace when DBType == sql ->
|
||||
ejabberd_sql:abort({exception, Class, Reason, StackTrace})
|
||||
end
|
||||
end,
|
||||
Ret = case DBType of
|
||||
@@ -3919,15 +3919,17 @@ transaction(Host, Fun, Trans) ->
|
||||
do_transaction(ServerHost, Fun, Trans, DBType) ->
|
||||
F = fun() ->
|
||||
try Fun()
|
||||
catch ?EX_RULE(Class, Reason, St) when (DBType == mnesia andalso
|
||||
Trans == transaction) orelse
|
||||
DBType == sql ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
Ex = {exception, Class, Reason, StackTrace},
|
||||
case DBType of
|
||||
mnesia -> mnesia:abort(Ex);
|
||||
sql -> ejabberd_sql:abort(Ex)
|
||||
end
|
||||
catch
|
||||
exit:{aborted, _} = Err when DBType == mnesia ->
|
||||
exit(Err);
|
||||
Class:Reason:StackTrace when (DBType == mnesia andalso
|
||||
Trans == transaction) orelse
|
||||
DBType == sql ->
|
||||
Ex = {exception, Class, Reason, StackTrace},
|
||||
case DBType of
|
||||
mnesia -> mnesia:abort(Ex);
|
||||
sql -> ejabberd_sql:abort(Ex)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Res = case DBType of
|
||||
|
||||
+14
-6
@@ -526,12 +526,20 @@ notify(LServer, PushLJID, Node, XData, Pkt0, Dir, HandleResponse) ->
|
||||
Item = #ps_item{sub_els = [#push_notification{xdata = Summary}]},
|
||||
PubSub = #pubsub{publish = #ps_publish{node = Node, items = [Item]},
|
||||
publish_options = XData},
|
||||
IQ = #iq{type = set,
|
||||
from = From,
|
||||
to = jid:make(PushLJID),
|
||||
id = p1_rand:get_string(),
|
||||
sub_els = [PubSub]},
|
||||
ejabberd_router:route_iq(IQ, HandleResponse)
|
||||
IQ0 = #iq{type = set,
|
||||
from = From,
|
||||
to = jid:make(PushLJID),
|
||||
id = p1_rand:get_string(),
|
||||
sub_els = [PubSub]},
|
||||
case ejabberd_hooks:run_fold(push_send_notification,
|
||||
LServer, IQ0, [Pkt]) of
|
||||
drop ->
|
||||
?DEBUG("Notification dropped by hook", []),
|
||||
ok;
|
||||
IQ ->
|
||||
?DEBUG("Sending notification: ~n~ts", [xmpp:pp(IQ)]),
|
||||
ejabberd_router:route_iq(IQ, HandleResponse)
|
||||
end
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
+2
-2
@@ -61,7 +61,7 @@
|
||||
-include("mod_roster.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("translate.hrl").
|
||||
|
||||
-define(ROSTER_CACHE, roster_cache).
|
||||
@@ -1192,7 +1192,7 @@ import_stop(_LServer, _DBType) ->
|
||||
ok.
|
||||
|
||||
row_length() ->
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true -> 10;
|
||||
false -> 9
|
||||
end.
|
||||
|
||||
+6
-6
@@ -51,7 +51,7 @@
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("mod_vcard.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
|
||||
@@ -151,11 +151,11 @@ handle_cast(Cast, State) ->
|
||||
|
||||
handle_info({route, Packet}, State) ->
|
||||
try route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
catch
|
||||
Class:Reason:StackTrace ->
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info(Info, State) ->
|
||||
|
||||
@@ -332,7 +332,7 @@ make_matchspec(LServer, Data) ->
|
||||
filter_fields(Data, <<"">>, LServer).
|
||||
|
||||
filter_fields([], Match, LServer) ->
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true ->
|
||||
SQLType = ejabberd_option:sql_type(LServer),
|
||||
SServer = ejabberd_sql:to_string_literal(SQLType, LServer),
|
||||
|
||||
@@ -71,7 +71,13 @@ end_per_suite(_Config) ->
|
||||
application:stop(ejabberd).
|
||||
|
||||
init_per_group(Group, Config) ->
|
||||
case lists:member(Group, ?BACKENDS) of
|
||||
BackendOfGroup = case Group of
|
||||
component -> agnostic;
|
||||
no_db -> agnostic;
|
||||
s2s -> agnostic;
|
||||
_ -> Group
|
||||
end,
|
||||
case lists:member(BackendOfGroup, ?BACKENDS) of
|
||||
false ->
|
||||
%% Not a backend related group, do default init:
|
||||
do_init_per_group(Group, Config);
|
||||
@@ -82,7 +88,7 @@ init_per_group(Group, Config) ->
|
||||
do_init_per_group(Group, Config);
|
||||
Backends ->
|
||||
%% Skipped backends that were not explicitly enabled
|
||||
case lists:member(Group, Backends) of
|
||||
case lists:member(BackendOfGroup, Backends) of
|
||||
true ->
|
||||
do_init_per_group(Group, Config);
|
||||
false ->
|
||||
@@ -1089,7 +1095,7 @@ update_sql(Host, Config) ->
|
||||
end.
|
||||
|
||||
schema_suffix(Config) ->
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
case ejabberd_sql:use_multihost_schema() of
|
||||
true ->
|
||||
case ?config(update_sql, Config) of
|
||||
true -> ".sql";
|
||||
|
||||
@@ -165,7 +165,7 @@ shaper:
|
||||
certfiles:
|
||||
- CERTFILE
|
||||
|
||||
new_sql_schema: NEW_SCHEMA
|
||||
sql_schema_multihost: MULTIHOST_SCHEMA
|
||||
|
||||
update_sql_schema: UPDATE_SQL_SCHEMA
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ define_macro:
|
||||
PRIV_DIR: "@@priv_dir@@"
|
||||
PUT_URL: "http://upload.@HOST@:@@web_port@@/upload"
|
||||
GET_URL: "http://upload.@HOST@:@@web_port@@/upload"
|
||||
NEW_SCHEMA: @@new_schema@@
|
||||
MULTIHOST_SCHEMA: @@multihost_schema@@
|
||||
UPDATE_SQL_SCHEMA: @@update_sql_schema@@
|
||||
MYSQL_USER: "@@mysql_user@@"
|
||||
MYSQL_SERVER: "@@mysql_server@@"
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/perl -p0
|
||||
|
||||
use strict;
|
||||
|
||||
sub sp{
|
||||
my $s=trim(shift);
|
||||
my @r;
|
||||
push @r, trim($1) while $s =~ m/
|
||||
(
|
||||
(?:
|
||||
[^(),]+ |
|
||||
(?: \( \) ) |
|
||||
\(
|
||||
( [^()]+ | \( (?2) \) )+
|
||||
\)
|
||||
)+
|
||||
)
|
||||
/gx;
|
||||
return join(",\n",sort(@r))
|
||||
}
|
||||
|
||||
sub trim {
|
||||
my $s = shift;
|
||||
$s =~ s/^\s+|\s+$//g;
|
||||
return $s;
|
||||
}
|
||||
|
||||
s/--.*\n//gm;
|
||||
|
||||
s/
|
||||
(
|
||||
[^()]+ |
|
||||
(?: \( \) )
|
||||
) |
|
||||
\(
|
||||
(
|
||||
(?: (?R) | [^()]+ )+
|
||||
)
|
||||
\)/ if ($2) {
|
||||
"(\n".sp($2)."\n)"
|
||||
} else {
|
||||
$1
|
||||
}/gsxe;
|
||||
+3
-3
@@ -63,7 +63,7 @@ init_config(Config) ->
|
||||
MacrosContentTpl,
|
||||
[{c2s_port, 5222},
|
||||
{loglevel, 4},
|
||||
{new_schema, false},
|
||||
{multihost_schema, false},
|
||||
{update_sql_schema, true},
|
||||
{s2s_port, 5269},
|
||||
{stun_port, 3478},
|
||||
@@ -214,7 +214,7 @@ setup_ejabberd_lib_path(Config) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% Read environment variable CT_DB=mysql to limit the backends to test.
|
||||
%% Read environment variable CT_BACKENDS=mysql to limit the backends to test.
|
||||
%% You can thus limit the backend you want to test with:
|
||||
%% CT_BACKENDS=mysql rebar ct suites=ejabberd
|
||||
get_config_backends() ->
|
||||
@@ -229,7 +229,7 @@ get_config_backends() ->
|
||||
end,
|
||||
application:load(ejabberd),
|
||||
EnabledBackends = application:get_env(ejabberd, enabled_backends, EnvBackends),
|
||||
misc:intersection(EnvBackends, [mnesia, ldap, extauth|EnabledBackends]).
|
||||
misc:intersection(EnvBackends, [agnostic, mnesia, ldap, extauth|EnabledBackends]).
|
||||
|
||||
process_config_tpl(Content, []) ->
|
||||
Content;
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@
|
||||
-define(S2S_VHOST, <<"s2s.localhost">>).
|
||||
-define(UPLOAD_VHOST, <<"upload.localhost">>).
|
||||
|
||||
-define(BACKENDS, [mnesia, redis, mysql, mssql, odbc, pgsql, sqlite, ldap, extauth]).
|
||||
-define(BACKENDS, [agnostic, mnesia, redis, mysql, mssql, pgsql, sqlite, ldap, extauth]).
|
||||
|
||||
insert(Val, N, Tuple) ->
|
||||
L = tuple_to_list(Tuple),
|
||||
|
||||
+5
-5
@@ -67,18 +67,18 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:]
|
||||
mix_vsn=$(mix_version "$rel_vsn")
|
||||
crosstool_vsn='1.27.0'
|
||||
termcap_vsn='1.3.1'
|
||||
expat_vsn='2.7.1'
|
||||
expat_vsn='2.7.3'
|
||||
zlib_vsn='1.3.1'
|
||||
yaml_vsn='0.2.5'
|
||||
ssl_vsn='3.5.2'
|
||||
otp_vsn='27.3.4.2'
|
||||
ssl_vsn='3.5.4'
|
||||
otp_vsn='27.3.4.3'
|
||||
elixir_vsn='1.18.4'
|
||||
pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet.
|
||||
png_vsn='1.6.45'
|
||||
jpeg_vsn='9f'
|
||||
webp_vsn='1.5.0'
|
||||
gd_vsn='2.3.3'
|
||||
odbc_vsn='2.3.12'
|
||||
odbc_vsn='2.3.14'
|
||||
sqlite_vsn='3490000'
|
||||
root_dir="${BUILD_DIR:-$HOME/build}"
|
||||
bootstrap_dir="$root_dir/bootstrap"
|
||||
@@ -118,7 +118,7 @@ rel_tar="$rel_name-$mix_vsn.tar.gz"
|
||||
ct_jobs=$(nproc)
|
||||
src_dir="$root_dir/src"
|
||||
platform=$(gcc -dumpmachine)
|
||||
targets='x86_64-linux-gnu aarch64-linux-gnu'
|
||||
targets="$platform"
|
||||
build_start=$(date '+%F %T')
|
||||
have_current_deps='false'
|
||||
dep_vsns_file="$build_dir/.dep_vsns"
|
||||
|
||||
+13
-1
@@ -23,8 +23,20 @@ set -e
|
||||
set -u
|
||||
|
||||
myself=${0##*/}
|
||||
architectures='x64 arm64'
|
||||
iteration=1
|
||||
platform=$(gcc -dumpmachine)
|
||||
case $platform in
|
||||
x86_64*)
|
||||
architectures='x64'
|
||||
;;
|
||||
aarch64*)
|
||||
architectures='arm64'
|
||||
;;
|
||||
*)
|
||||
error "Unsupported architecture: $platform"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
usage()
|
||||
{
|
||||
|
||||
+16
-1
@@ -23,8 +23,20 @@ set -e
|
||||
set -u
|
||||
|
||||
myself=${0##*/}
|
||||
architectures='x64 arm64'
|
||||
iteration=1
|
||||
platform=$(gcc -dumpmachine)
|
||||
case $platform in
|
||||
x86_64*)
|
||||
architectures='x64'
|
||||
;;
|
||||
aarch64*)
|
||||
architectures='arm64'
|
||||
;;
|
||||
*)
|
||||
error "Unsupported architecture: $platform"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
usage()
|
||||
{
|
||||
@@ -145,6 +157,9 @@ package_architecture()
|
||||
local host_target="$(uname -m)-$target"
|
||||
|
||||
case $host_target in
|
||||
aarch64-arm64)
|
||||
printf 'native'
|
||||
;;
|
||||
x86_64-x64)
|
||||
printf 'native'
|
||||
;;
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
{roster_gateway_workaround, @roster_gateway_workaround@}.
|
||||
{full_xml, @full_xml@}.
|
||||
{debug, @debug@}.
|
||||
{new_sql_schema, @new_sql_schema@}.
|
||||
{multihost_sql_schema, @multihost_sql_schema@}.
|
||||
|
||||
{tools, @tools@}.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user