Compare commits

...

68 Commits

Author SHA1 Message Date
Alexey Shchepin 3a36a722c5 Fix a bug in mod_matrix_gw_room:check_event_power_level/3
CI / Tests (25) (push) Failing after 1s
CI / Tests (26) (push) Failing after 0s
CI / Tests (27) (push) Failing after 0s
CI / Tests (28) (push) Failing after 1s
Container / Container (push) Failing after 50s
Installers / Binaries (push) Failing after 49s
Runtime / Rebars (24, rebar3) (push) Failing after 6s
Runtime / Rebars (25, rebar) (push) Failing after 5s
Runtime / Rebars (25, rebar3) (push) Failing after 4s
Runtime / Rebars (26, rebar) (push) Failing after 4s
Runtime / Rebars (26, rebar3) (push) Failing after 4s
Runtime / Rebars (27, rebar3) (push) Failing after 5s
Runtime / Rebars (28, rebar3) (push) Failing after 5s
Runtime / Rebar3+Elixir (1.14) (push) Failing after 4s
Runtime / Rebar3+Elixir (1.15) (push) Failing after 4s
Runtime / Rebar3+Elixir (1.16) (push) Failing after 5s
Runtime / Rebar3+Elixir (1.17) (push) Failing after 4s
Runtime / Rebar3+Elixir (1.18) (push) Failing after 5s
Runtime / Mix (1.14) (push) Failing after 5s
Runtime / Mix (1.15) (push) Failing after 5s
Runtime / Mix (1.16) (push) Failing after 4s
Runtime / Mix (1.17) (push) Failing after 5s
Runtime / Mix (1.18) (push) Failing after 5s
Installers / Release (push) Has been skipped
2025-08-22 14:46:59 +03:00
Badlop 00c75c3dc9 Set version to 25.08 2025-08-22 11:15:33 +02:00
Badlop cae7850a70 CHANGELOG.md: Update to 25.08 2025-08-22 10:56:06 +02:00
Badlop ce668bef14 Container: Apply some improvements from ejabberd source code
Applied:
- ejabberd.yml.example: Use HOST_URL_ENCODE to handle case when vhost is non-latin1
- ejabberdctl: Improve explanation how to stop ejabberd in live mode
- ejabberdctl: New "mnesia_change" command, a frontend to mnesia_change_nodename
2025-08-22 10:56:03 +02:00
Badlop 3887b6d930 Update man page to 25.08 2025-08-21 17:12:55 +02:00
Badlop b7bd0e196d Update rebar.lock too 2025-08-21 17:12:09 +02:00
Badlop 6d63842ad3 Fix typo in hu.msg string 2025-08-21 16:24:58 +02:00
Badlop 2f3b9015e9 Update module and options version notes 2025-08-21 16:24:55 +02:00
Badlop 6ae48eb991 Result of running "make options" 2025-08-21 16:24:50 +02:00
dependabot[bot] c508795ad4 build(deps): bump golang in /.github/container
Bumps golang from 1.24-alpine to 1.25-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25-alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-21 16:24:48 +02:00
Badlop 4a053807e0 build(deps-dev): bump dialyxir from 1.4.5 to 1.4.6
Bumps [dialyxir](https://github.com/jeremyjh/dialyxir) from 1.4.5 to 1.4.6.
- [Release notes](https://github.com/jeremyjh/dialyxir/releases)
- [Changelog](https://github.com/jeremyjh/dialyxir/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jeremyjh/dialyxir/compare/1.4.5...1.4.6)

---
updated-dependencies:
- dependency-name: dialyxir
  dependency-version: 1.4.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-21 16:24:46 +02:00
dependabot[bot] dd5bbda2dc build(deps): bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-21 16:24:44 +02:00
Badlop 98469678a0 ejabberd_listener: Add secret in temporary unix domain socket path (#4422) 2025-08-21 16:24:42 +02:00
Badlop b8550e087e mod_conversejs: Ensure plugins URL is separated with / (#4413) 2025-08-21 16:24:39 +02:00
Holger Weiss 644d468b4f Update registration test
Adjust test case for commit 654d4b81b1.
2025-08-21 09:51:24 +02:00
Pawel Chmielowski 74c810eeaa Tag dependencies 2025-08-20 13:46:55 +02:00
Holger Weiss a46325166a mod_register: Don't duplicate welcome subject
Don't include the configured welcome message subject with the body.  If
that's desired, the admin can simply configure it that way.  But if it's
undesired, there would be no way to avoid the subject duplication.
2025-08-19 20:09:17 +02:00
Holger Weiss 654d4b81b1 mod_register: Don't duplicate welcome message
Originally, the welcome message was sent as type 'normal'.  Apparently,
some clients don't display 'normal' messages as expected (see #4246).
To address that issue, commit 9a0ff13cc2
duplicated the welcome message as type 'chat'.  However, we shouldn't
send both formats.  The 'normal' message is either ignored by the
client, in which case it serves no purpose, or displayed, in which case
the user would see the message twice.
2025-08-19 20:03:01 +02:00
Holger Weiss ff3d33dde4 Bump xmpp version
Allow for adding HTTP File Upload purposes support to ejabberd.
2025-08-18 16:23:33 +02:00
Badlop 3183e2f733 Fix dialyzer warnings in recent commit 2025-08-15 16:33:05 +02:00
Badlop e1dc686ae7 mod_conversejs: Ensure assets_path ends in / as required by Converse (#4414) 2025-08-15 15:20:35 +02:00
Badlop 38b203feb1 ejabberd_listener: Use init_fail for errors as recommended by init_ack
That is recommended since OTP 26, see
 https://www.erlang.org/doc/apps/stdlib/proc_lib.html#init_ack/2
 Warning
 Do not use this function to return an error indicating that the process
 start failed. When doing so the start function can return before the
 failing process has exited, which may block VM resources required for a
 new start attempt to succeed. Use init_fail/2,3 for that purpose.
2025-08-15 15:20:33 +02:00
Alexey Shchepin 8b61cf0742 Don't send empty direct Matrix messages (thanks to snoopcatt) (#4420) 2025-08-15 04:52:07 +03:00
Alexey Shchepin a02c75aa08 Add support for null values in is_canonical_json (thanks to snoopcatt) (#4421) 2025-08-15 04:52:07 +03:00
Alexey Shchepin 51af393baa Add leave_timeout mod_matrix_gw option (#4386) 2025-08-15 04:52:07 +03:00
Badlop 41318e45a5 mod_conversejs: Add option conversejs_plugins (#4413) 2025-08-14 19:05:12 +02:00
badlop a94f227103 Merge pull request #4425 from guusdk/xmpp-interop-testing-v1.6.0
CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action
2025-08-14 11:37:51 +02:00
Badlop 517776acd4 COMPILE.md: Mention dependencies and add link to Docs (#4431) 2025-08-13 18:17:26 +02:00
dependabot[bot] 212a5ded6e build(deps): bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-13 18:17:05 +02:00
Badlop fd9c929e37 Bump OpenSSL version to 3.5.2 2025-08-13 16:50:18 +02:00
Badlop ce828163af Bump Erlang/OTP version to 27.3.4.2 2025-08-13 16:50:14 +02:00
Badlop 97e1b419a0 mod_providers: New module to serve easily XMPP Providers files 2025-08-13 16:49:42 +02:00
Badlop d70ac7f7c5 ejabberd_logger: Print log lines colorized in console when using rebar3 2025-08-13 16:49:42 +02:00
Badlop 7065cb69f1 ejabberdctl: New "mnesia_change" command, a frontend to mnesia_change_nodename 2025-08-13 16:49:42 +02:00
Badlop 7815463ba0 ejabberd.yml.example: Use HOST_URL_ENCODE to handle case when vhost is non-latin1 2025-08-13 16:49:42 +02:00
Badlop 48e6631751 mod_muc_room: Fix warning about unused variable 2025-08-13 16:49:37 +02:00
Badlop 903e6b70b4 mod_matrix_gw: Document what room versions are supported since when 2025-08-13 16:48:01 +02:00
Alexey Shchepin 5edba59b24 Fix dialyzer errors 2025-08-11 20:24:59 +03:00
Alexey Shchepin 31cb4b06e4 Matrix gateway updates
- Partially rewritten state resolution
- Support for Hydra rooms
- Use double colon for separating a matrix server from a room ID in JID
  with Hydra rooms
- Partially rewritten mod_matrix_gw_s2s
- Add notary_servers option
2025-08-11 19:44:50 +03:00
Pawel Chmielowski 10f6723f00 Prevent loops in xml_compress:decode with corrupted data 2025-08-07 13:49:10 +02:00
Pawel Chmielowski f594620c68 Only offer upgrades to methods that aren't already stored 2025-08-05 11:06:17 +02:00
Pawel Chmielowski dacfad61d8 Fix format of passwords updates triggered by mod_scram_upgrade 2025-08-05 11:02:33 +02:00
badlop 7c1da7e0cf Merge pull request #4412 from marc0s/issue-4410
fix: unsubscribe users from members-only rooms when expelled
2025-08-01 12:38:37 +02:00
marc0s e709f99b47 fix: unsubscribe users from members-only rooms when expelled
Fixes #4410
2025-07-31 08:05:32 +02:00
Guus der Kinderen f150419891 CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action
Updates this GitHub Action that's used to execute XMPP-based interop tests from v1.5.0 to v1.6.0.

In this update, 524 new tests were added (more than doubling the amount of tests that previously existed).
2025-07-30 22:44:54 +02:00
Badlop 053fd26994 econf: If a host in configuration is encoded IDNA, decode it (#3519)
For example:

hosts:
  - localhost
  - locälhost3
  - xn--loclhost4-x2a

all them are converted to utf8:

ejabberd_option:hosts().
[<<"localhost">>,
 <<"locälhost3"/utf8>>,
 <<"locälhost4"/utf8>>]
2025-07-26 00:43:25 +02:00
Badlop bba1a1e3ca mod_http_upload: Encode URLs into IDNA when showing to XMPP client (#3519) 2025-07-25 19:54:28 +02:00
Badlop e5da1efea4 misc: Move uri_parse/1 to yconf and merge with yconf:parse_uri/1 2025-07-25 19:54:26 +02:00
Badlop fbfd41c16e misc: uri_decode/1 moved here from ejabberd_http and prosody2ejabberd 2025-07-25 19:54:25 +02:00
Badlop 4391921727 ejabberd_config: New predefined keyword HOST_URL_ENCODE 2025-07-25 19:54:23 +02:00
Badlop 4cd3c657e2 ejabberd_listener: Try to create provisional socket in final directory (#4422)
and if that path is too long, then try HOME directory,
if that's too long too, throw error explaining the problem.

By the way, cutting the base64 string to 107 is a bad idea,
as it encodes the final path, which would get lost and crash.
2025-07-25 11:15:37 +02:00
Badlop 7647b77225 Runtime: Raise the minimum Erlang tested to Erlang/OTP 24
The Erlang containers from versions 20-23 use Debian Buster,
and require the debian repositories to install some development libraries.
The Debian Buster repositories are no longer available,
which means that we can no longer perform any test with Erlang 20-23.
2025-07-25 11:15:34 +02:00
Pawel Chmielowski fe8710fe00 Rename auth_password_types_hidden_in_scram1 option to auth_password_types_hidden_in_sasl1
Also add migration code from old name
2025-07-25 09:39:21 +02:00
Pawel Chmielowski 1a9b147baf Report db failures in mod_muc_mnesia:restore_room 2025-07-23 20:27:19 +02:00
Pawel Chmielowski 6214e0385d Report db failures from mod_muc:restore_room 2025-07-23 19:59:32 +02:00
Badlop f7002c31f0 Fix some typos in previous commit (#4422) 2025-07-22 13:30:54 +02:00
Badlop 99b75396ad ejabberd_listener: Log error when cannot set definitive unix socket (#4422) 2025-07-22 10:48:17 +02:00
Badlop b1c3baa7bd Bump p1_acme to fix 'AttributePKCS-10' and OTP 28 (processone/p1_acme#4) 2025-07-22 10:47:38 +02:00
Badlop 355eb5dfde Improve documentation of toplevel options default_db and default_ram_db 2025-07-22 10:47:36 +02:00
Badlop d269e32c3a ejabberd_config: Improve warning message about unsupported ram_db type 2025-07-22 10:47:33 +02:00
Badlop 214b76f763 ejabberd_doc: Document commands tags for modules 2025-07-22 10:47:30 +02:00
Holger Weiss 73a8fbdfb5 make-binaries: Re-add executable bit 2025-07-21 13:42:07 +02:00
Holger Weiss 9b6f0aeb3c make-binaries: Disable Linux-PAM's logind support
Make sure Linux-PAM doesn't attempt to include logind support.  This
avoids a build failure in case the host system has systemd's
development headers installed.
2025-07-21 13:38:11 +02:00
Badlop 45a6aed57f mod_admin_extra: Run sm_kick_user event when kicking account (#4415)
This is important when running the ban_account command and mod_auth_fast
is enabled, as the client may store auth tokens that bypass
the banning stored in private storage and enforced by ejabberd_auth.
2025-07-16 18:09:14 +02:00
Badlop 9d17a160b6 mod_admin_extra: No need to change password in ban_account (#4415)
When ban details are stored in private storage,
ejabberd_auth reads them and prevents user login,
so there's no need to modify the account password.
2025-07-16 13:33:40 +02:00
Badlop 850d097660 ejabberd_auth: Handle case running check_password when account is banned 2025-07-16 13:33:36 +02:00
Badlop 8ce8f67c06 misc: Add workaround for Json library not able to handle empty list 2025-07-16 13:33:33 +02:00
Pawel Chmielowski a17c2c166d Fix issue with filtering duplicates in auth_mnesia:get_users()
Previous version was only correct when data to process
was sorted, which was not always the case.

This add common implementation of lists:uniq in misc that works
also on <R25, and switches get_users to use it.
2025-07-16 12:43:25 +02:00
63 changed files with 2605 additions and 925 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
#' Define default build variables
ARG OTP_VSN='27.3.4.1'
ARG OTP_VSN='27.3.4.2'
ARG ELIXIR_VSN='1.18.4'
ARG UID='9000'
ARG USER='ejabberd'
@@ -9,7 +9,7 @@ ARG VERSION='master'
################################################################################
#' Compile ejabberdapi
FROM docker.io/golang:1.24-alpine AS api
FROM docker.io/golang:1.25-alpine AS api
RUN go install -v \
github.com/processone/ejabberd-api/cmd/ejabberd@master \
&& mv bin/ejabberd bin/ejabberdapi
+1 -1
View File
@@ -206,7 +206,7 @@ modules:
mod_fail2ban: {}
mod_http_api: {}
mod_http_upload:
put_url: https://@HOST@:5443/upload
put_url: https://@HOST_URL_ENCODE@:5443/upload
custom_headers:
"Access-Control-Allow-Origin": "https://@HOST@"
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
+122 -6
View File
@@ -195,7 +195,9 @@ livewarning()
echo "Please be extremely cautious with your actions,"
echo "and exit immediately if you are not completely sure."
echo ""
echo "To exit and detach this shell from ejabberd, press:"
echo "To stop ejabberd gracefully:"
echo " ejabberd:stop()."
echo "To quit erlang immediately, press:"
echo " control+g and then q"
echo ""
echo "--------------------------------------------------------------------"
@@ -363,6 +365,13 @@ post_waiter_loop()
# allow sync calls
wait_status()
{
wait_status_node "$ERLANG_NODE" $1 $2 $3
}
wait_status_node()
{
CONNECT_NODE=$1
shift
# args: status try delay
# return: 0 OK, 1 KO
timeout="$2"
@@ -374,9 +383,9 @@ wait_status()
status="$1"
else
run_erl "$(uid ctl)" -hidden -noinput \
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
-eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \
-s ejabberd_ctl \
-extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null
-extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null
status="$?"
fi
done
@@ -385,19 +394,26 @@ wait_status()
exec_other_command()
{
exec_other_command_node $ERLANG_NODE "$@"
}
exec_other_command_node()
{
CONNECT_NODE=$1
shift
if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \
|| [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \
|| [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then
run_erl "$(uid ctl)" -hidden -noinput \
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
-eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \
-s ejabberd_ctl \
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
-extra "$CONNECT_NODE" $NO_TIMEOUT "$@"
result=$?
case $result in
3) help;;
*) :;;
esac
exit $result
return $result
else
exec_ctl_over_http_socket "$@"
fi
@@ -439,6 +455,103 @@ cd "$SPOOL_DIR" || {
exit 6
}
printe()
{
printf "\n"
printf "\e[1;40;32m==> %s\e[0m\n" "$1"
}
## Function copied from tools/make-installers
user_agrees()
{
question="$*"
if [ -t 0 ]
then
printe "$question (y/n) [n]"
read -r response
case "$response" in
[Yy]|[Yy][Ee][Ss])
return 0
;;
[Nn]|[Nn][Oo]|'')
return 1
;;
*)
echo 'Please respond with "yes" or "no".'
user_agrees "$question"
;;
esac
else # Assume 'yes' if not running interactively.
return 0
fi
}
mnesia_change()
{
ERLANG_NODE_OLD="$1"
[ "$ERLANG_NODE_OLD" = "" ] \
&& echo "Error: Please provide the old erlang node name, for example:" \
&& echo " ejabberdctl mnesia_change ejabberd@oldmachine" \
&& exit 1
SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/
OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup
NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup
printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'"
[ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP"
if ! user_agrees "Do you want to proceed?"
then
echo 'Operation aborted.'
exit 1
fi
printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..."
exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached
wait_status_node $ERLANG_NODE_OLD 0 30 2
result=$?
case $result in
1) echo "There was a problem starting ejabberd with the old erlang node name. " \
&& echo "Check for log errors in $EJABBERD_LOG_PATH" \
&& exit $result;;
*) :;;
esac
exec_other_command_node $ERLANG_NODE_OLD "status"
printe "Making backup of old database to file $OLDFILE ..."
mkdir $SPOOL_DIR_BACKUP
exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE"
printe "Changing node name in new backup file $NEWFILE ..."
exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE"
printe "Stopping old ejabberd..."
exec_other_command_node $ERLANG_NODE_OLD "stop"
wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd
printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..."
mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP
mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP
mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP
printe "Starting ejabberd with new node name $ERLANG_NODE ..."
exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached
wait_status 0 30 2
exec_other_command "status"
printe "Installing fallback of new mnesia..."
exec_other_command install_fallback "$NEWFILE"
printe "Stopping new ejabberd..."
exec_other_command "stop"
wait_status 3 30 2 && stop_epmd
printe "Finished, now you can start ejabberd normally"
}
# main
case $1 in
start)
@@ -501,6 +614,9 @@ case $1 in
set_dist_client
wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout
;;
mnesia_change)
mnesia_change $2
;;
post_waiter)
post_waiter_waiting
;;
+2 -2
View File
@@ -35,7 +35,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Test shell scripts
if: matrix.otp == '27'
@@ -146,7 +146,7 @@ jobs:
- 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.5.0
uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.6.0
with:
domain: 'localhost'
adminAccountUsername: 'admin'
+2 -2
View File
@@ -22,12 +22,12 @@ jobs:
packages: write
steps:
- name: Check out repository code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Checkout ejabberd-contrib
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: processone/ejabberd-contrib
path: .ejabberd-modules/sources/ejabberd-contrib
+2 -2
View File
@@ -41,7 +41,7 @@ jobs:
gem install --no-document --user-install fpm
echo $HOME/.local/share/gem/ruby/*/bin >> $GITHUB_PATH
- name: Check out repository code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Build binary archives
@@ -74,7 +74,7 @@ jobs:
if: github.ref_type == 'tag'
steps:
- name: Download packages
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: ejabberd-packages
- name: Draft Release
+6 -4
View File
@@ -31,9 +31,11 @@ jobs:
strategy:
fail-fast: false
matrix:
otp: ['20', '25', '26', '27', '28']
otp: ['24', '25', '26', '27', '28']
rebar: ['rebar', 'rebar3']
exclude:
- otp: '24'
rebar: 'rebar'
- otp: '27'
rebar: 'rebar'
- otp: '28'
@@ -44,7 +46,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Get old compatible Rebar binaries
if: matrix.otp < 24
@@ -184,7 +186,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Prepare libraries
run: |
@@ -307,7 +309,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Prepare libraries
run: |
+52
View File
@@ -1,3 +1,55 @@
## Version 25.08
#### API Commands
- `ban_account`: Run `sm_kick_user` event when kicking account ([#4415](https://github.com/processone/ejabberd/issues/4415))
- `ban_account`: No need to change password ([#4415](https://github.com/processone/ejabberd/issues/4415))
- `mnesia_change`: New command in `ejabberdctl` script that helps changing the mnesia node name
#### Configuration
- Rename `auth_password_types_hidden_in_scram1` option to `auth_password_types_hidden_in_sasl1`
- `econf`: If a host in configuration is encoded IDNA, decode it ([#3519](https://github.com/processone/ejabberd/issues/3519))
- `ejabberd_config`: New predefined keyword `HOST_URL_ENCODE`
- `ejabberd.yml.example`: Use `HOST_URL_ENCODE` to handle case when vhost is non-latin1
- `mod_conversejs`: Add option `conversejs_plugins` ([#4413](https://github.com/processone/ejabberd/issues/4413))
- `mod_matrix_gw`: Add `leave_timeout` option ([#4386](https://github.com/processone/ejabberd/issues/4386))
#### Documentation and Tests
- `COMPILE.md`: Mention dependencies and add link to Docs ([#4431](https://github.com/processone/ejabberd/issues/4431))
- `ejabberd_doc`: Document commands tags for modules
- CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action ([#4425](https://github.com/processone/ejabberd/issues/4425))
- Runtime: Raise the minimum Erlang tested to Erlang/OTP 24
#### Installers and Container
- Bump Erlang/OTP version to 27.3.4.2
- Bump OpenSSL version to 3.5.2
- `make-binaries`: Disable Linux-PAM's `logind` support
#### Core and Modules
- Bump `p1_acme` to fix `'AttributePKCS-10'` and OTP 28 ([processone/p1_acme#4](https://github.com/processone/p1_acme/issues/4))
- Prevent loops in `xml_compress:decode` with corrupted data
- `ejabberd_auth_mnesia`: Fix issue with filtering duplicates in `get_users()`
- `ejabberd_listener`: Add secret in temporary unix domain socket path ([#4422](https://github.com/processone/ejabberd/issues/4422))
- `ejabberd_listener`: Log error when cannot set definitive unix socket ([#4422](https://github.com/processone/ejabberd/issues/4422))
- `ejabberd_listener`: Try to create provisional socket in final directory ([#4422](https://github.com/processone/ejabberd/issues/4422))
- `ejabberd_logger`: Print log lines colorized in console when using rebar3
- `mod_conversejs`: Ensure assets_path ends in `/` as required by Converse ([#4414](https://github.com/processone/ejabberd/issues/4414))
- `mod_conversejs`: Ensure plugins URL is separated with `/` ([#4413](https://github.com/processone/ejabberd/issues/4413))
- `mod_http_upload`: Encode URLs into IDNA when showing to XMPP client ([#3519](https://github.com/processone/ejabberd/issues/3519))
- `mod_matrix_gw`: Add support for null values in `is_canonical_json` ([#4421](https://github.com/processone/ejabberd/issues/4421))
- `mod_matrix_gw`: Don't send empty direct Matrix messages ([#4420](https://github.com/processone/ejabberd/issues/4420))
- `mod_matrix_gw`: Matrix gateway updates
- `mod_muc`: Report db failures when restoring rooms
- `mod_muc`: Unsubscribe users from members-only rooms when expelled ([#4412](https://github.com/processone/ejabberd/issues/4412))
- `mod_providers`: New module to serve easily XMPP Providers files
- `mod_register`: Don't duplicate welcome subject and message
- `mod_scram_upgrade`: Fix format of passwords updates
- `mod_scram_upgrade`: Only offer upgrades to methods that aren't already stored
## Version 25.07
#### Security fix
+5
View File
@@ -65,6 +65,11 @@ To configure the compilation, features, install paths...
./configure --help
The build tool automatically downloads and compiles the
erlang libraries that [ejabberd depends on][docs-repo].
[docs-repo]: https://docs.ejabberd.im/developer/repositories/
Install in the System
---------------------
+1 -1
View File
@@ -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.1-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.2-alpine <br /> Elixir 1.18.4 | Alpine 3.19 <br /> Erlang/OTP 26.2 <br /> Elixir 1.15.7 |
| 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 |
+1 -1
View File
@@ -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.07` | 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.08` | 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]),
+1 -1
View File
@@ -179,7 +179,7 @@ modules:
mod_fail2ban: {}
mod_http_api: {}
mod_http_upload:
put_url: https://@HOST@:5443/upload
put_url: https://@HOST_URL_ENCODE@:5443/upload
custom_headers:
"Access-Control-Allow-Origin": "https://@HOST@"
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
+119 -5
View File
@@ -318,6 +318,13 @@ check_start()
# allow sync calls
wait_status()
{
wait_status_node "$ERLANG_NODE" $1 $2 $3
}
wait_status_node()
{
CONNECT_NODE=$1
shift
# args: status try delay
# return: 0 OK, 1 KO
timeout="$2"
@@ -329,9 +336,9 @@ wait_status()
status="$1"
else
exec_erl "$(uid ctl)" -hidden -noinput \
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
-eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \
-s ejabberd_ctl \
-extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null
-extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null
status="$?"
fi
done
@@ -340,19 +347,26 @@ wait_status()
exec_other_command()
{
exec_other_command_node $ERLANG_NODE "$@"
}
exec_other_command_node()
{
CONNECT_NODE=$1
shift
if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \
|| [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \
|| [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then
exec_erl "$(uid ctl)" -hidden -noinput \
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
-eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \
-s ejabberd_ctl \
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
-extra "$CONNECT_NODE" $NO_TIMEOUT "$@"
result=$?
case $result in
3) help;;
*) :;;
esac
exit $result
return $result
else
exec_ctl_over_http_socket "$@"
fi
@@ -393,6 +407,103 @@ cd "$SPOOL_DIR" || {
exit 6
}
printe()
{
printf "\n"
printf "\e[1;40;32m==> %s\e[0m\n" "$1"
}
## Function copied from tools/make-installers
user_agrees()
{
question="$*"
if [ -t 0 ]
then
printe "$question (y/n) [n]"
read -r response
case "$response" in
[Yy]|[Yy][Ee][Ss])
return 0
;;
[Nn]|[Nn][Oo]|'')
return 1
;;
*)
echo 'Please respond with "yes" or "no".'
user_agrees "$question"
;;
esac
else # Assume 'yes' if not running interactively.
return 0
fi
}
mnesia_change()
{
ERLANG_NODE_OLD="$1"
[ "$ERLANG_NODE_OLD" = "" ] \
&& echo "Error: Please provide the old erlang node name, for example:" \
&& echo " ejabberdctl mnesia_change ejabberd@oldmachine" \
&& exit 1
SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/
OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup
NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup
printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'"
[ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP"
if ! user_agrees "Do you want to proceed?"
then
echo 'Operation aborted.'
exit 1
fi
printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..."
exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached
wait_status_node $ERLANG_NODE_OLD 0 30 2
result=$?
case $result in
1) echo "There was a problem starting ejabberd with the old erlang node name. " \
&& echo "Check for log errors in $EJABBERD_LOG_PATH" \
&& exit $result;;
*) :;;
esac
exec_other_command_node $ERLANG_NODE_OLD "status"
printe "Making backup of old database to file $OLDFILE ..."
mkdir $SPOOL_DIR_BACKUP
exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE"
printe "Changing node name in new backup file $NEWFILE ..."
exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE"
printe "Stopping old ejabberd..."
exec_other_command_node $ERLANG_NODE_OLD "stop"
wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd
printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..."
mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP
mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP
mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP
printe "Starting ejabberd with new node name $ERLANG_NODE ..."
exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached
wait_status 0 30 2
exec_other_command "status"
printe "Installing fallback of new mnesia..."
exec_other_command install_fallback "$NEWFILE"
printe "Stopping new ejabberd..."
exec_other_command "stop"
wait_status 3 30 2 && stop_epmd
printe "Finished, now you can start ejabberd normally"
}
# main
case $1 in
start)
@@ -452,6 +563,9 @@ case $1 in
set_dist_client
wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout
;;
mnesia_change)
mnesia_change $2
;;
*)
set_dist_client
exec_other_command "$@"
+31 -5
View File
@@ -39,20 +39,46 @@
-else.
-include_lib("kernel/include/logger.hrl").
-define(CLEAD, "\e[1"). % bold
-define(CMID, "\e[0"). % normal
-define(CCLEAN, "\e[0m"). % clean
-define(CDEFAULT, ";49;95m"). % light magenta
-define(CDEBUG, ";49;90m"). % dark gray
-define(CINFO, ";49;92m"). % green
-define(CWARNING, ";49;93m"). % light yellow
-define(CERROR, ";49;91m"). % light magenta
-define(CCRITICAL,";49;31m"). % light red
-define(DEBUG(Format, Args),
begin ?LOG_DEBUG(Format, Args), ok end).
begin ?LOG_DEBUG(Format, Args,
#{clevel => ?CLEAD ++ ?CDEBUG,
ctext => ?CMID ++ ?CDEBUG}),
ok end).
-define(INFO_MSG(Format, Args),
begin ?LOG_INFO(Format, Args), ok end).
begin ?LOG_INFO(Format, Args,
#{clevel => ?CLEAD ++ ?CINFO,
ctext => ?CCLEAN}),
ok end).
-define(WARNING_MSG(Format, Args),
begin ?LOG_WARNING(Format, Args), ok end).
begin ?LOG_WARNING(Format, Args,
#{clevel => ?CLEAD ++ ?CWARNING,
ctext => ?CMID ++ ?CWARNING}),
ok end).
-define(ERROR_MSG(Format, Args),
begin ?LOG_ERROR(Format, Args), ok end).
begin ?LOG_ERROR(Format, Args,
#{clevel => ?CLEAD ++ ?CERROR,
ctext => ?CMID ++ ?CERROR}),
ok end).
-define(CRITICAL_MSG(Format, Args),
begin ?LOG_CRITICAL(Format, Args), ok end).
begin ?LOG_CRITICAL(Format, Args,
#{clevel => ?CLEAD++ ?CCRITICAL,
ctext => ?CMID ++ ?CCRITICAL}),
ok end).
-endif.
%% Use only when trying to troubleshoot test problem with ExUnit
+2 -1
View File
@@ -31,5 +31,6 @@
knock_restricted_join_rule :: boolean(),
enforce_int_power_levels :: boolean(),
implicit_room_creator :: boolean(),
updated_redaction_rules :: boolean()
updated_redaction_rules :: boolean(),
hydra :: boolean()
}).
+270 -25
View File
@@ -2,12 +2,12 @@
.\" Title: ejabberd.yml
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
.\" Date: 07/11/2025
.\" Date: 08/22/2025
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
.TH "EJABBERD\&.YML" "5" "07/11/2025" "\ \&" "\ \&"
.TH "EJABBERD\&.YML" "5" "08/22/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\&.07/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\&.08/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\&.07\&. The options that changed in this version are marked with 🟤\&.
This section describes top level options of ejabberd 25\&.08\&. The options that changed in this version are marked with 🟤\&.
.PP
\fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLName|ACLDefinition}}\fR
.RS 4
@@ -461,10 +461,10 @@ option\&.
.sp
The default value is \fIplain\fR\&.
.PP
\fBauth_password_types_hidden_in_scram1 🟤\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR
\fBauth_password_types_hidden_in_sasl1\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR
.RS 4
\fINote\fR
about this option: added in 25\&.07\&. List of password types that should not be offered in SCRAM1 authenticatication\&. Because SCRAM1, unlike SCRAM2, can\(cqt have list of available mechanisms tailored to individual user, it\(cqs possible that offered mechanisms will not be compatible with stored password, especially if new password type was added recently\&. This option allows disabling offering some mechanisms in SASL1, to a time until new password type will be available for all users\&.
about this option: added in 25\&.07\&. List of password types that should not be offered in SASL1 authenticatication\&. Because SASL1, unlike SASL2, can\(cqt have list of available mechanisms tailored to individual user, it\(cqs possible that offered mechanisms will not be compatible with stored password, especially if new password type was added recently\&. This option allows disabling offering some mechanisms in SASL1, to a time until new password type will be available for all users\&.
.RE
.PP
\fBauth_scram_hash\fR: \fIsha | sha256 | sha512\fR
@@ -703,13 +703,19 @@ A list of Erlang nodes to connect on ejabberd startup\&. This option is mostly i
\fBdefault_db\fR: \fImnesia | sql\fR
.RS 4
\fIdatabase\&.md#default\-database|Default database\fR
to store persistent data in ejabberd\&. Modules and other components (e\&.g\&. authentication) may have its own value\&. The default value is
to store persistent data in ejabberd\&. Some components can be configured with specific toplevel options like
\fIoauth_db_type\fR\&. Many modules can be configured with specific module options, usually named
db_type\&. The default value is
\fImnesia\fR\&.
.RE
.PP
\fBdefault_ram_db\fR: \fImnesia | redis | sql\fR
.RS 4
Default volatile (in\-memory) storage for ejabberd\&. Modules and other components (e\&.g\&. session management) may have its own value\&. The default value is
Default volatile (in\-memory) storage for ejabberd\&. Some components can be configured with specific toplevel options like
\fIrouter_db_type\fR
and
\fIsm_db_type\fR\&. Some modules can be configured with specific module options, usually named
ram_db_type\&. The default value is
\fImnesia\fR\&.
.RE
.PP
@@ -957,7 +963,7 @@ List of one or more
option\&.
.RE
.PP
\fBhosts_alias 🟤\fR: \fI{Alias: Host}\fR
\fBhosts_alias\fR: \fI{Alias: Host}\fR
.RS 4
\fINote\fR
about this option: added in 25\&.07\&. Define aliases for existing vhosts managed by ejabberd\&. An alias may be a regexp expression\&. This option is only consulted by the
@@ -1280,7 +1286,7 @@ This option can be used to tune tick time parameter of
.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\&.07/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The
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
@@ -1547,25 +1553,25 @@ XMPP Core: section 7\&.7\&.2\&.2\&. The default value is
\fIcloseold\fR\&.
.RE
.PP
\fBrest_proxy 🟤\fR: \fIHost\fR
\fBrest_proxy\fR: \fIHost\fR
.RS 4
\fINote\fR
about this option: added in 25\&.07\&. Address of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest)
.RE
.PP
\fBrest_proxy_password 🟤\fR: \fIstring()\fR
\fBrest_proxy_password\fR: \fIstring()\fR
.RS 4
\fINote\fR
about this option: added in 25\&.07\&. Password used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest)
.RE
.PP
\fBrest_proxy_port 🟤\fR: \fI1\&.\&.65535\fR
\fBrest_proxy_port\fR: \fI1\&.\&.65535\fR
.RS 4
\fINote\fR
about this option: added in 25\&.07\&. Port of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest)
.RE
.PP
\fBrest_proxy_username 🟤\fR: \fIstring()\fR
\fBrest_proxy_username\fR: \fIstring()\fR
.RS 4
\fINote\fR
about this option: added in 25\&.07\&. Username used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest)
@@ -2101,7 +2107,7 @@ seconds\&.
.RE
.SH "MODULES"
.sp
This section describes modules options of ejabberd 25\&.07\&. The modules that changed in this version are marked with 🟤\&.
This section describes modules options of ejabberd 25\&.08\&. The modules that changed in this version are marked with 🟤\&.
.SS "mod_adhoc"
.sp
def:ad\-hoc command
@@ -2261,6 +2267,8 @@ ejabberdctl srg_create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g1
.if n \{\
.RE
.\}
.sp
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#accounts|accounts\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#erlang|erlang\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#last|last\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#private|private\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#roster|roster\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#session|session\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#shared_roster_group|shared_roster_group\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#stanza|stanza\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#statistics|statistics\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#vcard|vcard\fR
.RE
.SS "mod_admin_update_sql"
.sp
@@ -2455,7 +2463,7 @@ Same as top\-level
option, but applied to this module only\&.
.RE
.RE
.SS "mod_antispam 🟤"
.SS "mod_antispam"
.sp
\fINote\fR about this option: added in 25\&.07\&.
.sp
@@ -3034,7 +3042,7 @@ modules:
.RE
.\}
.RE
.SS "mod_conversejs 🟤"
.SS "mod_conversejs"
.sp
\fINote\fR about this option: improved in 25\&.07\&.
.sp
@@ -3080,6 +3088,17 @@ about this option: added in 22\&.05\&. Specify additional options to be passed t
Converse configuration\&. Only boolean, integer and string values are supported; lists are not supported\&.
.RE
.PP
\fBconversejs_plugins\fR: \fI[Filename]\fR
.RS 4
List of additional local files to include as scripts in the homepage\&. Please make sure those files are available in the path specified in
\fIconversejs_resources\fR
option, in subdirectory
\fIplugins/\fR\&. If using the public Converse client, then
\fI"libsignal"\fR
gets replaced with the URL of the public library\&. The default value is
\fI[]\fR\&.
.RE
.PP
\fBconversejs_resources\fR: \fIPath\fR
.RS 4
\fINote\fR
@@ -3138,6 +3157,7 @@ listen:
modules:
mod_bosh: {}
mod_conversejs:
conversejs_plugins: ["libsignal"]
websocket_url: "ws://@HOST@:5280/websocket"
.fi
.if n \{\
@@ -3161,7 +3181,9 @@ listen:
modules:
mod_conversejs:
conversejs_resources: "/home/ejabberd/conversejs\-9\&.0\&.0/package/dist"
conversejs_resources: "/home/ejabberd/conversejs\-x\&.y\&.z/package/dist"
conversejs_plugins: ["libsignal\-protocol\&.min\&.js"]
# File path is: /home/ejabberd/conversejs\-x\&.y\&.z/package/dist/plugins/libsignal\-protocol\&.min\&.js
.fi
.if n \{\
.RE
@@ -3424,6 +3446,8 @@ hour\&.
The number of C2S authentication failures to trigger the IP ban\&. The default value is
\fI20\fR\&.
.RE
.sp
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#accounts|accounts\fR
.RE
.SS "mod_host_meta"
.sp
@@ -3784,7 +3808,9 @@ A name of the service in the Service Discovery\&. The default value is
.RS 4
This option specifies the initial part of the PUT URLs used for file uploads\&. The keyword
\fI@HOST@\fR
is replaced with the virtual host name\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is
is replaced with the virtual host name\&. And
\fI@HOST_URL_ENCODE@\fR
is replaced with the host name encoded for URL, useful when your virtual hosts contain non\-latin characters\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is
\fI"https://@HOST@:5443/upload"\fR\&.
.RE
.PP
@@ -4147,12 +4173,14 @@ option, but applied to this module only\&.
When this option is disabled, for each individual 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
\fIfalse\fR\&.
.RE
.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 🟤"
.sp
\fINote\fR about this option: improved in 25\&.07\&.
\fINote\fR about this option: improved in 25\&.08\&.
.sp
Matrix gateway\&. Erlang/OTP 25 or higher is required to use this module\&. This module is available since ejabberd 24\&.02\&.
Matrix gateway\&. Supports room versions 9, 10 and 11 since ejabberd 25\&.03; room versions 4 and higher since ejabberd 25\&.07; room version 12 (hydra rooms) since ejabberd 25\&.08\&. Erlang/OTP 25 or higher is required to use this module\&. This module is available since ejabberd 24\&.02\&.
.sp
.it 1 an-trap
.nr an-no-space-flag 1
@@ -4182,6 +4210,13 @@ Value of the matrix signing key, in base64\&.
Name of the matrix signing key\&.
.RE
.PP
\fBleave_timeout\fR: \fIinteger()\fR
.RS 4
Delay in seconds between a user leaving a MUC room and sending
\fIleave\fR
Matrix event\&.
.RE
.PP
\fBmatrix_domain\fR: \fIDomain\fR
.RS 4
Specify a domain in the Matrix federation\&. The keyword
@@ -4206,6 +4241,11 @@ is the JID of the gateway service as set by the
option\&. The default is
\fIfalse\fR\&.
.RE
.PP
\fBnotary_servers\fR: \fI[Server, \&.\&.\&.]\fR
.RS 4
A list of notary servers\&.
.RE
.RE
.sp
.it 1 an-trap
@@ -5170,6 +5210,8 @@ about this option: added in 22\&.05\&. How many users can be subscribed to a roo
API\&. The default value is
\fI50\fR\&.
.RE
.sp
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc|muc\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc_room|muc_room\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc_sub|muc_sub\fR
.RE
.SS "mod_muc_log"
.sp
@@ -5747,6 +5789,8 @@ modules:
.if n \{\
.RE
.\}
.sp
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#offline|offline\fR
.RE
.SS "mod_ping"
.sp
@@ -5990,6 +6034,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#private|private\fR
.RE
.SS "mod_privilege"
.sp
@@ -6157,6 +6203,199 @@ modules:
.RE
.\}
.RE
.SS "mod_providers 🟤"
.sp
\fINote\fR about this option: added in 25\&.08\&.
.sp
This module serves JSON provider files API v2 as described by XMPP Providers\&.
.sp
It attempts to fill some properties gathering values automatically from your existing ejabberd configuration\&. Try enabling the module, check what values are displayed, and then customize using the options\&.
.sp
To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. Notice you should set in \fIlisten\&.md#ejabberd_http|ejabberd_http\fR the option \fIlisten\-options\&.md#tls|tls\fR enabled\&.
.sp
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.br
.ps +1
\fBAvailable options:\fR
.RS 4
.PP
\fBalternativeJids\fR: \fI[string()]\fR
.RS 4
List of JIDs (XMPP server domains) a provider offers for registration other than its main JID\&. The default value is
\fI[]\fR\&.
.RE
.PP
\fBbusFactor\fR: \fIinteger()\fR
.RS 4
Bus factor of the XMPP service (i\&.e\&., the minimum number of team members that the service could not survive losing) or
\fI\-1\fR
for n/a\&. The default value is
\fI\-1\fR\&.
.RE
.PP
\fBfreeOfCharge\fR: \fItrue | false\fR
.RS 4
Whether the XMPP service can be used for free\&. The default value is
\fIfalse\fR\&.
.RE
.PP
\fBlanguages\fR: \fI[string()]\fR
.RS 4
List of language codes that your pages are available\&. Some options define URL where the keyword
\fI@LANGUAGE_URL@\fR
will be replaced with each of those language codes\&. The default value is a list with the language set in the option
\fIlanguage\fR, for example:
\fI[en]\fR\&.
.RE
.PP
\fBlegalNotice\fR: \fIstring()\fR
.RS 4
Legal notice web page (per language)\&. The keyword
\fI@LANGUAGE_URL@\fR
is replaced with each language\&. The default value is
\fI""\fR\&.
.RE
.PP
\fBmaximumHttpFileUploadStorageTime\fR: \fIinteger()\fR
.RS 4
Maximum storage duration of each shared file (number in days,
\fI0\fR
for no limit or
\fI\-1\fR
for less than 1 day)\&. The default value is the same as option
\fImax_days\fR
from module
\fImod_http_upload_quota\fR, or
\fI0\fR
otherwise\&.
.RE
.PP
\fBmaximumHttpFileUploadTotalSize\fR: \fIinteger()\fR
.RS 4
Maximum size of all shared files in total per user (number in megabytes (MB),
\fI0\fR
for no limit or
\fI\-1\fR
for less than 1 MB)\&. Attention: MB is used instead of MiB (e\&.g\&., 104,857,600 bytes = 100 MiB H 104 MB)\&. This property is not about the maximum size of each shared file, which is already retrieved via XMPP\&. The default value is the value of the shaper value of option
\fIaccess_hard_quota\fR
from module
\fImod_http_upload_quota\fR, or
\fI0\fR
otherwise\&.
.RE
.PP
\fBmaximumMessageArchiveManagementStorageTime\fR: \fIinteger()\fR
.RS 4
Maximum storage duration of each exchanged message (number in days,
\fI0\fR
for no limit or
\fI\-1\fR
for less than 1 day)\&. The default value is
\fI0\fR\&.
.RE
.PP
\fBorganization\fR: \fIstring()\fR
.RS 4
Type of organization providing the XMPP service\&. Allowed values are:
\fIcompany\fR,
\fI"commercial person"\fR,
\fI"private person"\fR,
\fIgovernmental\fR,
\fI"non\-governmental"\fR
or
\fI""\fR\&. The default value is
\fI""\fR\&.
.RE
.PP
\fBpasswordReset\fR: \fIstring()\fR
.RS 4
Password reset web page (per language) used for an automatic password reset (e\&.g\&., via email) or describing how to manually reset a password (e\&.g\&., by contacting the provider)\&. The keyword
\fI@LANGUAGE_URL@\fR
is replaced with each language\&. The default value is an URL built automatically if
\fImod_register_web\fR
is configured as a
\fIrequest_handler\fR, or
\fI""\fR
otherwise\&.
.RE
.PP
\fBprofessionalHosting\fR: \fItrue | false\fR
.RS 4
Whether the XMPP server is hosted with good internet connection speed, uninterruptible power supply, access protection and regular backups\&. The default value is
\fIfalse\fR\&.
.RE
.PP
\fBserverLocations\fR: \fI[string()]\fR
.RS 4
List of language codes of Server/Backup locations\&. The default value is an empty list:
\fI[]\fR\&.
.RE
.PP
\fBserverTesting\fR: \fItrue | false\fR
.RS 4
Whether tests against the provider\(cqs server are allowed (e\&.g\&., certificate checks and uptime monitoring)\&. The default value is
\fIfalse\fR\&.
.RE
.PP
\fBsince\fR: \fIstring()\fR
.RS 4
Date since the XMPP service is available\&. The default value is an empty string:
\fI""\fR\&.
.RE
.PP
\fBwebsite\fR: \fIstring()\fR
.RS 4
Provider website\&. The keyword
\fI@LANGUAGE_URL@\fR
is replaced with each language\&. The default value is
\fI""\fR\&.
.RE
.RE
.sp
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.br
.ps +1
\fBExample:\fR
.RS 4
.sp
.if n \{\
.RS 4
.\}
.nf
listen:
\-
port: 443
module: ejabberd_http
tls: true
request_handlers:
/\&.well\-known/xmpp\-provider\-v2\&.json: mod_providers
modules:
mod_providers:
alternativeJids: ["example1\&.com", "example2\&.com"]
busFactor: 1
freeOfCharge: true
languages: [ag, ao, bg, en]
legalNotice: "http://@HOST@/legal/@LANGUAGE_URL@/"
maximumHttpFileUploadStorageTime: 0
maximumHttpFileUploadTotalSize: 0
maximumMessageArchiveManagementStorageTime: 0
organization: "non\-governmental"
passwordReset: "http://@HOST@/reset/@LANGUAGE_URL@/"
professionalHosting: true
serverLocations: [ao, bg]
serverTesting: true
since: "2025\-12\-31"
website: "http://@HOST@/website/@LANGUAGE_URL@/"
.fi
.if n \{\
.RE
.\}
.RE
.SS "mod_proxy65"
.sp
This module implements XEP\-0065: SOCKS5 Bytestreams\&. It allows ejabberd to act as a file transfer proxy between two XMPP clients\&.
@@ -6618,8 +6857,10 @@ modules:
.if n \{\
.RE
.\}
.sp
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR
.RE
.SS "mod_pubsub_serverinfo 🟤"
.SS "mod_pubsub_serverinfo"
.sp
\fINote\fR about this option: added in 25\&.07\&.
.sp
@@ -6741,6 +6982,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#purge|purge\fR
.RE
.SS "mod_push_keepalive"
.sp
@@ -7090,6 +7333,8 @@ modules:
.if n \{\
.RE
.\}
.sp
\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#roster|roster\fR
.RE
.SS "mod_s2s_bidi"
.sp
@@ -8617,7 +8862,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\&.07\&.
This section describes listeners options of ejabberd 25\&.08\&.
.sp
TODO
.SH "AUTHOR"
@@ -8625,13 +8870,13 @@ TODO
ProcessOne\&.
.SH "VERSION"
.sp
This document describes the configuration file of ejabberd 25\&.07\&. Configuration options of other ejabberd versions may differ significantly\&.
This document describes the configuration file of ejabberd 25\&.08\&. 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\&.07/ejabberd\&.yml\&.example
Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.08/ejabberd\&.yml\&.example
.sp
Main site: https://ejabberd\&.im
.sp
+3 -3
View File
@@ -125,13 +125,13 @@ defmodule Ejabberd.MixProject do
{:fast_yaml, "~> 1.0"},
{:idna, "~> 6.0"},
{:mqtree, "~> 1.0"},
{:p1_acme, "~> 1.0.27"},
{:p1_acme, ">= 1.0.28"},
{:p1_oauth2, "~> 0.6"},
{:p1_utils, "~> 1.0"},
{:pkix, "~> 1.0"},
{:stringprep, ">= 1.0.26"},
{:xmpp, ">= 1.11.0"},
{:yconf, ">= 1.0.18"}]
{:xmpp, ">= 1.11.1"},
{:yconf, ">= 1.0.21"}]
++ cond_deps()
end
+8 -8
View File
@@ -1,17 +1,17 @@
%{
"base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"},
"cache_tab": {:hex, :cache_tab, "1.0.33", "e2542afb34f17ee3ca19d2b0f546a074922c2b99fb6b2acfb38160d7d0336ec3", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "4258009eb050b22aabe0c848e230bba58401a6895c58c2ff74dfb635e3c35900"},
"dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"},
"dialyxir": {:hex, :dialyxir, "1.4.6", "7cca478334bf8307e968664343cbdb432ee95b4b68a9cba95bdabb0ad5bdfd9a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "8cf5615c5cd4c2da6c501faae642839c8405b49f8aa057ad4ae401cb808ef64d"},
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
"eimp": {:hex, :eimp, "1.0.26", "c0b05f32e35629c4d9bcfb832ff879a92b0f92b19844bc7835e0a45635f2899a", [:rebar3], [{:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d96d4e8572b9dfc40f271e47f0cb1d8849373bc98a21223268781765ed52044c"},
"epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"},
"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.58", "96bf0c07271f86f03f42778d4a1237099baec0714d00b0b815eb42d0007f73b4", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.20", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "e0f4204a5ede0fa7d00da3cc42f6440aa362bac7faf536f71ea29fa3f0fa7c75"},
"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"},
"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.24", "5492125689e3d84c101323a0bff3d3996b92a903832530fe4f0935ed30b1b7d1", [:rebar3], [{:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "fff88ada39fad10464567a160643f4529ef4aed49d156919f5d1f415b6cdbbb6"},
"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"},
@@ -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.27", "43d565cf0e22da51033ae329e773ccad0f44c4cb9c7199d0863f522e570f2767", [: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", "aa64b6a8856b1a229a128bea27631de2e1a2219835e3a833fa11137143a8d773"},
"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_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.34", "d36bd0d6c9765a47d075a87ad5e3fc3bfd153bdc4c07a1217b9979f33f73e9aa", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "cb0e32e086c9c35d0e3e966e3863d832737c7b4d2b5f147316a465c0b243ea7f"},
"p1_pgsql": {:hex, :p1_pgsql, "1.1.35", "e13d89f14d717553e85c88a152ce77461916b013d88fcb851e354a0b332d4218", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "e99594446c411c660696795b062336f5c4bd800451d8f620bb4d4ce304e255c2"},
"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.20", "62a149cea122a78a104b9e064a12d9e33105b26c23168ecf3aea6e0c26de0748", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "79e49f826a4f7d522c939ab633d935c79d7d6b229e4cb7e05f62f33b50177414"},
"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.0", "a3158c486c9b86a7090c361d876db622381f4312ede8c125d7a52ad390387932", [: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", "34a191d6a3b74e8f0a42346f859e2cab5b3a2ae7e5c28f392e5cb56612e7ce85"},
"yconf": {:hex, :yconf, "1.0.20", "f2b38db613fa826966e8d22bdc3e3ebae46919f2a27ab149a5a086c1d99d3bbd", [:rebar3], [{:fast_yaml, "1.0.39", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "f2b3d730756fc2e4afd1c0b0ab6efb99f0e448952d25dc15ed75ac1635bf8882"},
"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"},
}
+1 -1
View File
@@ -358,7 +358,7 @@
{"Too many child elements","Túl sok gyermekelem"}.
{"Too many <item/> elements","Túl sok <item/> elem"}.
{"Too many <list/> elements","Túl sok <list/> elem"}.
{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Túl sok (~p) sikertelen hitelesítés erről az IP-címről (~ts) A cím ~ts-kor lesz feloldva UTC szerint"}.
{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Túl sok (~p) sikertelen hitelesítés erről az IP-címről (~s) A cím ~s-kor lesz feloldva UTC szerint"}.
{"Too many receiver fields were specified","Túl sok fogadómező lett meghatározva"}.
{"Too many unacked stanzas","Túl sok nyugtázatlan stanza"}.
{"Too many users in this conference","Túl sok felhasználó ebben a konferenciában"}.
+7 -7
View File
@@ -41,10 +41,10 @@
{eredis, "~> 1.7.1", {git, "https://github.com/Nordix/eredis/", {tag, "v1.7.1"}}}
}}},
{if_var_true, sip,
{esip, "~> 1.0.58", {git, "https://github.com/processone/esip", {tag, "1.0.58"}}}},
{esip, "~> 1.0.59", {git, "https://github.com/processone/esip", {tag, "1.0.59"}}}},
{if_var_true, zlib,
{ezlib, "~> 1.0.15", {git, "https://github.com/processone/ezlib", {tag, "1.0.15"}}}},
{fast_tls, "~> 1.1.24", {git, "https://github.com/processone/fast_tls", {tag, "1.1.24"}}},
{fast_tls, "~> 1.1.25", {git, "https://github.com/processone/fast_tls", {tag, "1.1.25"}}},
{fast_xml, "~> 1.1.57", {git, "https://github.com/processone/fast_xml", {tag, "1.1.57"}}},
{fast_yaml, "~> 1.0.39", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.39"}}},
{idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}},
@@ -64,21 +64,21 @@
{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.27", {git, "https://github.com/processone/p1_acme", {tag, "1.0.27"}}},
{p1_acme, "~> 1.0.28", {git, "https://github.com/processone/p1_acme", {tag, "1.0.28"}}},
{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.34", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.34"}}}},
{p1_pgsql, "~> 1.1.35", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.35"}}}},
{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,
{sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}},
{stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}},
{if_var_true, stun,
{stun, "~> 1.2.20", {git, "https://github.com/processone/stun", {tag, "1.2.20"}}}},
{xmpp, "~> 1.11.0", {git, "https://github.com/processone/xmpp", {tag, "1.11.0"}}},
{yconf, "~> 1.0.20", {git, "https://github.com/processone/yconf", {tag, "1.0.20"}}}
{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"}}}
]}.
{gitonly_deps, [ejabberd_po]}.
+21 -21
View File
@@ -4,9 +4,9 @@
{<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.26">>},0},
{<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0},
{<<"eredis">>,{pkg,<<"eredis">>,<<"1.7.1">>},0},
{<<"esip">>,{pkg,<<"esip">>,<<"1.0.58">>},0},
{<<"esip">>,{pkg,<<"esip">>,<<"1.0.59">>},0},
{<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.15">>},0},
{<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.24">>},0},
{<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.25">>},0},
{<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.57">>},0},
{<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.39">>},0},
{<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0},
@@ -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.27">>},0},
{<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.28">>},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.34">>},0},
{<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.35">>},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.20">>},0},
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.21">>},0},
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1},
{<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.0">>},0},
{<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.20">>},0}]}.
{<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.1">>},0},
{<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.21">>},0}]}.
[
{pkg_hash,[
{<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>},
@@ -33,9 +33,9 @@
{<<"eimp">>, <<"C0B05F32E35629C4D9BCFB832FF879A92B0F92B19844BC7835E0A45635F2899A">>},
{<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>},
{<<"eredis">>, <<"39E31AA02ADCD651C657F39AAFD4D31A9B2F63C6C700DC9CECE98D4BC3C897AB">>},
{<<"esip">>, <<"96BF0C07271F86F03F42778D4A1237099BAEC0714D00B0B815EB42D0007F73B4">>},
{<<"esip">>, <<"EB202F8C62928193588091DFEDBC545FE3274C34ECD209961F86DCB6C9EBCE88">>},
{<<"ezlib">>, <<"D74F5DF191784744726A5B1AE9062522C606334F11086363385EB3B772D91357">>},
{<<"fast_tls">>, <<"5492125689E3D84C101323A0BFF3D3996B92A903832530FE4F0935ED30B1B7D1">>},
{<<"fast_tls">>, <<"DA8ED6F05A2452121B087158B17234749F36704C1F2B74DC51DB99A1E27ED5E8">>},
{<<"fast_xml">>, <<"31EFC0F9BCEDA92069704F7A25830407DA5DC3DAD1272B810D6F2E13E73CC11A">>},
{<<"fast_yaml">>, <<"2E71168091949BAB0E5F583B340A99072B4D22D93EB86624E7850A12B1517BE4">>},
{<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>},
@@ -43,27 +43,27 @@
{<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>},
{<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>},
{<<"mqtree">>, <<"D769C25F898810725FC7DB0DBFFE5F72098647048B1BE2E6D772F1C2F31D8476">>},
{<<"p1_acme">>, <<"43D565CF0E22DA51033AE329E773CCAD0F44C4CB9C7199D0863F522E570F2767">>},
{<<"p1_acme">>, <<"64D9C17F5412AA92D75B29206B2B984D734A4FE1B7EACB66C3D7A7C697AC612C">>},
{<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>},
{<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>},
{<<"p1_pgsql">>, <<"D36BD0D6C9765A47D075A87AD5E3FC3BFD153BDC4C07A1217B9979F33F73E9AA">>},
{<<"p1_pgsql">>, <<"E13D89F14D717553E85C88A152CE77461916B013D88FCB851E354A0B332D4218">>},
{<<"p1_utils">>, <<"9A7088A98D788B4C4880FD3C82D0C135650DB13F2E4EF7E10DB179791BC94D59">>},
{<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>},
{<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>},
{<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>},
{<<"stun">>, <<"62A149CEA122A78A104B9E064A12D9E33105B26C23168ECF3AEA6E0C26DE0748">>},
{<<"stun">>, <<"735855314AD22CB7816B88597D2F5CA22E24AA5E4D6010A0EF3AFFB33CEED6A5">>},
{<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>},
{<<"xmpp">>, <<"A3158C486C9B86A7090C361D876DB622381F4312EDE8C125D7A52AD390387932">>},
{<<"yconf">>, <<"F2B38DB613FA826966E8D22BDC3E3EBAE46919F2A27AB149A5A086C1D99D3BBD">>}]},
{<<"xmpp">>, <<"60181E7D3E8E48AA3B23B2792075CDA37E2E507EC152490B866E61E5320CB1DA">>},
{<<"yconf">>, <<"DBAE1589381E044529E112B7E0097C89D88DF89E446EAD53BD33E8D27E2BCC83">>}]},
{pkg_hash_ext,[
{<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>},
{<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>},
{<<"eimp">>, <<"D96D4E8572B9DFC40F271E47F0CB1D8849373BC98A21223268781765ED52044C">>},
{<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>},
{<<"eredis">>, <<"7C2B54C566FED55FEEF3341CA79B0100A6348FD3F162184B7ED5118D258C3CC1">>},
{<<"esip">>, <<"E0F4204A5EDE0FA7D00DA3CC42F6440AA362BAC7FAF536F71EA29FA3F0FA7C75">>},
{<<"esip">>, <<"0BDF2E3C349DC0B144F173150329E675C6A51AC473D7A0B2E362245FAAD3FBE6">>},
{<<"ezlib">>, <<"DD14BA6C12521AF5CFE6923E73E3D545F4A0897DC66BFAB5287FBB7AE3962EAB">>},
{<<"fast_tls">>, <<"FFF88ADA39FAD10464567A160643F4529EF4AED49D156919F5D1F415B6CDBBB6">>},
{<<"fast_tls">>, <<"59E183B5740E670E02B8AA6BE673B5E7779E5FE5BFCC679FE2D4993D1949A821">>},
{<<"fast_xml">>, <<"EEC34E90ADACAFE467D5DDAB635A014DED73B98B4061554B2D1972173D929C39">>},
{<<"fast_yaml">>, <<"24C7B9AB9E2B9269D64E45F4A2A1280966ADB17D31E63365CFD3EE277FB0A78D">>},
{<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>},
@@ -71,16 +71,16 @@
{<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>},
{<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>},
{<<"mqtree">>, <<"C81065715C49A1882812F80A5AE2D842E80DD3F2D130530DF35990248BF8CE3C">>},
{<<"p1_acme">>, <<"AA64B6A8856B1A229A128BEA27631DE2E1A2219835E3A833FA11137143A8D773">>},
{<<"p1_acme">>, <<"CE686986DE3F9D5FD285AFE87523CB45329A349C6C6BE7ACC1ED916725D46423">>},
{<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>},
{<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>},
{<<"p1_pgsql">>, <<"CB0E32E086C9C35D0E3E966E3863D832737C7B4D2B5F147316A465C0B243EA7F">>},
{<<"p1_pgsql">>, <<"E99594446C411C660696795B062336F5C4BD800451D8F620BB4D4CE304E255C2">>},
{<<"p1_utils">>, <<"C49BD44BC4A40AD996691AF826DD7E0AA56D4D0CD730817190A1F84D1A7F0033">>},
{<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>},
{<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>},
{<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>},
{<<"stun">>, <<"79E49F826A4F7D522C939AB633D935C79D7D6B229E4CB7E05F62F33B50177414">>},
{<<"stun">>, <<"3D7FE8EFB9D05B240A6AA9A6BF8B8B7BFF2D802895D170443C588987DC1E12D9">>},
{<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>},
{<<"xmpp">>, <<"34A191D6A3B74E8F0A42346F859E2CAB5B3A2AE7E5C28F392E5CB56612E7CE85">>},
{<<"yconf">>, <<"F2B3D730756FC2E4AFD1C0B0AB6EFB99F0E448952D25DC15ED75AC1635BF8882">>}]}
{<<"xmpp">>, <<"A5C933DF904AB3CEC15425DA334E410CE84EC3AE7B81EFE069E5DB368A7B3716">>},
{<<"yconf">>, <<"C524A5F1FD86875D85B469CC2E368C204F97CCA1C3918736E21F5001C01D096C">>}]}
].
+2
View File
@@ -482,6 +482,8 @@ domain() ->
non_empty(binary()),
fun(Val) ->
try jid:tolower(jid:decode(Val)) of
{<<"">>, <<"xn--", _/binary>> = Domain, <<"">>} ->
unicode:characters_to_binary(idna:decode(binary_to_list(Domain)), utf8);
{<<"">>, Domain, <<"">>} -> Domain;
_ -> fail({bad_domain, Val})
catch _:{bad_jid, _} ->
+1
View File
@@ -237,6 +237,7 @@ check_password(User, AuthzId, Server, Password, Digest, DigestGen) ->
case check_password_with_authmodule(
User, AuthzId, Server, Password, Digest, DigestGen) of
{true, _AuthModule} -> true;
{false, _ErrorAtom, _Reason} -> false;
false -> false
end.
+1 -7
View File
@@ -151,13 +151,7 @@ get_users(Server, []) ->
Users = mnesia:dirty_select(passwd,
[{#passwd{us = '$1', _ = '_'},
[{'==', {element, 2, '$1'}, Server}], ['$1']}]),
{_, Res} = lists:foldl(
fun({U, S, _}, {{U2, S2}, _} = Acc) when U == U2 andalso S == S2 ->
Acc;
({U, S, _}, {_, Res}) ->
{{U, S}, [{U, S} | Res]}
end, {{none, none}, []}, Users),
Res;
misc:lists_uniq([{U, S} || {U, S, _} <- Users]);
get_users(Server, [{from, Start}, {to, End}])
when is_integer(Start) and is_integer(End) ->
get_users(Server, [{limit, End - Start + 1}, {offset, Start}]);
+3 -3
View File
@@ -416,8 +416,8 @@ unauthenticated_stream_features(#{lserver := LServer}) ->
authenticated_stream_features(#{lserver := LServer}) ->
ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]).
inline_stream_features(#{lserver := LServer}) ->
ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], [], []}, [LServer]).
inline_stream_features(#{lserver := LServer} = State) ->
ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], [], []}, [LServer, State]).
sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = State) ->
Type = ejabberd_auth:store_type(LServer),
@@ -455,7 +455,7 @@ sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = St
(<<"EXTERNAL">>) -> maps:get(tls_verify, State, false);
(_) -> false
end, Mechs -- Mechs1),
case ejabberd_option:auth_password_types_hidden_in_scram1() of
case ejabberd_option:auth_password_types_hidden_in_sasl1() of
[] -> Mechs2;
List ->
Mechs3 = lists:foldl(
+11 -25
View File
@@ -264,30 +264,31 @@ version() ->
-spec default_db(binary() | global, module()) -> atom().
default_db(Host, Module) ->
default_db(default_db, Host, Module, mnesia).
default_db(default_db, db_type, Host, Module, mnesia).
-spec default_db(binary() | global, module(), atom()) -> atom().
default_db(Host, Module, Default) ->
default_db(default_db, Host, Module, Default).
default_db(default_db, db_type, Host, Module, Default).
-spec default_ram_db(binary() | global, module()) -> atom().
default_ram_db(Host, Module) ->
default_db(default_ram_db, Host, Module, mnesia).
default_db(default_ram_db, ram_db_type, Host, Module, mnesia).
-spec default_ram_db(binary() | global, module(), atom()) -> atom().
default_ram_db(Host, Module, Default) ->
default_db(default_ram_db, Host, Module, Default).
default_db(default_ram_db, ram_db_type, Host, Module, Default).
-spec default_db(default_db | default_ram_db, binary() | global, module(), atom()) -> atom().
default_db(Opt, Host, Mod, Default) ->
-spec default_db(default_db | default_ram_db, db_type | ram_db_type, binary() | global, module(), atom()) -> atom().
default_db(Opt, ModOpt, Host, Mod, Default) ->
Type = get_option({Opt, Host}),
DBMod = list_to_atom(atom_to_list(Mod) ++ "_" ++ atom_to_list(Type)),
case code:ensure_loaded(DBMod) of
{module, _} -> Type;
{error, _} ->
?WARNING_MSG("Module ~ts doesn't support database '~ts' "
"defined in option '~ts', using "
"'~ts' as fallback", [Mod, Type, Opt, Default]),
"defined in toplevel option '~ts': will use the value "
"set in ~ts option '~ts', or '~ts' as fallback",
[Mod, Type, Opt, Mod, ModOpt, Default]),
Default
end.
@@ -507,7 +508,7 @@ get_predefined_keywords(Host) ->
global ->
[];
_ ->
[{<<"HOST">>, Host}]
[{<<"HOST">>, Host}, {<<"HOST_URL_ENCODE">>, misc:url_encode(Host)}]
end,
Home = misc:get_home(),
ConfigDirPath =
@@ -631,22 +632,7 @@ callback_modules(external) ->
end
end, beams(external));
callback_modules(all) ->
lists_uniq(callback_modules(local) ++ callback_modules(external)).
-ifdef(OTP_BELOW_25).
lists_uniq(List) ->
{Res, _} = lists:foldr(
fun(El, {Result, Existing} = Acc) ->
case maps:is_key(El, Existing) of
true -> Acc;
_ -> {[El | Result], Existing#{El => true}}
end
end, {[], #{}}, List),
Res.
-else.
lists_uniq(List) ->
lists:uniq(List).
-endif.
misc:lists_uniq(callback_modules(local) ++ callback_modules(external)).
-spec validators(module(), [atom()], [any()]) -> econf:validators().
validators(Mod, Disallowed, DK) ->
+2
View File
@@ -230,6 +230,8 @@ filter(_Host, captcha_host, _, _) ->
filter(_Host, route_subdomains, _, _) ->
warn_removed_option(route_subdomains, s2s_access),
false;
filter(_Host, auth_password_types_hidden_in_scram1, Val, _) ->
{true, {auth_password_types_hidden_in_sasl1, Val}};
filter(Host, modules, ModOpts, State) ->
NoDialbackHosts = maps:get(remove_s2s_dialback, State, []),
ModOpts1 = lists:filter(
+11
View File
@@ -1148,6 +1148,17 @@ get_commands_spec() ->
desc = "Get list of commands, or help of a command (only ejabberdctl)",
longdesc = "This command is exclusive for the ejabberdctl command-line script, "
"don't attempt to execute it using any other API frontend."},
#ejabberd_commands{name = mnesia_change, tags = [ejabberdctl, mnesia],
desc = "Change the erlang node name in the mnesia database (only ejabberdctl)",
longdesc = "This command internally calls the _`mnesia_change_nodename`_ API. "
"This is a special command that starts and stops ejabberd several times: "
"do not attempt to run this command when ejabberd is running. "
"This command is exclusive for the ejabberdctl command-line script, "
"don't attempt to execute it using any other API frontend.",
note = "added in 25.08",
args = [{old_node_name, string}],
args_desc = ["Old erlang node name"],
args_example = ["ejabberd@oldmachine"]},
#ejabberd_commands{name = mnesia_info_ctl, tags = [ejabberdctl, mnesia],
desc = "Show information of Mnesia system (only ejabberdctl)",
note = "renamed in 24.02",
+33 -2
View File
@@ -24,6 +24,7 @@
%% API
-export([man/0, man/1, have_a2x/0]).
-include("ejabberd_commands.hrl").
-include("translate.hrl").
%%%===================================================================
@@ -46,7 +47,8 @@ man(Lang) ->
DocOpts = maps:get(opts, Map, []),
Example = maps:get(example, Map, []),
Note = maps:get(note, Map, []),
{[{M, Descr, DocOpts, #{example => Example, note => Note}}|Mods], SubMods};
Apitags = get_module_apitags(M),
{[{M, Descr, DocOpts, #{example => Example, note => Note, apitags => Apitags}}|Mods], SubMods};
#{opts := DocOpts} ->
{ParentMod, Backend} = strip_backend_suffix(M),
{Mods, dict:append(ParentMod, {M, Backend, DocOpts}, SubMods)};
@@ -113,7 +115,8 @@ man(Lang) ->
format_versions(Lang, Example) ++ [io_lib:nl()] ++
tr_multi(Lang, Descr) ++ [io_lib:nl()] ++
opts_to_man(Lang, [{M, '', DocOpts}|Backends]) ++
format_example(0, Lang, Example)
format_example(0, Lang, Example) ++ [io_lib:nl()] ++
format_apitags(Lang, Example)
end, lists:keysort(1, ModDoc1)),
ListenOptions =
[io_lib:nl(),
@@ -190,6 +193,34 @@ format_versions(_Lang, #{note := Note}) when Note /= [] ->
format_versions(_, _) ->
[].
%% @format-begin
get_module_apitags(M) ->
AllCommands = ejabberd_commands:get_commands_definition(),
Tags = [C#ejabberd_commands.tags || C <- AllCommands, C#ejabberd_commands.module == M],
TagsClean =
lists:sort(
misc:lists_uniq(
lists:flatten(Tags))),
TagsStrings = [atom_to_list(C) || C <- TagsClean],
TagFiltering =
fun ("internal") ->
false;
([$v | Rest]) ->
{error, no_integer} == string:to_integer(Rest);
(_) ->
true
end,
TagsFiltered = lists:filter(TagFiltering, TagsStrings),
TagsUrls =
[["_`../../developer/ejabberd-api/admin-tags.md#", C, "|", C, "`_"] || C <- TagsFiltered],
lists:join(", ", TagsUrls).
format_apitags(_Lang, #{apitags := TagsString}) when TagsString /= "" ->
["**API Tags:** ", TagsString];
format_apitags(_, _) ->
[].
%% @format-end
format_desc(Lang, #{desc := Desc}) ->
tr_multi(Lang, Desc).
+1 -14
View File
@@ -718,7 +718,7 @@ file_format_error(Reason) ->
url_decode_q_split_normalize(Path) ->
{NPath, Query} = url_decode_q_split(Path),
LPath = normalize_path([NPE
|| NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
|| NPE <- str:tokens(misc:uri_decode(NPath), <<"/">>)]),
{LPath, Query}.
% Code below is taken (with some modifications) from the yaws webserver, which
@@ -746,19 +746,6 @@ url_decode_q_split(<<H, T/binary>>, Acc) when H /= 0 ->
url_decode_q_split(<<>>, Ack) ->
{path_norm_reverse(Ack), <<>>}.
%% @doc Decode a part of the URL and return string()
path_decode(Path) -> path_decode(Path, <<>>).
path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) ->
Hex = list_to_integer([Hi, Lo], 16),
if Hex == 0 -> exit(badurl);
true -> ok
end,
path_decode(Tail, <<Acc/binary, Hex>>);
path_decode(<<H, T/binary>>, Acc) when H /= 0 ->
path_decode(T, <<Acc/binary, H>>);
path_decode(<<>>, Acc) -> Acc.
path_norm_reverse(<<"/", T/binary>>) -> start_dir(0, <<"/">>, T);
path_norm_reverse(T) -> start_dir(0, <<"">>, T).
+64 -35
View File
@@ -113,12 +113,12 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
{Port, SockOpts}
end,
ExtraOpts2 = lists:keydelete(send_timeout, 1, ExtraOpts),
case gen_udp:open(Port2, [binary,
case {gen_udp:open(Port2, [binary,
{active, false},
{reuseaddr, true} |
ExtraOpts2]) of
{ok, Socket} ->
set_definitive_udsocket(Port, Opts),
ExtraOpts2]),
set_definitive_udsocket(Port, Opts)} of
{{ok, Socket}, ok} ->
misc:set_proc_label({?MODULE, udp, Port}),
case inet:sockname(Socket) of
{ok, {Addr, Port1}} ->
@@ -139,18 +139,18 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
{error, _} ->
ok
end;
{error, Reason} = Err ->
report_socket_error(Reason, EndPoint, Module),
proc_lib:init_ack(Err)
{error, Reason} ->
return_socket_error(Reason, EndPoint, Module)
end;
{error, Reason} = Err ->
report_socket_error(Reason, EndPoint, Module),
proc_lib:init_ack(Err)
{{error, Reason}, _} ->
return_socket_error(Reason, EndPoint, Module);
{_, {error, Reason} } ->
return_socket_error(Reason, EndPoint, Module)
end;
init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) ->
case listen_tcp(Port, SockOpts) of
{ok, ListenSocket} ->
set_definitive_udsocket(Port, Opts),
case {listen_tcp(Port, SockOpts),
set_definitive_udsocket(Port, Opts)} of
{{ok, ListenSocket}, ok} ->
case inet:sockname(ListenSocket) of
{ok, {Addr, Port1}} ->
proc_lib:init_ack({ok, self()}),
@@ -174,13 +174,13 @@ init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) ->
{error, _} ->
ok
end;
{error, Reason} = Err ->
report_socket_error(Reason, EndPoint, Module),
proc_lib:init_ack(Err)
{error, Reason} ->
return_socket_error(Reason, EndPoint, Module)
end;
{error, Reason} = Err ->
report_socket_error(Reason, EndPoint, Module),
proc_lib:init_ack(Err)
{{error, Reason}, _} ->
return_socket_error(Reason, EndPoint, Module);
{_, {error, Reason}} ->
return_socket_error(Reason, EndPoint, Module)
end.
-spec listen_tcp(inet:port_number(), [gen_tcp:option()]) ->
@@ -216,23 +216,39 @@ listen_tcp(Port, SockOpts) ->
setup_provisional_udsocket_dir(DefinitivePath) ->
ProvisionalPath = get_provisional_udsocket_path(DefinitivePath),
?DEBUG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s",
?INFO_MSG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s",
[ProvisionalPath, DefinitivePath]),
ProvisionalPath.
ProvisionalPathAbsolute = relative_socket_to_mnesia(ProvisionalPath),
create_base_dir(ProvisionalPathAbsolute),
ProvisionalPathAbsolute.
get_provisional_udsocket_path(Path) ->
PathBase64 = misc:term_to_base64(Path),
ReproducibleSecret = binary:part(crypto:hash(sha, misc:atom_to_binary(erlang:get_cookie())), 1, 8),
PathBase64 = misc:term_to_base64({ReproducibleSecret, Path}),
PathBuild = filename:join(misc:get_home(), PathBase64),
%% Shorthen the path, a long path produces a crash when opening the socket.
binary:part(PathBuild, {0, erlang:min(107, byte_size(PathBuild))}).
DestPath = filename:join(filename:dirname(Path), PathBase64),
case {byte_size(DestPath) > 107, byte_size(PathBuild) > 107} of
{false, _} ->
DestPath;
{true, false} ->
?INFO_MSG("The provisional Unix Domain Socket path ~ts is longer than 107, let's use home directory instead which is ~p", [DestPath, byte_size(PathBuild)]),
PathBuild;
{true, true} ->
?ERROR_MSG("The Unix Domain Socket path ~ts is too long, "
"and I cannot create the provisional file safely. "
"Please configure a shorter path and try again.", [Path]),
throw({error_socket_path_too_long, Path})
end.
get_definitive_udsocket_path(<<"unix", _>> = Unix) ->
Unix;
get_definitive_udsocket_path(ProvisionalPath) ->
PathBase64 = filename:basename(ProvisionalPath),
{term, Path} = misc:base64_to_term(PathBase64),
{term, {_, Path}} = misc:base64_to_term(PathBase64),
relative_socket_to_mnesia(Path).
-spec set_definitive_udsocket(integer() | binary(), opts()) -> ok | {error, file:posix() | badarg}.
set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) ->
Prov = get_provisional_udsocket_path(Path),
Usd = maps:get(unix_socket, Opts),
@@ -263,16 +279,19 @@ set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) ->
end
end,
FinalPath = relative_socket_to_mnesia(Path),
FinalPathDir = filename:dirname(FinalPath),
case file:make_dir(FinalPathDir) of
create_base_dir(FinalPath),
file:rename(Prov, FinalPath);
set_definitive_udsocket(Port, _Opts) when is_integer(Port) ->
ok.
create_base_dir(Path) ->
Dirname = filename:dirname(Path),
case file:make_dir(Dirname) of
ok ->
file:change_mode(FinalPathDir, 8#00700);
file:change_mode(Dirname, 8#00700);
_ ->
ok
end,
file:rename(Prov, FinalPath);
set_definitive_udsocket(_Port, _Opts) ->
ok.
end.
relative_socket_to_mnesia(Path1) ->
case filename:pathtype(Path1) of
@@ -584,10 +603,20 @@ config_reloaded() ->
end
end, New).
-spec report_socket_error(inet:posix(), endpoint(), module()) -> ok.
report_socket_error(Reason, EndPoint, Module) ->
-spec return_socket_error(inet:posix(), endpoint(), module()) -> no_return().
return_socket_error(Reason, EndPoint, Module) ->
?ERROR_MSG("Failed to open socket at ~ts for ~ts: ~ts",
[format_endpoint(EndPoint), Module, format_error(Reason)]).
[format_endpoint(EndPoint), Module, format_error(Reason)]),
return_init_error(Reason).
-ifdef(OTP_BELOW_26).
return_init_error(Reason) ->
proc_lib:init_ack({error, Reason}).
-else.
-spec return_init_error(inet:posix()) -> no_return().
return_init_error(Reason) ->
proc_lib:init_fail({error, Reason}, {exit, normal}).
-endif.
-spec format_error(inet:posix() | atom()) -> string().
format_error(Reason) ->
+9 -5
View File
@@ -47,6 +47,8 @@
-export_type([loglevel/0]).
-include("logger.hrl").
%%%===================================================================
%%% API
%%%===================================================================
@@ -383,19 +385,21 @@ console_template() ->
false ->
[time, " [", level, "] " | msg()]
end.
msg() ->
[{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []},
msg, io_lib:nl()].
-else.
console_template() ->
[time, " [", level, "] " | msg()].
[time, " ", ?CLEAD, ?CDEFAULT, clevel, "[", level, "] ", ?CMID, ?CDEFAULT, ctext | msg()].
msg() ->
[{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []},
msg, ?CCLEAN, io_lib:nl()].
-endif.
file_template() ->
[time, " [", level, "] ", pid,
{mfa, ["@", mfa, {line, [":", line], []}], []}, " " | msg()].
msg() ->
[{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []},
msg, io_lib:nl()].
-spec reopen_log() -> ok.
reopen_log() ->
ok.
+4 -3
View File
@@ -80,10 +80,11 @@ init([]) ->
Schema = read_schema_file(),
{ok, #state{schema = Schema}};
false ->
?CRITICAL_MSG("Node name mismatch: I'm [~ts], "
"the database is owned by ~p", [MyNode, DbNodes]),
?CRITICAL_MSG("Erlang node name mismatch: I'm running in node [~ts], "
"but the mnesia database is owned by ~p", [MyNode, DbNodes]),
?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg "
"or change node name in Mnesia", []),
"or change node name in Mnesia by running: "
"ejabberdctl mnesia_change ~ts", [hd(DbNodes)]),
{stop, node_name_mismatch}
end.
+7 -7
View File
@@ -18,7 +18,7 @@
-export([auth_method/0, auth_method/1]).
-export([auth_opts/0, auth_opts/1]).
-export([auth_password_format/0, auth_password_format/1]).
-export([auth_password_types_hidden_in_scram1/0, auth_password_types_hidden_in_scram1/1]).
-export([auth_password_types_hidden_in_sasl1/0, auth_password_types_hidden_in_sasl1/1]).
-export([auth_scram_hash/0, auth_scram_hash/1]).
-export([auth_stored_password_types/0, auth_stored_password_types/1]).
-export([auth_use_cache/0, auth_use_cache/1]).
@@ -264,12 +264,12 @@ auth_password_format() ->
auth_password_format(Host) ->
ejabberd_config:get_option({auth_password_format, Host}).
-spec auth_password_types_hidden_in_scram1() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512'].
auth_password_types_hidden_in_scram1() ->
auth_password_types_hidden_in_scram1(global).
-spec auth_password_types_hidden_in_scram1(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512'].
auth_password_types_hidden_in_scram1(Host) ->
ejabberd_config:get_option({auth_password_types_hidden_in_scram1, Host}).
-spec auth_password_types_hidden_in_sasl1() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512'].
auth_password_types_hidden_in_sasl1() ->
auth_password_types_hidden_in_sasl1(global).
-spec auth_password_types_hidden_in_sasl1(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512'].
auth_password_types_hidden_in_sasl1(Host) ->
ejabberd_config:get_option({auth_password_types_hidden_in_sasl1, Host}).
-spec auth_scram_hash() -> 'sha' | 'sha256' | 'sha512'.
auth_scram_hash() ->
+2 -2
View File
@@ -79,7 +79,7 @@ opt_type(auth_opts) ->
end;
opt_type(auth_stored_password_types) ->
econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512]));
opt_type(auth_password_types_hidden_in_scram1) ->
opt_type(auth_password_types_hidden_in_sasl1) ->
econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512]));
opt_type(auth_password_format) ->
econf:enum([plain, scram]);
@@ -566,7 +566,7 @@ options() ->
{auth_password_format, plain},
{auth_scram_hash, sha},
{auth_stored_password_types, []},
{auth_password_types_hidden_in_scram1, []},
{auth_password_types_hidden_in_sasl1, []},
{auth_external_user_exists_check, true},
{auth_use_cache,
fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
+13 -7
View File
@@ -109,14 +109,20 @@ doc() ->
desc =>
?T("_`database.md#default-database|Default database`_ "
"to store persistent data in ejabberd. "
"Modules and other components (e.g. authentication) "
"may have its own value. The default value is 'mnesia'.")}},
"Some components can be configured with specific toplevel options "
"like _`oauth_db_type`_. "
"Many modules can be configured with specific module options, "
"usually named `db_type`. "
"The default value is 'mnesia'.")}},
{default_ram_db,
#{value => "mnesia | redis | sql",
desc =>
?T("Default volatile (in-memory) storage for ejabberd. "
"Modules and other components (e.g. session management) "
"may have its own value. The default value is 'mnesia'.")}},
"Some components can be configured with specific toplevel options "
"like _`router_db_type`_ and _`sm_db_type`_. "
"Some modules can be configured with specific module options, "
"usually named `ram_db_type`. "
"The default value is 'mnesia'.")}},
{queue_type,
#{value => "ram | file",
desc =>
@@ -393,12 +399,12 @@ doc() ->
"depends on the _`auth_scram_hash`_ option."), "",
?T("The default value is 'plain'."), ""]}},
{auth_password_types_hidden_in_scram1,
{auth_password_types_hidden_in_sasl1,
#{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]",
note => "added in 25.07",
desc =>
?T("List of password types that should not be offered in SCRAM1 authenticatication. "
"Because SCRAM1, unlike SCRAM2, can't have list of available mechanisms tailored to "
?T("List of password types that should not be offered in SASL1 authenticatication. "
"Because SASL1, unlike SASL2, can't have list of available mechanisms tailored to "
"individual user, it's possible that offered mechanisms will not be compatible "
"with stored password, especially if new password type was added recently. "
"This option allows disabling offering some mechanisms in SASL1, to a time until new "
+1
View File
@@ -21,6 +21,7 @@
-export([start_link/0, new/1, update/2, match/3, get_max_rate/1]).
-export([reload_from_config/0]).
-export([read_shaper_rules/2]).
-export([validator/1, shaper_rules_validator/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+46 -37
View File
@@ -43,10 +43,12 @@
is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4,
get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1,
crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1,
uri_decode/1,
json_encode/1, json_decode/1,
set_proc_label/1,
match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1,
semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]).
semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1,
lists_uniq/1]).
%% Deprecated functions
-export([decode_base64/1, encode_base64/1]).
@@ -71,45 +73,11 @@
-type distance_cache() :: #{{string(), string()} => non_neg_integer()}.
-spec uri_parse(binary()|string()) -> {ok, string(), string(), string(), number(), string(), string()} | {error, term()}.
-ifdef(USE_OLD_HTTP_URI).
uri_parse(URL) when is_binary(URL) ->
uri_parse(binary_to_list(URL));
uri_parse(URL) ->
uri_parse(URL, []).
yconf:parse_uri(URL).
uri_parse(URL, Protocols) when is_binary(URL) ->
uri_parse(binary_to_list(URL), Protocols);
uri_parse(URL, Protocols) ->
case http_uri:parse(URL, [{scheme_defaults, Protocols}]) of
{ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
{ok, atom_to_list(Scheme), UserInfo, Host, Port, Path, Query};
{error, _} = E ->
E
end.
-else.
uri_parse(URL) when is_binary(URL) ->
uri_parse(binary_to_list(URL));
uri_parse(URL) ->
uri_parse(URL, [{http, 80}, {https, 443}]).
uri_parse(URL, Protocols) when is_binary(URL) ->
uri_parse(binary_to_list(URL), Protocols);
uri_parse(URL, Protocols) ->
case uri_string:parse(URL) of
#{scheme := Scheme, host := Host, port := Port, path := Path} = M1 ->
{ok, Scheme, maps:get(userinfo, M1, ""), Host, Port, Path, maps:get(query, M1, "")};
#{scheme := Scheme, host := Host, path := Path} = M2 ->
case lists:keyfind(list_to_atom(Scheme), 1, Protocols) of
{_, Port} ->
{ok, Scheme, maps:get(userinfo, M2, ""), Host, Port, Path, maps:get(query, M2, "")};
_ ->
{error, unknown_protocol}
end;
{error, Atom, _} ->
{error, Atom}
end.
-endif.
yconf:parse_uri(URL, Protocols).
-ifdef(OTP_BELOW_25).
-ifdef(OTP_BELOW_24).
@@ -124,6 +92,26 @@ uri_quote(Data) ->
uri_string:quote(Data).
-endif.
%% @doc Decode a part of the URL and return string()
%% -spec url_decode(binary()) -> bitstring().
-ifdef(OTP_BELOW_24).
uri_decode(Path) -> uri_decode(Path, <<>>).
uri_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) ->
Hex = list_to_integer([Hi, Lo], 16),
if Hex == 0 -> exit(badurl);
true -> ok
end,
uri_decode(Tail, <<Acc/binary, Hex>>);
uri_decode(<<H, T/binary>>, Acc) when H /= 0 ->
uri_decode(T, <<Acc/binary, H>>);
uri_decode(<<>>, Acc) -> Acc.
-else.
uri_decode(Path) ->
uri_string:percent_decode(Path).
-endif.
-ifdef(USE_OLD_CRYPTO_HMAC).
crypto_hmac(Type, Key, Data) -> crypto:hmac(Type, Key, Data).
crypto_hmac(Type, Key, Data, MacL) -> crypto:hmac(Type, Key, Data, MacL).
@@ -145,6 +133,9 @@ json_encode({[{_Key, _Value} | _]} = Term) ->
(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)).
json_decode(Bin) ->
@@ -819,3 +810,21 @@ set_proc_label(_Label) ->
set_proc_label(Label) ->
proc_lib:set_label(Label).
-endif.
-ifdef(OTP_BELOW_25).
-spec lists_uniq([term()]) -> [term()].
lists_uniq(List) ->
lists_uniq_int(List, #{}).
lists_uniq_int([El | Rest], Existing) ->
case maps:is_key(El, Existing) of
true -> lists_uniq_int(Rest, Existing);
_ -> [El | lists_uniq_int(Rest, Existing#{El => true})]
end;
lists_uniq_int([], _) ->
[].
-else.
-spec lists_uniq([term()]) -> [term()].
lists_uniq(List) ->
lists:uniq(List).
-endif.
+33 -67
View File
@@ -268,14 +268,14 @@ get_commands_spec() ->
#ejabberd_commands{name = ban_account, tags = [accounts],
desc = "Ban an account",
longdesc = "This command kicks the account sessions, "
"sets a random password, and stores ban details in the "
"account private storage. "
"stores ban details in the account private storage, "
"which blocks login to the account. "
"This command requires _`mod_private`_ to be enabled. "
"Check also _`get_ban_details`_ API "
"and _`unban_account`_ API.",
module = ?MODULE, function = ban_account_v2,
version = 2,
note = "improved in 24.06",
note = "improved in 25.08",
args = [{user, binary}, {host, binary}, {reason, binary}],
args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
args_desc = ["User name to ban", "Server name",
@@ -301,7 +301,7 @@ get_commands_spec() ->
{"lastdate", "2024-04-22T08:39:12Z"},
{"lastreason", "Connection reset by peer"}]},
#ejabberd_commands{name = unban_account, tags = [accounts],
desc = "Revert the ban from an account: set back the old password",
desc = "Remove the ban from an account",
longdesc = "Check _`ban_account`_ API.",
module = ?MODULE, function = unban_account,
version = 2,
@@ -1158,6 +1158,7 @@ ban_account(User, Host, ReasonText) ->
ok.
kick_sessions(User, Server, Reason) ->
ejabberd_hooks:run(sm_kick_user, Server, [User, Server]),
lists:map(
fun(Resource) ->
kick_this_session(User, Server, Resource, Reason)
@@ -1189,27 +1190,29 @@ prepare_reason(Reason) when is_binary(Reason) ->
%% Ban account v2
ban_account_v2(User, Host, ReasonText) ->
case gen_mod:is_loaded(Host, mod_private) of
false ->
IsPrivateEnabled = gen_mod:is_loaded(Host, mod_private),
Exists = ejabberd_auth:user_exists(User, Host),
IsBanned = is_banned(User, Host),
case {IsPrivateEnabled, Exists, IsBanned} of
{true, true, false} ->
ban_account_v2_b(User, Host, ReasonText);
{false, _, _} ->
mod_private_is_required_but_disabled;
true ->
case is_banned(User, Host) of
true ->
account_was_already_banned;
false ->
ban_account_v2_b(User, Host, ReasonText)
end
{_, false, _} ->
account_does_not_exist;
{_, _, true} ->
account_was_already_banned;
{_, _, _} ->
other_error
end.
ban_account_v2_b(User, Host, ReasonText) ->
Reason = prepare_reason(ReasonText),
Pass = ejabberd_auth:get_password_s(User, Host),
Last = get_last(User, Host),
BanDate = xmpp_util:encode_timestamp(erlang:timestamp()),
Hash = get_hash_value(User, Host),
BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate, Hash),
BanPrivateXml = build_ban_xmlel(Reason, Last, BanDate, Hash),
ok = private_set2(User, Host, BanPrivateXml),
ok = set_random_password_v2(User, Host),
kick_sessions(User, Host, Reason),
ok.
@@ -1217,36 +1220,16 @@ get_hash_value(User, Host) ->
Cookie = misc:atom_to_binary(erlang:get_cookie()),
misc:term_to_base64(crypto:hash(sha256, <<User/binary, Host/binary, Cookie/binary>>)).
set_random_password_v2(User, Server) ->
NewPass = p1_rand:get_string(),
ok = ejabberd_auth:set_password(User, Server, NewPass).
build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate, Hash) ->
PassEls = build_pass_els(Pass),
build_ban_xmlel(Reason, {LastDate, LastReason}, BanDate, Hash) ->
#xmlel{name = <<"banned">>,
attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}],
children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]},
#xmlel{name = <<"password">>, attrs = [], children = PassEls},
#xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]},
#xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]},
#xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]},
#xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, Hash}]}
]}.
build_pass_els(Pass) when is_binary(Pass) ->
[{xmlcdata, Pass}];
build_pass_els(#scram{storedkey = StoredKey,
serverkey = ServerKey,
salt = Salt,
hash = Hash,
iterationcount = IterationCount}) ->
[#xmlel{name = <<"storedkey">>, attrs = [], children = [{xmlcdata, StoredKey}]},
#xmlel{name = <<"serverkey">>, attrs = [], children = [{xmlcdata, ServerKey}]},
#xmlel{name = <<"salt">>, attrs = [], children = [{xmlcdata, Salt}]},
#xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, misc:atom_to_binary(Hash)}]},
#xmlel{name = <<"iterationcount">>, attrs = [], children = [{xmlcdata, integer_to_binary(IterationCount)}]}
].
%%
%% Get ban details
@@ -1286,43 +1269,26 @@ is_banned(User, Host) ->
%% Unban account
unban_account(User, Host) ->
case gen_mod:is_loaded(Host, mod_private) of
false ->
IsPrivateEnabled = gen_mod:is_loaded(Host, mod_private),
Exists = ejabberd_auth:user_exists(User, Host),
IsBanned = is_banned(User, Host),
case {IsPrivateEnabled, Exists, IsBanned} of
{true, true, true} ->
unban_account2(User, Host);
{false, _, _} ->
mod_private_is_required_but_disabled;
true ->
case is_banned(User, Host) of
false ->
account_was_not_banned;
true ->
unban_account2(User, Host)
end
{_, false, _} ->
account_does_not_exist;
{_, _, false} ->
account_was_not_banned;
{_, _, _} ->
other_error
end.
unban_account2(User, Host) ->
OldPass = get_oldpass(User, Host),
ok = ejabberd_auth:set_password(User, Host, OldPass),
UnBanPrivateXml = build_unban_xmlel(),
private_set2(User, Host, UnBanPrivateXml).
get_oldpass(User, Host) ->
[El] = private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>),
Pass = fxml:get_subtag(El, <<"password">>),
get_pass(Pass).
get_pass(#xmlel{children = [{xmlcdata, Pass}]}) ->
Pass;
get_pass(#xmlel{children = ScramEls} = Pass) when is_list(ScramEls) ->
StoredKey = fxml:get_subtag_cdata(Pass, <<"storedkey">>),
ServerKey = fxml:get_subtag_cdata(Pass, <<"serverkey">>),
Salt = fxml:get_subtag_cdata(Pass, <<"salt">>),
Hash = fxml:get_subtag_cdata(Pass, <<"hash">>),
IterationCount = fxml:get_subtag_cdata(Pass, <<"iterationcount">>),
#scram{storedkey = StoredKey,
serverkey = ServerKey,
salt = Salt,
hash = binary_to_existing_atom(Hash, latin1),
iterationcount = binary_to_integer(IterationCount)}.
build_unban_xmlel() ->
#xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}]}.
+2 -2
View File
@@ -29,7 +29,7 @@
-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]).
-export([mod_doc/0]).
%% Hooks
-export([c2s_inline_features/2, c2s_handle_sasl2_inline/1,
-export([c2s_inline_features/3, c2s_handle_sasl2_inline/1,
get_tokens/3, get_mechanisms/1, remove_user_tokens/2]).
-include_lib("xmpp/include/xmpp.hrl").
@@ -131,7 +131,7 @@ get_tokens(LServer, LUser, UA) ->
{{Type, CreatedAt < ToRefresh}, Token}
end, Mod:get_tokens(LServer, LUser, ua_hash(UA))).
c2s_inline_features({Sasl, Bind, Extra}, Host) ->
c2s_inline_features({Sasl, Bind, Extra}, Host, _State) ->
{Sasl ++ [#fast{mechs = get_mechanisms(Host)}], Bind, Extra}.
gen_token(#{sasl2_ua_id := UA, server := Server, user := User}) ->
+2 -2
View File
@@ -38,7 +38,7 @@
iq_handler/1, disco_features/5,
depends/2, mod_options/1, mod_doc/0]).
-export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1,
c2s_inline_features/2, c2s_handle_bind2_inline/1]).
c2s_inline_features/3, c2s_handle_bind2_inline/1]).
%% For debugging purposes
-export([list/2]).
@@ -145,7 +145,7 @@ c2s_session_resumed(State) ->
c2s_session_opened(State) ->
maps:remove(carboncopy, State).
c2s_inline_features({Sasl, Bind, Extra} = Acc, Host) ->
c2s_inline_features({Sasl, Bind, Extra} = Acc, Host, _State) ->
case gen_mod:is_loaded(Host, ?MODULE) of
true ->
{Sasl, [#bind2_feature{var = ?NS_CARBONS_2} | Bind], Extra};
+40 -3
View File
@@ -64,11 +64,12 @@ process([], #request{method = 'GET', host = Host, q = Query, raw_path = RawPath1
CSS = get_file_url(Host, conversejs_css,
<<RawPath/binary, "/converse.min.css">>,
<<"https://cdn.conversejs.org/dist/converse.min.css">>),
PluginsHtml = get_plugins_html(Host, RawPath),
Init = [{<<"discover_connection_methods">>, false},
{<<"default_domain">>, Domain},
{<<"domain_placeholder">>, Domain},
{<<"registration_domain">>, Domain},
{<<"assets_path">>, RawPath},
{<<"assets_path">>, <<RawPath/binary, "/">>},
{<<"view_mode">>, <<"fullscreen">>}
| ExtraOptions],
Init2 =
@@ -89,7 +90,8 @@ process([], #request{method = 'GET', host = Host, q = Query, raw_path = RawPath1
<<"<meta charset='utf-8'>">>,
<<"<link rel='stylesheet' type='text/css' media='screen' href='">>,
fxml:crypt(CSS), <<"'>">>,
<<"<script src='">>, fxml:crypt(Script), <<"' charset='utf-8'></script>">>,
<<"<script src='">>, fxml:crypt(Script), <<"' charset='utf-8'></script>">>
] ++ PluginsHtml ++ [
<<"</head>">>,
<<"<body>">>,
<<"<script>">>,
@@ -115,6 +117,7 @@ is_served_file([<<"emojis.js">>]) -> true;
is_served_file([<<"locales">>, _]) -> true;
is_served_file([<<"locales">>, <<"dayjs">>, _]) -> true;
is_served_file([<<"webfonts">>, _]) -> true;
is_served_file([<<"plugins">>, _]) -> true;
is_served_file(_) -> false.
serve(Host, LocalPath) ->
@@ -224,6 +227,25 @@ get_auto_file_url(Host, Filename, Default) ->
_ -> Filename
end.
get_plugins_html(Host, RawPath) ->
Resources = get_conversejs_resources(Host),
lists:map(fun(F) ->
Plugin =
case {F, Resources} of
{<<"libsignal">>, undefined} ->
<<"https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js">>;
{<<"libsignal">>, Path} ->
?WARNING_MSG("~p is configured to use local Converse files "
"from path ~ts but the public plugin ~ts!",
[?MODULE, Path, F]),
<<"https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js">>;
_ ->
fxml:crypt(<<RawPath/binary, "/plugins/", F/binary>>)
end,
<<"<script src='", Plugin/binary, "' charset='utf-8'></script>">>
end,
gen_mod:get_module_opt(Host, ?MODULE, conversejs_plugins)).
%%----------------------------------------------------------------------
%% WebAdmin link and autologin
%%----------------------------------------------------------------------
@@ -305,6 +327,8 @@ mod_opt_type(conversejs_script) ->
econf:binary();
mod_opt_type(conversejs_css) ->
econf:binary();
mod_opt_type(conversejs_plugins) ->
econf:list(econf:binary());
mod_opt_type(default_domain) ->
econf:host().
@@ -315,6 +339,7 @@ mod_options(Host) ->
{conversejs_resources, undefined},
{conversejs_options, []},
{conversejs_script, auto},
{conversejs_plugins, []},
{conversejs_css, auto}].
mod_doc() ->
@@ -345,6 +370,7 @@ mod_doc() ->
"modules:",
" mod_bosh: {}",
" mod_conversejs:",
" conversejs_plugins: [\"libsignal\"]",
" websocket_url: \"ws://@HOST@:5280/websocket\""]},
{?T("Host Converse locally and let auto detection of WebSocket and Converse URLs:"),
["listen:",
@@ -358,7 +384,9 @@ mod_doc() ->
"",
"modules:",
" mod_conversejs:",
" conversejs_resources: \"/home/ejabberd/conversejs-9.0.0/package/dist\""]},
" conversejs_resources: \"/home/ejabberd/conversejs-x.y.z/package/dist\"",
" conversejs_plugins: [\"libsignal-protocol.min.js\"]",
" # File path is: /home/ejabberd/conversejs-x.y.z/package/dist/plugins/libsignal-protocol.min.js"]},
{?T("Configure some additional options for Converse"),
["modules:",
" mod_conversejs:",
@@ -410,6 +438,15 @@ mod_doc() ->
"See https://conversejs.org/docs/html/configuration.html[Converse configuration]. "
"Only boolean, integer and string values are supported; "
"lists are not supported.")}},
{conversejs_plugins,
#{value => ?T("[Filename]"),
desc =>
?T("List of additional local files to include as scripts in the homepage. "
"Please make sure those files are available in the path specified in "
"'conversejs_resources' option, in subdirectory 'plugins/'. "
"If using the public Converse client, then '\"libsignal\"' "
"gets replaced with the URL of the public library. "
"The default value is '[]'.")}},
{conversejs_script,
#{value => ?T("auto | URL"),
desc =>
+7
View File
@@ -6,6 +6,7 @@
-export([bosh_service_url/1]).
-export([conversejs_css/1]).
-export([conversejs_options/1]).
-export([conversejs_plugins/1]).
-export([conversejs_resources/1]).
-export([conversejs_script/1]).
-export([default_domain/1]).
@@ -29,6 +30,12 @@ conversejs_options(Opts) when is_map(Opts) ->
conversejs_options(Host) ->
gen_mod:get_module_opt(Host, mod_conversejs, conversejs_options).
-spec conversejs_plugins(gen_mod:opts() | global | binary()) -> [binary()].
conversejs_plugins(Opts) when is_map(Opts) ->
gen_mod:get_opt(conversejs_plugins, Opts);
conversejs_plugins(Host) ->
gen_mod:get_module_opt(Host, mod_conversejs, conversejs_plugins).
-spec conversejs_resources(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
conversejs_resources(Opts) when is_map(Opts) ->
gen_mod:get_opt(conversejs_resources, Opts);
+22 -6
View File
@@ -319,8 +319,10 @@ mod_doc() ->
desc =>
?T("This option specifies the initial part of the PUT URLs "
"used for file uploads. The keyword '@HOST@' is replaced "
"with the virtual host name. NOTE: different virtual "
"hosts cannot use the same PUT URL. "
"with the virtual host name. "
"And '@HOST_URL_ENCODE@' is replaced with the host name encoded for URL, "
"useful when your virtual hosts contain non-latin characters. "
"NOTE: different virtual hosts cannot use the same PUT URL. "
"The default value is '\"https://@HOST@:5443/upload\"'.")}},
{get_url,
#{value => ?T("URL"),
@@ -531,7 +533,8 @@ process(LocalPath, #request{method = Method, host = Host, ip = IP})
[Method, encode_addr(IP), Host]),
http_response(404);
process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP,
length = Length} = Request) ->
length = Length} = Request0) ->
Request = Request0#request{host = redecode_url(Host)},
{Proc, Slot} = parse_http_request(Request),
try gen_server:call(Proc, {use_slot, Slot, Length}, ?CALL_TIMEOUT) of
{ok, Path, FileMode, DirMode, GetPrefix, Thumbnail, CustomHeaders} ->
@@ -571,9 +574,10 @@ process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP,
[encode_addr(IP), Host, Error]),
http_response(500)
end;
process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request)
process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request0)
when Method == 'GET';
Method == 'HEAD' ->
Request = Request0#request{host = redecode_url(Host)},
{Proc, [_UserDir, _RandDir, FileName] = Slot} = parse_http_request(Request),
try gen_server:call(Proc, get_conf, ?CALL_TIMEOUT) of
{ok, DocRoot, CustomHeaders} ->
@@ -907,8 +911,8 @@ mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS, Query) ->
GetURL = str:join([GetPrefix | Slot], <<$/>>),
mk_slot(PutURL, GetURL, XMLNS, Query);
mk_slot(PutURL, GetURL, XMLNS, Query) ->
PutURL1 = <<(misc:url_encode(PutURL))/binary, Query/binary>>,
GetURL1 = misc:url_encode(GetURL),
PutURL1 = <<(reencode_url(PutURL))/binary, Query/binary>>,
GetURL1 = reencode_url(GetURL),
case XMLNS of
?NS_HTTP_UPLOAD_0 ->
#upload_slot_0{get = GetURL1, put = PutURL1, xmlns = XMLNS};
@@ -916,6 +920,18 @@ mk_slot(PutURL, GetURL, XMLNS, Query) ->
#upload_slot{get = GetURL1, put = PutURL1, xmlns = XMLNS}
end.
reencode_url(UrlString) ->
{ok, _, _, Host, _, _, _} = yconf:parse_uri(UrlString),
HostDecoded = misc:uri_decode(Host),
HostIdna = idna:encode(HostDecoded),
re:replace(UrlString, Host, HostIdna, [{return, binary}]).
redecode_url(UrlString) ->
{ok, _, _, HostIdna, _, _, _} = yconf:parse_uri(<<"http://", UrlString/binary>>),
HostDecoded = idna:decode(HostIdna),
Host = misc:uri_quote(HostDecoded),
re:replace(UrlString, HostIdna, Host, [{return, binary}]).
-spec make_user_string(jid(), sha1 | node) -> binary().
make_user_string(#jid{luser = U, lserver = S}, sha1) ->
str:sha(<<U/binary, $@, S/binary>>);
+41 -16
View File
@@ -611,22 +611,28 @@ parse_auth4(<<>>, Key, Val, Ts) ->
prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event,
RoomVersion) ->
Event2 =
Keys =
case RoomVersion#room_version.updated_redaction_rules of
false ->
maps:with(
[<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>,
<<"state_key">>, <<"content">>, <<"hashes">>,
<<"signatures">>, <<"depth">>, <<"prev_events">>,
<<"prev_state">>, <<"auth_events">>, <<"origin">>,
<<"origin_server_ts">>, <<"membership">>], Event);
[<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>,
<<"state_key">>, <<"content">>, <<"hashes">>,
<<"signatures">>, <<"depth">>, <<"prev_events">>,
<<"prev_state">>, <<"auth_events">>, <<"origin">>,
<<"origin_server_ts">>, <<"membership">>];
true ->
maps:with(
[<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>,
<<"state_key">>, <<"content">>, <<"hashes">>,
<<"signatures">>, <<"depth">>, <<"prev_events">>,
<<"auth_events">>, <<"origin_server_ts">>], Event)
[<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>,
<<"state_key">>, <<"content">>, <<"hashes">>,
<<"signatures">>, <<"depth">>, <<"prev_events">>,
<<"auth_events">>, <<"origin_server_ts">>]
end,
Keys2 =
case {RoomVersion#room_version.hydra, Type} of
{true, <<"m.room.create">>} ->
lists:delete(<<"room_id">>, Keys);
_ ->
Keys
end,
Event2 = maps:with(Keys2, Event),
Content2 =
case Type of
<<"m.room.member">> ->
@@ -738,6 +744,8 @@ is_canonical_json(B) when is_binary(B) ->
true;
is_canonical_json(B) when is_boolean(B) ->
true;
is_canonical_json(null) ->
true;
is_canonical_json(Map) when is_map(Map) ->
maps:fold(
fun(_K, V, true) ->
@@ -976,7 +984,11 @@ mod_opt_type(key) ->
crypto:generate_key(eddsa, ed25519, Key2)
end;
mod_opt_type(matrix_id_as_jid) ->
econf:bool().
econf:bool();
mod_opt_type(notary_servers) ->
econf:list(econf:host());
mod_opt_type(leave_timeout) ->
econf:non_neg_int().
-spec mod_options(binary()) -> [{key, {binary(), binary()}} |
{atom(), any()}].
@@ -986,14 +998,19 @@ mod_options(Host) ->
{host, <<"matrix.", Host/binary>>},
{key_name, <<"">>},
{key, {<<"">>, <<"">>}},
{matrix_id_as_jid, false}].
{matrix_id_as_jid, false},
{notary_servers, []},
{leave_timeout, 0}].
mod_doc() ->
#{desc =>
[?T("https://matrix.org/[Matrix] gateway. "),
?T("Supports room versions 9, 10 and 11 since ejabberd 25.03; "
"room versions 4 and higher since ejabberd 25.07; "
"room version 12 (hydra rooms) since ejabberd 25.08. "),
?T("Erlang/OTP 25 or higher is required to use this module."),
?T("This module is available since ejabberd 24.02.")],
note => "improved in 25.07",
note => "improved in 25.08",
example =>
["listen:",
" -",
@@ -1042,7 +1059,15 @@ mod_doc() ->
"Matrix user '@user:matrixdomain.tld', the client must send a message "
"to the JID 'user%matrixdomain.tld@matrix.myxmppdomain.tld', where "
"'matrix.myxmppdomain.tld' is the JID of the gateway service as set by the "
"'host' option. The default is 'false'.")}}
"'host' option. The default is 'false'.")}},
{notary_servers,
#{value => "[Server, ...]",
desc =>
?T("A list of notary servers.")}},
{leave_timeout,
#{value => "integer()",
desc =>
?T("Delay in seconds between a user leaving a MUC room and sending 'leave' Matrix event.")}}
]
}.
-endif.
+14
View File
@@ -6,8 +6,10 @@
-export([host/1]).
-export([key/1]).
-export([key_name/1]).
-export([leave_timeout/1]).
-export([matrix_domain/1]).
-export([matrix_id_as_jid/1]).
-export([notary_servers/1]).
-spec host(gen_mod:opts() | global | binary()) -> binary().
host(Opts) when is_map(Opts) ->
@@ -27,6 +29,12 @@ key_name(Opts) when is_map(Opts) ->
key_name(Host) ->
gen_mod:get_module_opt(Host, mod_matrix_gw, key_name).
-spec leave_timeout(gen_mod:opts() | global | binary()) -> non_neg_integer().
leave_timeout(Opts) when is_map(Opts) ->
gen_mod:get_opt(leave_timeout, Opts);
leave_timeout(Host) ->
gen_mod:get_module_opt(Host, mod_matrix_gw, leave_timeout).
-spec matrix_domain(gen_mod:opts() | global | binary()) -> binary().
matrix_domain(Opts) when is_map(Opts) ->
gen_mod:get_opt(matrix_domain, Opts);
@@ -39,3 +47,9 @@ matrix_id_as_jid(Opts) when is_map(Opts) ->
matrix_id_as_jid(Host) ->
gen_mod:get_module_opt(Host, mod_matrix_gw, matrix_id_as_jid).
-spec notary_servers(gen_mod:opts() | global | binary()) -> [binary()].
notary_servers(Opts) when is_map(Opts) ->
gen_mod:get_opt(notary_servers, Opts);
notary_servers(Host) ->
gen_mod:get_module_opt(Host, mod_matrix_gw, notary_servers).
+661 -325
View File
File diff suppressed because it is too large Load Diff
+168 -149
View File
@@ -44,12 +44,23 @@
{to :: binary(),
pid :: pid()}).
-record(pending,
{request_id :: any(),
servers :: [binary()],
key_queue = []}).
-record(wait,
{timer_ref :: reference(),
last :: integer()}).
-record(data,
{host :: binary(),
matrix_server :: binary(),
matrix_host_port :: {binary(), integer()} | undefined,
keys = #{},
key_queue = #{}}).
state :: #pending{} | #wait{}}).
-define(KEYS_REQUEST_TIMEOUT, 600000).
%%%===================================================================
%%% API
@@ -227,8 +238,11 @@ init([Host, MatrixServer]) ->
#matrix_s2s{to = MatrixServer,
pid = self()}),
{ok, state_name,
#data{host = Host,
matrix_server = MatrixServer}}.
request_keys(
MatrixServer,
#data{host = Host,
matrix_server = MatrixServer,
state = #wait{timer_ref = make_ref(), last = 0}})}.
%%--------------------------------------------------------------------
%% @private
@@ -246,7 +260,7 @@ init([Host, MatrixServer]) ->
handle_event({call, From}, get_matrix_host_port, _State, Data) ->
case Data#data.matrix_host_port of
undefined ->
Result = do_get_matrix_host_port(Data),
Result = do_get_matrix_host_port(Data#data.matrix_server),
Data2 = Data#data{matrix_host_port = Result},
{keep_state, Data2, [{reply, From, Result}]};
Result ->
@@ -254,104 +268,59 @@ handle_event({call, From}, get_matrix_host_port, _State, Data) ->
end;
handle_event({call, From}, {get_key, KeyID}, State, Data) ->
case maps:find(KeyID, Data#data.keys) of
{ok, {ok, _, _} = Result} ->
{keep_state, Data, [{reply, From, Result}]};
{ok, error = Result} ->
{keep_state, Data, [{reply, From, Result}]};
{ok, pending} ->
KeyQueue = maps:update_with(
KeyID,
fun(Xs) ->
[From | Xs]
end,
[From],
Data#data.key_queue),
{next_state, State,
Data#data{key_queue = KeyQueue}, []};
{ok, {Key, ValidUntil}} ->
{keep_state, Data, [{reply, From, {ok, Key, ValidUntil}}]};
error ->
{MHost, MPort} = do_get_matrix_host_port(Data),
URL = <<"https://", MHost/binary,
":", (integer_to_binary(MPort))/binary,
"/_matrix/key/v2/server/", KeyID/binary>>,
Self = self(),
httpc:request(get, {URL, []},
[{timeout, 5000}],
[{sync, false},
{receiver,
fun({_RequestId, Result}) ->
gen_statem:cast(
Self, {key_reply, KeyID, Result})
end}]),
Keys = (Data#data.keys)#{KeyID => pending},
KeyQueue = maps:update_with(
KeyID,
fun(Xs) ->
[From | Xs]
end,
[From],
Data#data.key_queue),
{next_state, State,
Data#data{keys = Keys,
key_queue = KeyQueue},
[]}
case Data#data.state of
#pending{key_queue = KeyQueue} = St ->
KeyQueue2 = [{From, KeyID} | KeyQueue],
{next_state, State,
Data#data{state = St#pending{key_queue = KeyQueue2}}, []};
#wait{timer_ref = TimerRef, last = Last} ->
TS = erlang:system_time(millisecond),
if
Last + ?KEYS_REQUEST_TIMEOUT =< TS ->
Data2 = request_keys(Data#data.matrix_server, Data),
#pending{key_queue = KeyQueue} = St = Data2#data.state,
KeyQueue2 = [{From, KeyID} | KeyQueue],
{next_state, State,
Data2#data{state = St#pending{key_queue = KeyQueue2}}, []};
true ->
Timeout =
case erlang:read_timer(TimerRef) of
false ->
Last + ?KEYS_REQUEST_TIMEOUT - TS;
Left ->
erlang:cancel_timer(TimerRef),
min(Left,
Last + ?KEYS_REQUEST_TIMEOUT - TS)
end,
TRef = erlang:start_timer(Timeout, self(), []),
{next_state, State,
Data#data{state = #wait{timer_ref = TRef,
last = Last}},
[{reply, From, error}]}
end
end
end;
handle_event(cast, {query, AuthParams, _Query, _JSON, _Request} = Msg,
State, Data) ->
#{<<"key">> := KeyID} = AuthParams,
case maps:find(KeyID, Data#data.keys) of
{ok, {ok, VerifyKey, _ValidUntil}} ->
Data2 = process_unverified_query(
KeyID, VerifyKey, Msg, Data),
{next_state, State, Data2, []};
{ok, error} ->
%TODO
{next_state, State, Data, []};
{ok, pending} ->
KeyQueue = maps:update_with(
KeyID,
fun(Xs) ->
[Msg | Xs]
end,
[Msg],
Data#data.key_queue),
{next_state, State,
Data#data{key_queue = KeyQueue}, []};
error ->
{MHost, MPort} = do_get_matrix_host_port(Data),
URL = <<"https://", MHost/binary,
":", (integer_to_binary(MPort))/binary,
"/_matrix/key/v2/server/", KeyID/binary>>,
Self = self(),
httpc:request(get, {URL, []},
[{timeout, 5000}],
[{sync, false},
{receiver,
fun({_RequestId, Result}) ->
gen_statem:cast(
Self, {key_reply, KeyID, Result})
end}]),
Keys = (Data#data.keys)#{KeyID => pending},
KeyQueue = maps:update_with(
KeyID,
fun(Xs) ->
[Msg | Xs]
end,
[Msg],
Data#data.key_queue),
{next_state, State,
Data#data{keys = Keys,
key_queue = KeyQueue},
[]}
end;
handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) ->
KeyVal =
handle_event(cast, {key_reply, RequestID, HTTPResult}, State,
#data{state = #pending{request_id = RequestID,
servers = Servers,
key_queue = KeyQueue} = St} = Data) ->
TS = erlang:system_time(millisecond),
Res =
case HTTPResult of
{{_, 200, _}, _, SJSON} ->
try
JSON = misc:json_decode(SJSON),
?DEBUG("key ~p~n", [JSON]),
JSON1 = misc:json_decode(SJSON),
JSON =
case JSON1 of
#{<<"server_keys">> := [J]} -> J;
_ -> JSON1
end,
?DEBUG("keys ~p~n", [JSON]),
#{<<"verify_keys">> := VerifyKeys} = JSON,
#{KeyID := KeyData} = VerifyKeys,
{KeyID, KeyData, _} = maps:next(maps:iterator(VerifyKeys)),
#{<<"key">> := SKey} = KeyData,
VerifyKey = mod_matrix_gw:base64_decode(SKey),
?DEBUG("key ~p~n", [VerifyKey]),
@@ -366,22 +335,77 @@ handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) ->
ValidUntil2 =
min(ValidUntil,
erlang:system_time(millisecond) + timer:hours(24 * 7)),
{ok, VerifyKey, ValidUntil2}
OldKeysJSON =
case JSON of
#{<<"old_verify_keys">> := OldKeysJ}
when is_map(OldKeysJ) ->
OldKeysJ;
_ ->
#{}
end,
OldKeys =
maps:filtermap(
fun(_KID,
#{<<"key">> := SK,
<<"expired_ts">> := Exp})
when is_integer(Exp),
is_binary(SK) ->
{true, {mod_matrix_gw:base64_decode(SK),
Exp}};
(_, _) -> false
end, OldKeysJSON),
NewKeys =
maps:filtermap(
fun(_KID,
#{<<"key">> := SK})
when is_binary(SK) ->
{true, {mod_matrix_gw:base64_decode(SK),
ValidUntil2}};
(_, _) -> false
end, VerifyKeys),
{ok, maps:merge(OldKeys, NewKeys), ValidUntil2}
catch
_:_ ->
error
{ok, Data#data.keys, TS + ?KEYS_REQUEST_TIMEOUT}
end;
_ ->
error
case Servers of
[] ->
{ok, Data#data.keys, TS + ?KEYS_REQUEST_TIMEOUT};
[S | Servers1] ->
{error,
request_keys(
S,
Data#data{state = St#pending{servers = Servers1}})}
end
end,
Keys = (Data#data.keys)#{KeyID => KeyVal},
Froms = maps:get(KeyID, Data#data.key_queue, []),
KeyQueue = maps:remove(KeyID, Data#data.key_queue),
Data2 = Data#data{keys = Keys,
key_queue = KeyQueue},
Replies = lists:map(fun(From) -> {reply, From, KeyVal} end, Froms),
?DEBUG("KEYS ~p~n", [{Keys, Data2}]),
{next_state, State, Data2, Replies};
case Res of
{ok, Keys, ValidTS} ->
Replies =
lists:map(
fun({From, KeyID}) ->
case maps:find(KeyID, Keys) of
{ok, {Key, KeyValidUntil}} ->
{reply, From, {ok, Key, KeyValidUntil}};
error ->
{reply, From, error}
end
end,
KeyQueue),
TimerRef = erlang:start_timer(max(ValidTS - TS, ?KEYS_REQUEST_TIMEOUT),
self(), []),
Data2 = Data#data{keys = Keys,
state = #wait{timer_ref = TimerRef,
last = TS}},
?DEBUG("KEYS ~p~n", [{Keys, Data2}]),
{next_state, State, Data2, Replies};
{error, Data2} ->
{next_state, State, Data2, []}
end;
handle_event(info, {timeout, TimerRef, []}, State,
#data{state = #wait{timer_ref = TimerRef}} = Data) ->
Data2 = request_keys(Data#data.matrix_server, Data),
{next_state, State, Data2, []};
handle_event(cast, Msg, State, Data) ->
?WARNING_MSG("Unexpected cast: ~p", [Msg]),
{next_state, State, Data, []};
@@ -427,8 +451,7 @@ callback_mode() ->
%%% Internal functions
%%%===================================================================
do_get_matrix_host_port(Data) ->
MatrixServer = Data#data.matrix_server,
do_get_matrix_host_port(MatrixServer) ->
case binary:split(MatrixServer, <<":">>) of
[Addr] ->
case inet:parse_address(binary_to_list(Addr)) of
@@ -530,47 +553,43 @@ check_signature(JSON, SignatureName, KeyID, VerifyKey) ->
false
end.
%process_unverified_queries(KeyID, Data) ->
% case maps:find(KeyID, Data#data.keys) of
% {ok, {ok, VerifyKey, _ValidUntil}} ->
% Queue = maps:get(KeyID, Data#data.key_queue, []),
% KeyQueue = maps:remove(KeyID, Data#data.key_queue),
% Data2 = Data#data{key_queue = KeyQueue},
% lists:foldl(
% fun(Query, DataAcc) ->
% process_unverified_query(KeyID, VerifyKey, Query, DataAcc)
% end, Data2, Queue);
% _ ->
% %% TODO
% Data
% end.
process_unverified_query(
KeyID, VerifyKey, {query, AuthParams, _Query, Content, Request} = _Msg, Data) ->
Destination = mod_matrix_gw_opt:matrix_domain(Data#data.host),
#{<<"sig">> := Sig} = AuthParams,
JSON = #{<<"method">> => atom_to_binary(Request#request.method, latin1),
<<"uri">> => Request#request.raw_path,
<<"origin">> => Data#data.matrix_server,
<<"destination">> => Destination,
<<"signatures">> => #{
Data#data.matrix_server => #{KeyID => Sig}
}
},
JSON2 =
case Content of
none -> JSON;
_ ->
JSON#{<<"content">> => Content}
request_keys(Via, Data) ->
{MHost, MPort} = do_get_matrix_host_port(Via),
URL =
case Data#data.matrix_server of
Via ->
<<"https://", MHost/binary,
":", (integer_to_binary(MPort))/binary,
"/_matrix/key/v2/server">>;
MatrixServer ->
<<"https://", MHost/binary,
":", (integer_to_binary(MPort))/binary,
"/_matrix/key/v2/query/", MatrixServer/binary>>
end,
case check_signature(JSON2, Data#data.matrix_server, KeyID, VerifyKey) of
true ->
todo_remove_me;
%process_query(Msg, Data);
false ->
?WARNING_MSG("Failed authentication: ~p", [JSON]),
%% TODO
Data
Self = self(),
{ok, RequestID} =
httpc:request(get, {URL, []},
[{timeout, 5000}],
[{sync, false},
{receiver,
fun({RequestId, Result}) ->
gen_statem:cast(
Self, {key_reply, RequestId, Result})
end}]),
case Data#data.state of
#pending{request_id = OldReqID} = St ->
case OldReqID of
undefined ->
ok;
_ ->
httpc:cancel_request(OldReqID)
end,
Data#data{state = St#pending{request_id = RequestID}};
#wait{timer_ref = TimerRef} ->
erlang:cancel_timer(TimerRef),
NotaryServers = mod_matrix_gw_opt:notary_servers(Data#data.host),
Data#data{state = #pending{request_id = RequestID,
servers = NotaryServers}}
end.
-endif.
+6 -7
View File
@@ -97,7 +97,7 @@
-callback import(binary(), binary(), [binary()]) -> ok.
-callback store_room(binary(), binary(), binary(), list(), list()|undefined) -> {atomic, any()}.
-callback store_changes(binary(), binary(), binary(), list()) -> {atomic, any()}.
-callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error.
-callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error | {error, atom()}.
-callback forget_room(binary(), binary(), binary()) -> {atomic, any()}.
-callback can_use_nick(binary(), binary(), jid(), binary()) -> boolean().
-callback get_rooms(binary(), binary()) -> [#muc_room{}].
@@ -591,20 +591,17 @@ extract_password(#iq{} = IQ) ->
false
end.
-spec unhibernate_room(binary(), binary(), binary()) -> {ok, pid()} | error.
-spec unhibernate_room(binary(), binary(), binary()) -> {ok, pid()} | {error, notfound | db_failure | term()}.
unhibernate_room(ServerHost, Host, Room) ->
unhibernate_room(ServerHost, Host, Room, true).
-spec unhibernate_room(binary(), binary(), binary(), boolean()) -> {ok, pid()} | error.
-spec unhibernate_room(binary(), binary(), binary(), boolean()) -> {ok, pid()} | {error, notfound | db_failure | term()}.
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}),
case ?GEN_SERVER:call(Proc, {unhibernate, Room, Host, ResetHibernationTime}, 20000) of
{ok, _} = R -> R;
_ -> error
end;
?GEN_SERVER:call(Proc, {unhibernate, Room, Host, ResetHibernationTime}, 20000);
{ok, _} = R2 -> R2
end.
@@ -888,6 +885,8 @@ load_room(RMod, Host, ServerHost, Room, ResetHibernationTime) ->
case restore_room(ServerHost, Host, Room) of
error ->
{error, notfound};
{error, _} = Err ->
Err;
Opts0 ->
Mod = gen_mod:db_mod(ServerHost, mod_muc),
case proplists:get_bool(persistent, Opts0) of
+30 -4
View File
@@ -1289,6 +1289,8 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) ->
{error, _} ->
throw({error, "Unable to start room"})
end;
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "Room already exists"})
end.
@@ -1307,6 +1309,8 @@ destroy_room(Name1, Service1) ->
case get_room_pid_validate(Name1, Service1, <<"service">>) of
{room_not_found, _, _} ->
throw({error, "Room doesn't exists"});
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
{Pid, _, _} ->
mod_muc_room:destroy(Pid),
ok
@@ -1698,6 +1702,8 @@ change_room_option(Name, Service, OptionString, ValueString) ->
case get_room_pid_validate(Name, Service, <<"service">>) of
{room_not_found, _, _} ->
throw({error, "Room not found"});
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
{Pid, _, _} ->
{Option, Value} = format_room_option(OptionString, ValueString),
change_room_option(Pid, Option, Value)
@@ -1823,25 +1829,29 @@ parse_nodes(_, _) ->
throw({error, "Invalid 'subscribers' - unknown node name used"}).
-spec get_room_pid_validate(binary(), binary(), binary()) ->
{pid() | room_not_found, binary(), binary()}.
{pid() | room_not_found | db_failure, binary(), binary()}.
get_room_pid_validate(Name, Service, ServiceArg) ->
Name2 = validate_room(Name),
{ServerHost, Service2} = validate_muc2(Service, ServiceArg),
case mod_muc:unhibernate_room(ServerHost, Service2, Name2) of
error ->
{error, notfound} ->
{room_not_found, Name2, Service2};
{error, db_failure} ->
{db_failure, Name2, Service2};
{ok, Pid} ->
{Pid, Name2, Service2}
end.
%% @doc Get the Pid of an existing MUC room, or 'room_not_found'.
-spec get_room_pid(binary(), binary()) -> pid() | room_not_found | invalid_service | unknown_service.
-spec get_room_pid(binary(), binary()) -> pid() | room_not_found | db_failure | invalid_service | unknown_service.
get_room_pid(Name, Service) ->
try get_room_serverhost(Service) of
ServerHost ->
case mod_muc:unhibernate_room(ServerHost, Service, Name) of
error ->
{error, notfound} ->
room_not_found;
{error, db_failure} ->
db_failure;
{ok, Pid} ->
Pid
end
@@ -1954,6 +1964,8 @@ get_room_affiliations(Name, Service) ->
({{Uname, Domain, _Res}, Aff}) when is_atom(Aff)->
{Uname, Domain, Aff, <<>>}
end, Affiliations);
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "The room does not exist."})
end.
@@ -1975,6 +1987,8 @@ get_room_affiliations_v3(Name, Service) ->
Jid = makeencode(Uname, Domain),
{Jid, Aff, <<>>}
end, Affiliations);
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "The room does not exist."})
end.
@@ -1993,6 +2007,8 @@ get_room_history(Name, Service) ->
_ ->
throw({error, "Unable to fetch room state."})
end;
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "The room does not exist."})
end.
@@ -2012,6 +2028,8 @@ get_room_affiliation(Name, Service, JID) ->
{ok, StateData} = mod_muc_room:get_state(Pid),
UserJID = jid:decode(JID),
mod_muc_room:get_affiliation(UserJID, StateData);
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "The room does not exist."})
end.
@@ -2052,6 +2070,8 @@ set_room_affiliation(Name, Service, JID, AffiliationString) ->
{error, _} ->
throw({error, "Unable to perform change"})
end;
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "Room doesn't exists"})
end.
@@ -2084,6 +2104,8 @@ subscribe_room(User, Nick, Room, NodeList) ->
{error, Reason} ->
throw({error, binary_to_list(Reason)})
end;
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "The room does not exist"})
end
@@ -2129,6 +2151,8 @@ unsubscribe_room(User, Room) ->
{error, Reason} ->
throw({error, binary_to_list(Reason)})
end;
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "The room does not exist"})
end
@@ -2146,6 +2170,8 @@ get_subscribers(Name, Host) ->
{Pid, _, _} when is_pid(Pid) ->
{ok, JIDList} = mod_muc_room:get_subscribers(Pid),
[jid:encode(jid:remove_resource(J)) || J <- JIDList];
{db_failure, _Name, _Host} ->
throw({error, "Database error"});
_ ->
throw({error, "The room does not exist"})
end.
+3 -1
View File
@@ -76,9 +76,11 @@ store_room(_LServer, Host, Name, Opts, _) ->
mnesia:transaction(F).
restore_room(_LServer, Host, Name) ->
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
try mnesia:dirty_read(muc_room, {Name, Host}) of
[#muc_room{opts = Opts}] -> Opts;
_ -> error
catch
_:_ -> {error, db_failure}
end.
forget_room(_LServer, Host, Name) ->
+26
View File
@@ -3221,6 +3221,7 @@ process_item_change(Item, SD, UJID) ->
true ->
send_kickban_presence(UJID, JID, Reason, 321, none, SD),
maybe_send_affiliation(JID, none, SD),
unsubscribe_from_room(JID, SD),
SD1 = set_affiliation(JID, none, SD),
set_role(JID, none, SD1);
_ ->
@@ -3237,6 +3238,7 @@ process_item_change(Item, SD, UJID) ->
{JID, affiliation, outcast, Reason} ->
send_kickban_presence(UJID, JID, Reason, 301, outcast, SD),
maybe_send_affiliation(JID, outcast, SD),
unsubscribe_from_room(JID, SD),
{result, undefined, SD2} =
process_iq_mucsub(JID,
#iq{type = set,
@@ -3279,6 +3281,30 @@ process_item_change(Item, SD, UJID) ->
{error, xmpp:err_internal_server_error()}
end.
-spec unsubscribe_from_room(jid(), state()) -> ok | error.
unsubscribe_from_room(JID, SD) ->
case SD#state.config#config.members_only of
false ->
ok;
true ->
case mod_muc:unhibernate_room(SD#state.server_host, SD#state.host, SD#state.room) of
{error, _Reason0} ->
error;
{ok, Pid} ->
_UnsubPid =
spawn(fun() ->
case unsubscribe(Pid, JID) of
ok ->
ok;
{error, Reason} ->
?WARNING_MSG("Failed to automatically unsubscribe expelled member from room: ~ts",
[Reason]),
error
end
end)
end
end.
-spec find_changed_items(jid(), affiliation(), role(),
[muc_item()], binary(), state(), [admin_action()]) ->
{result, [admin_action()]}.
+4 -2
View File
@@ -220,10 +220,12 @@ restore_room(LServer, Host, Name) ->
Opts2 = lists:keystore(subscribers, 1, OptsD, {subscribers, SubData}),
mod_muc:opts_to_binary(Opts2);
_ ->
error
{error, db_failure}
end;
{selected, _} ->
error;
_ ->
error
{error, db_failure}
end.
forget_room(LServer, Host, Name) ->
+462
View File
@@ -0,0 +1,462 @@
%%%-------------------------------------------------------------------
%%% File : mod_providers.erl
%%% Author : Badlop <badlop@process-one.net>
%%% Purpose : Serve xmpp-provider-v2.json files as described by XMPP Providers
%%% Created : 7 August 2025 by Badlop <badlop@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 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.
%%%
%%%----------------------------------------------------------------------
%%--------------------------------------------------------------------
%%| Definitions
%% This module is based in mod_host_meta.erl
%% @format-begin
-module(mod_providers).
-author('badlop@process-one.net').
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, mod_options/1, depends/2]).
-export([mod_doc/0]).
-include("logger.hrl").
-include_lib("xmpp/include/xmpp.hrl").
-include("ejabberd_http.hrl").
-include("ejabberd_web_admin.hrl").
-include("translate.hrl").
%%--------------------------------------------------------------------
%%| gen_mod callbacks
start(_Host, _Opts) ->
report_hostmeta_listener(),
ok.
stop(_Host) ->
ok.
reload(_Host, _NewOpts, _OldOpts) ->
report_hostmeta_listener(),
ok.
depends(_Host, _Opts) ->
[].
%%--------------------------------------------------------------------
%%| HTTP handlers
process([],
#request{method = 'GET',
host = Host,
path = Path}) ->
case lists:last(Path) of
<<"xmpp-provider-v2.json">> ->
file_json(Host)
end;
process(_Path, _Request) ->
{404, [], "Not Found"}.
%%--------------------------------------------------------------------
%%| JSON
file_json(Host) ->
Content =
#{website => build_urls(Host, website),
alternativeJids => gen_mod:get_module_opt(Host, ?MODULE, alternativeJids),
busFactor => gen_mod:get_module_opt(Host, ?MODULE, busFactor),
organization => gen_mod:get_module_opt(Host, ?MODULE, organization),
passwordReset => get_password_url(Host),
serverTesting => gen_mod:get_module_opt(Host, ?MODULE, serverTesting),
maximumHttpFileUploadTotalSize => get_upload_size(Host),
maximumHttpFileUploadStorageTime => get_upload_time(Host),
maximumMessageArchiveManagementStorageTime =>
gen_mod:get_module_opt(Host, ?MODULE, maximumMessageArchiveManagementStorageTime),
professionalHosting => gen_mod:get_module_opt(Host, ?MODULE, professionalHosting),
freeOfCharge => gen_mod:get_module_opt(Host, ?MODULE, freeOfCharge),
legalNotice => build_urls(Host, legalNotice),
serverLocations => gen_mod:get_module_opt(Host, ?MODULE, serverLocations),
since => gen_mod:get_module_opt(Host, ?MODULE, since)},
{200,
[html,
{<<"Content-Type">>, <<"application/json">>},
{<<"Access-Control-Allow-Origin">>, <<"*">>}],
[misc:json_encode(Content)]}.
%%--------------------------------------------------------------------
%%| Upload Size
get_upload_size(Host) ->
case gen_mod:get_module_opt(Host, ?MODULE, maximumHttpFileUploadTotalSize) of
default_value ->
get_upload_size_mhuq(Host);
I when is_integer(I) ->
I
end.
get_upload_size_mhuq(Host) ->
case gen_mod:is_loaded(Host, mod_http_upload_quota) of
true ->
Access = gen_mod:get_module_opt(Host, mod_http_upload_quota, access_hard_quota),
Rules = ejabberd_shaper:read_shaper_rules(Access, Host),
get_upload_size_rules(Rules);
false ->
0
end.
get_upload_size_rules(Rules) ->
case lists:keysearch([{acl, all}], 2, Rules) of
{value, {Size, _}} ->
Size;
false ->
0
end.
%%--------------------------------------------------------------------
%%| Upload Time
get_upload_time(Host) ->
case gen_mod:get_module_opt(Host, ?MODULE, maximumHttpFileUploadStorageTime) of
default_value ->
get_upload_time_mhuq(Host);
I when is_integer(I) ->
I
end.
get_upload_time_mhuq(Host) ->
case gen_mod:is_loaded(Host, mod_http_upload_quota) of
true ->
case gen_mod:get_module_opt(Host, mod_http_upload_quota, max_days) of
infinity ->
0;
I when is_integer(I) ->
I
end;
false ->
0
end.
%%--------------------------------------------------------------------
%%| Password URL
get_password_url(Host) ->
build_urls(Host, get_password_url2(Host)).
get_password_url2(Host) ->
case gen_mod:get_module_opt(Host, ?MODULE, passwordReset) of
default_value ->
get_password_url3(Host);
U when is_binary(U) ->
U
end.
get_password_url3(Host) ->
case find_handler_port_path2(any, mod_register_web) of
[] ->
<<"">>;
[{ThisTls, Port, Path} | _] ->
Protocol =
case ThisTls of
false ->
<<"http">>;
true ->
<<"https">>
end,
<<Protocol/binary,
"://",
Host/binary,
":",
(integer_to_binary(Port))/binary,
"/",
(str:join(Path, <<"/">>))/binary,
"/">>
end.
%% TODO Ya hay otra funciona como esta
find_handler_port_path2(Tls, Module) ->
lists:filtermap(fun ({{Port, _, _},
ejabberd_http,
#{tls := ThisTls, request_handlers := Handlers}})
when (Tls == any) or (Tls == ThisTls) ->
case lists:keyfind(Module, 2, Handlers) of
false ->
false;
{Path, Module} ->
{true, {ThisTls, Port, Path}}
end;
(_) ->
false
end,
ets:tab2list(ejabberd_listener)).
%%--------------------------------------------------------------------
%%| Build URLs
build_urls(Host, Option) when is_atom(Option) ->
build_urls(Host, gen_mod:get_module_opt(Host, ?MODULE, Option));
build_urls(_Host, <<"">>) ->
#{};
build_urls(Host, Url) when not is_atom(Url) ->
Languages = gen_mod:get_module_opt(Host, ?MODULE, languages),
maps:from_list([{L, misc:expand_keyword(<<"@LANGUAGE_URL@">>, Url, L)}
|| L <- Languages]).
find_handler_port_path(Tls, Module) ->
lists:filtermap(fun ({{Port, _, _},
ejabberd_http,
#{tls := ThisTls, request_handlers := Handlers}})
when is_integer(Port) and ((Tls == any) or (Tls == ThisTls)) ->
case lists:keyfind(Module, 2, Handlers) of
false ->
false;
{Path, Module} ->
{true, {ThisTls, Port, Path}}
end;
(_) ->
false
end,
ets:tab2list(ejabberd_listener)).
report_hostmeta_listener() ->
case {find_handler_port_path(false, ?MODULE), find_handler_port_path(true, ?MODULE)} of
{[], []} ->
?CRITICAL_MSG("It seems you enabled ~p in 'modules' but forgot to "
"add it as a request_handler in an ejabberd_http "
"listener.",
[?MODULE]);
{[_ | _], _} ->
?WARNING_MSG("Apparently ~p is enabled in a request_handler in a "
"non-encrypted ejabberd_http listener. If this is "
"not desired, enable 'tls' in that "
"listener, or setup a proxy encryption mechanism.",
[?MODULE]);
{[], [_ | _]} ->
ok
end.
%%--------------------------------------------------------------------
%%| Options
mod_opt_type(languages) ->
econf:list(
econf:binary());
mod_opt_type(website) ->
econf:binary();
mod_opt_type(alternativeJids) ->
econf:list(
econf:domain(), [unique]);
mod_opt_type(busFactor) ->
econf:int();
mod_opt_type(organization) ->
econf:enum([company,
'commercial person',
'private person',
governmental,
'non-governmental']);
mod_opt_type(passwordReset) ->
econf:binary();
mod_opt_type(serverTesting) ->
econf:bool();
mod_opt_type(maximumHttpFileUploadTotalSize) ->
econf:int();
mod_opt_type(maximumHttpFileUploadStorageTime) ->
econf:int();
mod_opt_type(maximumMessageArchiveManagementStorageTime) ->
econf:int();
mod_opt_type(professionalHosting) ->
econf:bool();
mod_opt_type(freeOfCharge) ->
econf:bool();
mod_opt_type(legalNotice) ->
econf:binary();
mod_opt_type(serverLocations) ->
econf:list(
econf:binary());
mod_opt_type(since) ->
econf:binary().
mod_options(Host) ->
[{languages, [ejabberd_option:language(Host)]},
{website, <<"">>},
{alternativeJids, []},
{busFactor, -1},
{organization, ''},
{passwordReset, default_value},
{serverTesting, false},
{maximumHttpFileUploadTotalSize, default_value},
{maximumHttpFileUploadStorageTime, default_value},
{maximumMessageArchiveManagementStorageTime, 0},
{professionalHosting, false},
{freeOfCharge, false},
{legalNotice, <<"">>},
{serverLocations, []},
{since, <<"">>}].
%%--------------------------------------------------------------------
%%| Doc
mod_doc() ->
#{desc =>
[?T("This module serves JSON provider files API v2 as described by "
"https://providers.xmpp.net/provider-file-generator/[XMPP Providers]."),
"",
?T("It attempts to fill some properties gathering values automatically from your existing ejabberd configuration. Try enabling the module, check what values are displayed, and then customize using the options."),
"",
?T("To use this module, in addition to adding it to the 'modules' "
"section, you must also enable it in 'listen' -> 'ejabberd_http' -> "
"_`listen-options.md#request_handlers|request_handlers`_. "
"Notice you should set in _`listen.md#ejabberd_http|ejabberd_http`_ "
"the option _`listen-options.md#tls|tls`_ enabled.")],
note => "added in 25.08",
opts =>
[{languages,
#{value => "[string()]",
desc =>
?T("List of language codes that your pages are available. "
"Some options define URL where the keyword '@LANGUAGE_URL@' "
"will be replaced with each of those language codes. "
"The default value is a list with the language set in the "
"option _`language`_, for example: '[en]'.")}},
{website,
#{value => "string()",
desc =>
?T("Provider website. "
"The keyword '@LANGUAGE_URL@' is replaced with each language. "
"The default value is '\"\"'.")}},
{alternativeJids,
#{value => "[string()]",
desc =>
?T("List of JIDs (XMPP server domains) a provider offers for "
"registration other than its main JID. "
"The default value is '[]'.")}},
{busFactor,
#{value => "integer()",
desc =>
?T("Bus factor of the XMPP service (i.e., the minimum number of "
"team members that the service could not survive losing) or '-1' for n/a. "
"The default value is '-1'.")}},
{organization,
#{value => "string()",
desc =>
?T("Type of organization providing the XMPP service. "
"Allowed values are: 'company', '\"commercial person\"', '\"private person\"', "
"'governmental', '\"non-governmental\"' or '\"\"'. "
"The default value is '\"\"'.")}},
{passwordReset,
#{value => "string()",
desc =>
?T("Password reset web page (per language) used for an automatic password reset "
"(e.g., via email) or describing how to manually reset a password "
"(e.g., by contacting the provider). "
"The keyword '@LANGUAGE_URL@' is replaced with each language. "
"The default value is an URL built automatically "
"if _`mod_register_web`_ is configured as a 'request_handler', "
"or '\"\"' otherwise.")}},
{serverTesting,
#{value => "true | false",
desc =>
?T("Whether tests against the provider's server are allowed "
"(e.g., certificate checks and uptime monitoring). "
"The default value is 'false'.")}},
{maximumHttpFileUploadTotalSize,
#{value => "integer()",
desc =>
?T("Maximum size of all shared files in total per user (number in megabytes (MB), "
"'0' for no limit or '-1' for less than 1 MB). "
"Attention: MB is used instead of MiB (e.g., 104,857,600 bytes = 100 MiB ≈ 104 MB). "
"This property is not about the maximum size of each shared file, "
"which is already retrieved via XMPP. "
"The default value is the value of the shaper value "
"of option 'access_hard_quota' "
"from module _`mod_http_upload_quota`_, or '0' otherwise.")}},
{maximumHttpFileUploadStorageTime,
#{value => "integer()",
desc =>
?T("Maximum storage duration of each shared file "
"(number in days, '0' for no limit or '-1' for less than 1 day). "
"The default value is the same as option 'max_days' "
"from module _`mod_http_upload_quota`_, or '0' otherwise.")}},
{maximumMessageArchiveManagementStorageTime,
#{value => "integer()",
desc =>
?T("Maximum storage duration of each exchanged message "
"(number in days, '0' for no limit or '-1' for less than 1 day). "
"The default value is '0'.")}},
{professionalHosting,
#{value => "true | false",
desc =>
?T("Whether the XMPP server is hosted with good internet connection speed, "
"uninterruptible power supply, access protection and regular backups. "
"The default value is 'false'.")}},
{freeOfCharge,
#{value => "true | false",
desc =>
?T("Whether the XMPP service can be used for free. "
"The default value is 'false'.")}},
{legalNotice,
#{value => "string()",
desc =>
?T("Legal notice web page (per language). "
"The keyword '@LANGUAGE_URL@' is replaced with each language. "
"The default value is '\"\"'.")}},
{serverLocations,
#{value => "[string()]",
desc =>
?T("List of language codes of Server/Backup locations. "
"The default value is an empty list: '[]'.")}},
{since,
#{value => "string()",
desc =>
?T("Date since the XMPP service is available. "
"The default value is an empty string: '\"\"'.")}}],
example =>
["listen:",
" -",
" port: 443",
" module: ejabberd_http",
" tls: true",
" request_handlers:",
" /.well-known/xmpp-provider-v2.json: mod_providers",
"",
"modules:",
" mod_providers:",
" alternativeJids: [\"example1.com\", \"example2.com\"]",
" busFactor: 1",
" freeOfCharge: true",
" languages: [ag, ao, bg, en]",
" legalNotice: \"http://@HOST@/legal/@LANGUAGE_URL@/\"",
" maximumHttpFileUploadStorageTime: 0",
" maximumHttpFileUploadTotalSize: 0",
" maximumMessageArchiveManagementStorageTime: 0",
" organization: \"non-governmental\"",
" passwordReset: \"http://@HOST@/reset/@LANGUAGE_URL@/\"",
" professionalHosting: true",
" serverLocations: [ao, bg]",
" serverTesting: true",
" since: \"2025-12-31\"",
" website: \"http://@HOST@/website/@LANGUAGE_URL@/\""]}.
%%--------------------------------------------------------------------
%%| vim: set foldmethod=marker foldmarker=%%|,%%-:
+111
View File
@@ -0,0 +1,111 @@
%% Generated automatically
%% DO NOT EDIT: run `make options` instead
-module(mod_providers_opt).
-export([alternativeJids/1]).
-export([busFactor/1]).
-export([freeOfCharge/1]).
-export([languages/1]).
-export([legalNotice/1]).
-export([maximumHttpFileUploadStorageTime/1]).
-export([maximumHttpFileUploadTotalSize/1]).
-export([maximumMessageArchiveManagementStorageTime/1]).
-export([organization/1]).
-export([passwordReset/1]).
-export([professionalHosting/1]).
-export([serverLocations/1]).
-export([serverTesting/1]).
-export([since/1]).
-export([website/1]).
-spec alternativeJids(gen_mod:opts() | global | binary()) -> [binary()].
alternativeJids(Opts) when is_map(Opts) ->
gen_mod:get_opt(alternativeJids, Opts);
alternativeJids(Host) ->
gen_mod:get_module_opt(Host, mod_providers, alternativeJids).
-spec busFactor(gen_mod:opts() | global | binary()) -> integer().
busFactor(Opts) when is_map(Opts) ->
gen_mod:get_opt(busFactor, Opts);
busFactor(Host) ->
gen_mod:get_module_opt(Host, mod_providers, busFactor).
-spec freeOfCharge(gen_mod:opts() | global | binary()) -> boolean().
freeOfCharge(Opts) when is_map(Opts) ->
gen_mod:get_opt(freeOfCharge, Opts);
freeOfCharge(Host) ->
gen_mod:get_module_opt(Host, mod_providers, freeOfCharge).
-spec languages(gen_mod:opts() | global | binary()) -> [binary()].
languages(Opts) when is_map(Opts) ->
gen_mod:get_opt(languages, Opts);
languages(Host) ->
gen_mod:get_module_opt(Host, mod_providers, languages).
-spec legalNotice(gen_mod:opts() | global | binary()) -> binary().
legalNotice(Opts) when is_map(Opts) ->
gen_mod:get_opt(legalNotice, Opts);
legalNotice(Host) ->
gen_mod:get_module_opt(Host, mod_providers, legalNotice).
-spec maximumHttpFileUploadStorageTime(gen_mod:opts() | global | binary()) -> 'default_value' | integer().
maximumHttpFileUploadStorageTime(Opts) when is_map(Opts) ->
gen_mod:get_opt(maximumHttpFileUploadStorageTime, Opts);
maximumHttpFileUploadStorageTime(Host) ->
gen_mod:get_module_opt(Host, mod_providers, maximumHttpFileUploadStorageTime).
-spec maximumHttpFileUploadTotalSize(gen_mod:opts() | global | binary()) -> 'default_value' | integer().
maximumHttpFileUploadTotalSize(Opts) when is_map(Opts) ->
gen_mod:get_opt(maximumHttpFileUploadTotalSize, Opts);
maximumHttpFileUploadTotalSize(Host) ->
gen_mod:get_module_opt(Host, mod_providers, maximumHttpFileUploadTotalSize).
-spec maximumMessageArchiveManagementStorageTime(gen_mod:opts() | global | binary()) -> integer().
maximumMessageArchiveManagementStorageTime(Opts) when is_map(Opts) ->
gen_mod:get_opt(maximumMessageArchiveManagementStorageTime, Opts);
maximumMessageArchiveManagementStorageTime(Host) ->
gen_mod:get_module_opt(Host, mod_providers, maximumMessageArchiveManagementStorageTime).
-spec organization(gen_mod:opts() | global | binary()) -> '' | 'commercial person' | 'company' | 'governmental' | 'non-governmental' | 'private person'.
organization(Opts) when is_map(Opts) ->
gen_mod:get_opt(organization, Opts);
organization(Host) ->
gen_mod:get_module_opt(Host, mod_providers, organization).
-spec passwordReset(gen_mod:opts() | global | binary()) -> 'default_value' | binary().
passwordReset(Opts) when is_map(Opts) ->
gen_mod:get_opt(passwordReset, Opts);
passwordReset(Host) ->
gen_mod:get_module_opt(Host, mod_providers, passwordReset).
-spec professionalHosting(gen_mod:opts() | global | binary()) -> boolean().
professionalHosting(Opts) when is_map(Opts) ->
gen_mod:get_opt(professionalHosting, Opts);
professionalHosting(Host) ->
gen_mod:get_module_opt(Host, mod_providers, professionalHosting).
-spec serverLocations(gen_mod:opts() | global | binary()) -> [binary()].
serverLocations(Opts) when is_map(Opts) ->
gen_mod:get_opt(serverLocations, Opts);
serverLocations(Host) ->
gen_mod:get_module_opt(Host, mod_providers, serverLocations).
-spec serverTesting(gen_mod:opts() | global | binary()) -> boolean().
serverTesting(Opts) when is_map(Opts) ->
gen_mod:get_opt(serverTesting, Opts);
serverTesting(Host) ->
gen_mod:get_module_opt(Host, mod_providers, serverTesting).
-spec since(gen_mod:opts() | global | binary()) -> binary().
since(Opts) when is_map(Opts) ->
gen_mod:get_opt(since, Opts);
since(Host) ->
gen_mod:get_module_opt(Host, mod_providers, since).
-spec website(gen_mod:opts() | global | binary()) -> binary().
website(Opts) when is_map(Opts) ->
gen_mod:get_opt(website, Opts);
website(Host) ->
gen_mod:get_module_opt(Host, mod_providers, website).
-5
View File
@@ -420,11 +420,6 @@ send_welcome_message(JID) ->
to = JID,
type = chat,
subject = xmpp:mk_text(Subj),
body = xmpp:mk_text(<<Subj/binary, "\n\n", Body/binary>>)}),
ejabberd_router:route(
#message{from = jid:make(Host),
to = JID,
subject = xmpp:mk_text(Subj),
body = xmpp:mk_text(Body)})
end.
+22 -8
View File
@@ -27,7 +27,7 @@
-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]).
-export([mod_doc/0]).
%% Hooks
-export([c2s_inline_features/2, c2s_handle_sasl2_inline/1,
-export([c2s_inline_features/3, c2s_handle_sasl2_inline/1,
c2s_handle_sasl2_task_next/4, c2s_handle_sasl2_task_data/3]).
-include_lib("xmpp/include/xmpp.hrl").
@@ -76,11 +76,23 @@ mod_doc() ->
" - sha256",
" - sha512"]}.
c2s_inline_features({Sasl, Bind, Extra}, Host) ->
Methods = lists:map(
fun(sha256) -> #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-256">>};
(sha512) -> #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-512">>}
end, mod_scram_upgrade_opt:offered_upgrades(Host)),
c2s_inline_features({Sasl, Bind, Extra}, Host, State) ->
KnowTypes = case State of
#{sasl2_password_fun := Fun} ->
case Fun(<<>>) of
{Pass, _} -> lists:filtermap(
fun(#scram{hash = sha256}) -> {true, sha256};
(#scram{hash = sha512}) -> {true, sha512};
(_) -> false
end, Pass);
_ -> []
end;
_ -> []
end,
Methods = lists:filtermap(
fun(sha256) -> {true, #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-256">>}};
(sha512) -> {true, #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-512">>}}
end, mod_scram_upgrade_opt:offered_upgrades(Host) -- KnowTypes),
{Sasl, Bind, Methods ++ Extra}.
c2s_handle_sasl2_inline({State, Els, _Results} = Acc) ->
@@ -108,8 +120,10 @@ c2s_handle_sasl2_task_data({_, #{user := User, server := Server,
StoredKey = scram:stored_key(Algo, scram:client_key(Algo, SaltedPassword)),
ServerKey = scram:server_key(Algo, SaltedPassword),
ejabberd_auth:set_password_instance(User, Server,
#scram{hash = Algo, iterationcount = Iter, salt = Salt,
serverkey = ServerKey, storedkey = StoredKey}),
#scram{hash = Algo, iterationcount = Iter,
salt = base64:encode(Salt),
serverkey = base64:encode(ServerKey),
storedkey = base64:encode(StoredKey)}),
State2 = maps:remove(scram_upgrade, State),
InlineEls2 = lists:keydelete(sasl_upgrade, 1, InlineEls),
{State3, NewEls, Results} = ejabberd_c2s:handle_sasl2_inline(InlineEls2, State2),
+2 -2
View File
@@ -33,7 +33,7 @@
c2s_authenticated_packet/2, c2s_unauthenticated_packet/2,
c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2,
c2s_handle_send/3, c2s_handle_info/2, c2s_handle_cast/2,
c2s_handle_call/3, c2s_handle_recv/3, c2s_inline_features/2,
c2s_handle_call/3, c2s_handle_recv/3, c2s_inline_features/3,
c2s_handle_sasl2_inline/1, c2s_handle_sasl2_inline_post/3,
c2s_handle_bind2_inline/1]).
%% adjust pending session timeout / access queue
@@ -122,7 +122,7 @@ c2s_stream_features(Acc, Host) ->
Acc
end.
c2s_inline_features({Sasl, Bind, Extra} = Acc, Host) ->
c2s_inline_features({Sasl, Bind, Extra} = Acc, Host, _State) ->
case gen_mod:is_loaded(Host, ?MODULE) of
true ->
{[#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Sasl],
+2 -12
View File
@@ -87,8 +87,8 @@ convert_dir(Path, Host, Type) ->
case eval_file(FilePath) of
{ok, Data} ->
Name = iolist_to_binary(filename:rootname(File)),
convert_data(url_decode(Host), Type,
url_decode(Name), Data);
convert_data(misc:uri_decode(Host), Type,
misc:uri_decode(Name), Data);
Err ->
Err
end
@@ -410,16 +410,6 @@ convert_privacy_item({_, Item}) ->
match_presence_in = MatchPresIn,
match_presence_out = MatchPresOut}.
url_decode(Encoded) ->
url_decode(Encoded, <<>>).
url_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) ->
Hex = list_to_integer([Hi, Lo], 16),
url_decode(Tail, <<Acc/binary, Hex>>);
url_decode(<<H, Tail/binary>>, Acc) ->
url_decode(Tail, <<Acc/binary, H>>);
url_decode(<<>>, Acc) ->
Acc.
decode_pubsub_host(Host) ->
try jid:decode(Host) of
#jid{luser = <<>>, lserver = LServer} -> LServer;
+53 -48
View File
@@ -480,8 +480,11 @@ encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
decode(<<$<, _/binary>> = Data, _J1, _J2) ->
fxml_stream:parse_element(Data);
decode(<<1:8, Rest/binary>>, J1, J2) ->
{El, _} = decode(Rest, <<"jabber:client">>, J1, J2),
El.
try decode(Rest, <<"jabber:client">>, J1, J2, false) of
{El, _} -> El
catch throw:loop_detected ->
{error, {loop_detected, <<"Compressed data corrupted">>}}
end.
decode_string(Data) ->
case Data of
@@ -489,35 +492,37 @@ decode_string(Data) ->
{Str, Rest};
<<1:2, L1:6, 0:2, L2:6, Rest/binary>> ->
L = L2*64 + L1,
<<Str:L/binary, Rest2/binary>> = Rest,
<<Str:L/binary, Rest2/binary>> = Rest,
{Str, Rest2};
<<1:2, L1:6, 1:2, L2:6, L3:8, Rest/binary>> ->
L = (L3*64 + L2)*64 + L1,
<<Str:L/binary, Rest2/binary>> = Rest,
<<Str:L/binary, Rest2/binary>> = Rest,
{Str, Rest2}
end.
decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2) ->
decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2, _) ->
{Text, Rest2} = decode_string(Rest),
{{xmlcdata, Text}, Rest2};
decode_child(<<2:8, Rest/binary>>, PNs, J1, J2) ->
decode_child(<<2:8, Rest/binary>>, PNs, J1, J2, _) ->
{Name, Rest2} = decode_string(Rest),
{Attrs, Rest3} = decode_attrs(Rest2),
{Children, Rest4} = decode_children(Rest3, PNs, J1, J2),
{{xmlel, Name, Attrs, Children}, Rest4};
decode_child(<<3:8, Rest/binary>>, PNs, J1, J2) ->
decode_child(<<3:8, Rest/binary>>, PNs, J1, J2, _) ->
{Ns, Rest2} = decode_string(Rest),
{Name, Rest3} = decode_string(Rest2),
{Attrs, Rest4} = decode_attrs(Rest3),
{Children, Rest5} = decode_children(Rest4, Ns, J1, J2),
{{xmlel, Name, add_ns(PNs, Ns, Attrs), Children}, Rest5};
decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2) ->
decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2, _) ->
{stop, Rest};
decode_child(Other, PNs, J1, J2) ->
decode(Other, PNs, J1, J2).
decode_child(_Other, _PNs, _J1, _J2, true) ->
throw(loop_detected);
decode_child(Other, PNs, J1, J2, _) ->
decode(Other, PNs, J1, J2, true).
decode_children(Data, PNs, J1, J2) ->
prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2) end, Data).
prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2, false) end, Data).
decode_attr(<<1:8, Rest/binary>>) ->
{Name, Rest2} = decode_string(Rest),
@@ -545,7 +550,7 @@ add_ns(Ns, Ns, Attrs) ->
add_ns(_, Ns, Attrs) ->
[{<<"xmlns">>, Ns} | Attrs].
decode(<<5:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<5:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"eu.siacs.conversations.axolotl">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -563,12 +568,12 @@ decode(<<5:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"key">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<12:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<12:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"eu.siacs.conversations.axolotl">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"encrypted">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<13:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<13:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"eu.siacs.conversations.axolotl">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -581,17 +586,17 @@ decode(<<13:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"header">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<14:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<14:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"eu.siacs.conversations.axolotl">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"iv">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<15:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<15:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"eu.siacs.conversations.axolotl">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"payload">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<6:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<6:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"jabber:client">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -636,7 +641,7 @@ decode(<<6:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"message">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<8:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<8:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"jabber:client">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = prefix_map(fun (<<9:8, Rest5/binary>>) ->
@@ -650,25 +655,25 @@ decode(<<8:8, Rest/binary>>, PNs, J1, J2) ->
32,104,116,116,112,115,58,47,47,99,111,110,118,101,114,115,
97,116,105,111,110,115,46,105,109,47,111,109,101,109,111>>}, Rest5};
(Other) ->
decode_child(Other, Ns, J1, J2)
decode_child(Other, Ns, J1, J2, false)
end, Rest2),
{{xmlel, <<"body">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<31:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<31:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"jabber:client">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"subject">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<32:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<32:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"jabber:client">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"thread">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<7:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<7:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:hints">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"store">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<10:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<10:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:sid:0">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -681,7 +686,7 @@ decode(<<10:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"origin-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<22:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<22:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:sid:0">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -697,12 +702,12 @@ decode(<<22:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"stanza-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<11:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<11:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:chat-markers:0">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"markable">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<20:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<20:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:chat-markers:0">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -724,7 +729,7 @@ decode(<<20:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"displayed">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<24:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<24:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:chat-markers:0">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -737,7 +742,7 @@ decode(<<24:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<16:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<16:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:eme:0">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -757,7 +762,7 @@ decode(<<16:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"encryption">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<17:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<17:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:delay">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -775,7 +780,7 @@ decode(<<17:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"delay">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<18:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<18:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/address">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -796,12 +801,12 @@ decode(<<18:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"address">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<19:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<19:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/address">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"addresses">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<21:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<21:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:mam:tmp">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -817,12 +822,12 @@ decode(<<21:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"archived">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<23:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<23:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:receipts">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"request">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<25:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<25:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:receipts">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -835,17 +840,17 @@ decode(<<25:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<26:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<26:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/chatstates">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"active">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<39:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<39:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/chatstates">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"composing">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<27:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<27:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/muc#user">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -861,17 +866,17 @@ decode(<<27:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"invite">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<28:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<28:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/muc#user">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"reason">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<29:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<29:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/muc#user">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<30:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<30:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"jabber:x:conference">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -886,12 +891,12 @@ decode(<<30:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<33:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<33:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/pubsub#event">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"event">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<34:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<34:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/pubsub#event">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -904,7 +909,7 @@ decode(<<34:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"item">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<35:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<35:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"http://jabber.org/protocol/pubsub#event">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -919,7 +924,7 @@ decode(<<35:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"items">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<36:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<36:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"p1:push:custom">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -935,12 +940,12 @@ decode(<<36:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<37:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<37:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"p1:pushed">>,
{Attrs, Rest2} = decode_attrs(Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(<<38:8, Rest/binary>>, PNs, J1, J2) ->
decode(<<38:8, Rest/binary>>, PNs, J1, J2, _) ->
Ns = <<"urn:xmpp:message-correct:0">>,
{Attrs, Rest2} = prefix_map(fun
(<<3:8, Rest3/binary>>) ->
@@ -953,6 +958,6 @@ decode(<<38:8, Rest/binary>>, PNs, J1, J2) ->
end, Rest),
{Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
{{xmlel, <<"replace">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
decode(Other, PNs, J1, J2) ->
decode_child(Other, PNs, J1, J2).
decode(Other, PNs, J1, J2, Loop) ->
decode_child(Other, PNs, J1, J2, Loop).
-2
View File
@@ -961,8 +961,6 @@ presence_broadcast(Config) ->
sub_els = [#disco_info{node = Node}]} = recv_iq(Config),
#message{type = chat,
subject = [#text{lang = <<"en">>,data = <<"Welcome!">>}]} = recv_message(Config),
#message{type = normal,
subject = [#text{lang = <<"en">>,data = <<"Welcome!">>}]} = recv_message(Config),
#presence{from = JID, to = JID} = recv_presence(Config),
send(Config, #iq{type = result, id = IQ#iq.id,
to = JID, sub_els = [Info]}),
+4 -4
View File
@@ -70,8 +70,8 @@ termcap_vsn='1.3.1'
expat_vsn='2.7.1'
zlib_vsn='1.3.1'
yaml_vsn='0.2.5'
ssl_vsn='3.5.1'
otp_vsn='27.3.4.1'
ssl_vsn='3.5.2'
otp_vsn='27.3.4.2'
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'
@@ -629,8 +629,8 @@ build_deps()
info "Building Linux-PAM $pam_vsn for $arch ..."
cd "$target_src_dir/$pam_dir"
$configure --prefix="$prefix" --includedir="$prefix/include/security" \
--enable-static --disable-shared --disable-doc --disable-examples \
--enable-db=no \
--enable-static --disable-shared --disable-logind --disable-doc \
--disable-examples --enable-db=no \
CFLAGS="$CFLAGS -O3 -fPIC -Wno-error=implicit-function-declaration"
make
make install
+20 -15
View File
@@ -93,41 +93,46 @@ gen_decode(Dev, Data, VerId) ->
io:format(Dev, "decode(<<$<, _/binary>> = Data, _J1, _J2) ->~n"
" fxml_stream:parse_element(Data);~n"
"decode(<<~s, Rest/binary>>, J1, J2) ->~n"
" {El, _} = decode(Rest, <<\"jabber:client\">>, J1, J2),~n"
" El.~n~n", [VerId]),
" try decode(Rest, <<\"jabber:client\">>, J1, J2, false) of~n"
" {El, _} -> El~n"
" catch throw:loop_detected ->~n"
" {error, {loop_detected, <<\"Compressed data corrupted\">>}}~n"
" end.~n~n", [VerId]),
io:format(Dev, "decode_string(Data) ->~n"
" case Data of~n"
" <<0:2, L:6, Str:L/binary, Rest/binary>> ->~n"
" {Str, Rest};~n"
" <<1:2, L1:6, 0:2, L2:6, Rest/binary>> ->~n"
" L = L2*64 + L1,~n"
" <<Str:L/binary, Rest2/binary>> = Rest,~n"
" <<Str:L/binary, Rest2/binary>> = Rest,~n"
" {Str, Rest2};~n"
" <<1:2, L1:6, 1:2, L2:6, L3:8, Rest/binary>> ->~n"
" L = (L3*64 + L2)*64 + L1,~n"
" <<Str:L/binary, Rest2/binary>> = Rest,~n"
" <<Str:L/binary, Rest2/binary>> = Rest,~n"
" {Str, Rest2}~n"
" end.~n~n", []),
io:format(Dev, "decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2) ->~n"
io:format(Dev, "decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2, _) ->~n"
" {Text, Rest2} = decode_string(Rest),~n"
" {{xmlcdata, Text}, Rest2};~n", []),
io:format(Dev, "decode_child(<<2:8, Rest/binary>>, PNs, J1, J2) ->~n"
io:format(Dev, "decode_child(<<2:8, Rest/binary>>, PNs, J1, J2, _) ->~n"
" {Name, Rest2} = decode_string(Rest),~n"
" {Attrs, Rest3} = decode_attrs(Rest2),~n"
" {Children, Rest4} = decode_children(Rest3, PNs, J1, J2),~n"
" {{xmlel, Name, Attrs, Children}, Rest4};~n", []),
io:format(Dev, "decode_child(<<3:8, Rest/binary>>, PNs, J1, J2) ->~n"
io:format(Dev, "decode_child(<<3:8, Rest/binary>>, PNs, J1, J2, _) ->~n"
" {Ns, Rest2} = decode_string(Rest),~n"
" {Name, Rest3} = decode_string(Rest2),~n"
" {Attrs, Rest4} = decode_attrs(Rest3),~n"
" {Children, Rest5} = decode_children(Rest4, Ns, J1, J2),~n"
" {{xmlel, Name, add_ns(PNs, Ns, Attrs), Children}, Rest5};~n", []),
io:format(Dev, "decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2) ->~n"
io:format(Dev, "decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2, _) ->~n"
" {stop, Rest};~n", []),
io:format(Dev, "decode_child(Other, PNs, J1, J2) ->~n"
" decode(Other, PNs, J1, J2).~n~n", []),
io:format(Dev, "decode_child(_Other, _PNs, _J1, _J2, true) ->~n"
" throw(loop_detected);~n", []),
io:format(Dev, "decode_child(Other, PNs, J1, J2, _) ->~n"
" decode(Other, PNs, J1, J2, true).~n~n", []),
io:format(Dev, "decode_children(Data, PNs, J1, J2) ->~n"
" prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2) end, Data).~n~n", []),
" prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2, false) end, Data).~n~n", []),
io:format(Dev, "decode_attr(<<1:8, Rest/binary>>) ->~n"
" {Name, Rest2} = decode_string(Rest),~n"
" {Val, Rest3} = decode_string(Rest2),~n"
@@ -153,7 +158,7 @@ gen_decode(Dev, Data, VerId) ->
fun({Ns, Els}) ->
lists:foreach(
fun({Name, Id, Attrs, Text}) ->
io:format(Dev, "decode(<<~s, Rest/binary>>, PNs, J1, J2) ->~n"
io:format(Dev, "decode(<<~s, Rest/binary>>, PNs, J1, J2, _) ->~n"
" Ns = ~p,~n", [Id, Ns]),
case Attrs of
[] ->
@@ -209,14 +214,14 @@ gen_decode(Dev, Data, VerId) ->
end, Text),
io:format(Dev, " (Other) ->~n"
" decode_child(Other, Ns, J1, J2)~n"
" decode_child(Other, Ns, J1, J2, false)~n"
" end, Rest2),~n", [])
end,
io:format(Dev, " {{xmlel, ~p, add_ns(PNs, Ns, Attrs), Children}, Rest6};~n", [Name])
end, Els)
end, Data),
io:format(Dev, "decode(Other, PNs, J1, J2) ->~n"
" decode_child(Other, PNs, J1, J2).~n~n", []).
io:format(Dev, "decode(Other, PNs, J1, J2, Loop) ->~n"
" decode_child(Other, PNs, J1, J2, Loop).~n~n", []).
gen_encode(Dev, Data, VerId) ->