mirror of
https://github.com/processone/ejabberd
synced 2025-10-06 03:50:15 +02:00
Merge branch 'master' of github.com:processone/ejabberd
This commit is contained in:
commit
92b2bb7532
33 changed files with 1319 additions and 266 deletions
283
CONTAINER.md
283
CONTAINER.md
|
@ -240,6 +240,7 @@ ejabberd reads `EJABBERD_MACRO_*` environment variables
|
|||
and uses them to define the `*`
|
||||
[macros](https://docs.ejabberd.im/admin/configuration/file-format/#macros-in-configuration-file),
|
||||
overwriting the corresponding macro definition if it was set in the configuration file.
|
||||
This is supported since ejabberd 24.12.
|
||||
|
||||
For example, if you configure this in `ejabberd.yml`:
|
||||
|
||||
|
@ -273,12 +274,12 @@ For this you can either:
|
|||
|
||||
Example to connect a local `ejabberdctl` to a containerized ejabberd:
|
||||
1. When creating the container, export port 5210, and set `ERLANG_COOKIE`:
|
||||
```sh
|
||||
docker run --name ejabberd -it \
|
||||
-e ERLANG_COOKIE=`cat $HOME/.erlang.cookie` \
|
||||
-p 5210:5210 -p 5222:5222 \
|
||||
ghcr.io/processone/ejabberd
|
||||
```
|
||||
```sh
|
||||
docker run --name ejabberd -it \
|
||||
-e ERLANG_COOKIE=`cat $HOME/.erlang.cookie` \
|
||||
-p 5210:5210 -p 5222:5222 \
|
||||
ghcr.io/processone/ejabberd
|
||||
```
|
||||
2. Set `ERL_DIST_PORT=5210` in ejabberdctl.cfg of container and local ejabberd
|
||||
3. Restart the container
|
||||
4. Now use `ejabberdctl` in your local ejabberd deployment
|
||||
|
@ -320,7 +321,7 @@ docker buildx build \
|
|||
|
||||
### Podman build
|
||||
|
||||
It's also possible to use podman instead of docker, just notice:
|
||||
To build the image using podman instead of docker, notice:
|
||||
- `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile
|
||||
- It mentions that `healthcheck` is not supported by the Open Container Initiative image format
|
||||
- to start with command `live`, you may want to add environment variable `EJABBERD_BYPASS_WARNINGS=true`
|
||||
|
@ -372,7 +373,9 @@ Composer Examples
|
|||
### Minimal Example
|
||||
|
||||
This is the barely minimal file to get a usable ejabberd.
|
||||
Store it as `docker-compose.yml`:
|
||||
|
||||
If using Docker, write this `docker-compose.yml` file
|
||||
and start it with `docker-compose up`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
|
@ -386,11 +389,34 @@ services:
|
|||
- "5443:5443"
|
||||
```
|
||||
|
||||
Create and start the container with the command:
|
||||
```bash
|
||||
docker-compose up
|
||||
If using Podman, write this `minimal.yml` file
|
||||
and start it with `podman kube play minimal.yml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
|
||||
kind: Pod
|
||||
|
||||
metadata:
|
||||
name: ejabberd
|
||||
|
||||
spec:
|
||||
containers:
|
||||
|
||||
- name: ejabberd
|
||||
image: ghcr.io/processone/ejabberd
|
||||
ports:
|
||||
- containerPort: 5222
|
||||
hostPort: 5222
|
||||
- containerPort: 5269
|
||||
hostPort: 5269
|
||||
- containerPort: 5280
|
||||
hostPort: 5280
|
||||
- containerPort: 5443
|
||||
hostPort: 5443
|
||||
```
|
||||
|
||||
|
||||
### Customized Example
|
||||
|
||||
This example shows the usage of several customizations:
|
||||
|
@ -420,7 +446,9 @@ mkdir database
|
|||
sudo chown 9000:9000 database
|
||||
```
|
||||
|
||||
Now write this `docker-compose.yml` file:
|
||||
If using Docker, write this `docker-compose.yml` file
|
||||
and start it with `docker-compose up`:
|
||||
|
||||
```yaml
|
||||
version: '3.7'
|
||||
|
||||
|
@ -444,6 +472,56 @@ services:
|
|||
- ./database:/opt/ejabberd/database
|
||||
```
|
||||
|
||||
If using Podman, write this `custom.yml` file
|
||||
and start it with `podman kube play custom.yml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
|
||||
kind: Pod
|
||||
|
||||
metadata:
|
||||
name: ejabberd
|
||||
|
||||
spec:
|
||||
containers:
|
||||
|
||||
- name: ejabberd
|
||||
image: ghcr.io/processone/ejabberd
|
||||
env:
|
||||
- name: CTL_ON_CREATE
|
||||
value: register admin example.com asd
|
||||
- name: CTL_ON_START
|
||||
value: registered_users example.com ;
|
||||
status
|
||||
ports:
|
||||
- containerPort: 5222
|
||||
hostPort: 5222
|
||||
- containerPort: 5269
|
||||
hostPort: 5269
|
||||
- containerPort: 5280
|
||||
hostPort: 5280
|
||||
- containerPort: 5443
|
||||
hostPort: 5443
|
||||
volumeMounts:
|
||||
- mountPath: /opt/ejabberd/conf/ejabberd.yml
|
||||
name: config
|
||||
readOnly: true
|
||||
- mountPath: /opt/ejabberd/database
|
||||
name: db
|
||||
|
||||
volumes:
|
||||
- name: config
|
||||
hostPath:
|
||||
path: ./ejabberd.yml
|
||||
type: File
|
||||
- name: db
|
||||
hostPath:
|
||||
path: ./database
|
||||
type: DirectoryOrCreate
|
||||
```
|
||||
|
||||
|
||||
### Clustering Example
|
||||
|
||||
In this example, the main container is created first.
|
||||
|
@ -458,6 +536,9 @@ and it should exist in the second node after join.
|
|||
Notice that in this example the main container does not have access
|
||||
to the exterior; the replica exports the ports and can be accessed.
|
||||
|
||||
If using Docker, write this `docker-compose.yml` file
|
||||
and start it with `docker-compose up`:
|
||||
|
||||
```yaml
|
||||
version: '3.7'
|
||||
|
||||
|
@ -477,15 +558,183 @@ services:
|
|||
depends_on:
|
||||
main:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "5222:5222"
|
||||
- "5269:5269"
|
||||
- "5280:5280"
|
||||
- "5443:5443"
|
||||
environment:
|
||||
- ERLANG_NODE_ARG=ejabberd@replica
|
||||
- ERLANG_COOKIE=dummycookie123
|
||||
- CTL_ON_CREATE=join_cluster ejabberd@main
|
||||
- CTL_ON_START=registered_users localhost ;
|
||||
status
|
||||
ports:
|
||||
- "5222:5222"
|
||||
- "5269:5269"
|
||||
- "5280:5280"
|
||||
- "5443:5443"
|
||||
```
|
||||
|
||||
If using Podman, write this `cluster.yml` file
|
||||
and start it with `podman kube play cluster.yml`.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
|
||||
kind: Pod
|
||||
|
||||
metadata:
|
||||
name: cluster
|
||||
|
||||
spec:
|
||||
containers:
|
||||
|
||||
- name: first
|
||||
image: ghcr.io/processone/ejabberd
|
||||
env:
|
||||
- name: ERLANG_NODE_ARG
|
||||
value: main@cluster
|
||||
- name: ERLANG_COOKIE
|
||||
value: dummycookie123
|
||||
- name: CTL_ON_CREATE
|
||||
value: register admin localhost asd
|
||||
- name: CTL_ON_START
|
||||
value: stats registeredusers ;
|
||||
status
|
||||
- name: EJABBERD_MACRO_PORT_C2S
|
||||
value: 6222
|
||||
- name: EJABBERD_MACRO_PORT_C2S_TLS
|
||||
value: 6223
|
||||
- name: EJABBERD_MACRO_PORT_S2S
|
||||
value: 6269
|
||||
- name: EJABBERD_MACRO_PORT_HTTP_TLS
|
||||
value: 6443
|
||||
- name: EJABBERD_MACRO_PORT_HTTP
|
||||
value: 6280
|
||||
- name: EJABBERD_MACRO_PORT_MQTT
|
||||
value: 6883
|
||||
- name: EJABBERD_MACRO_PORT_PROXY65
|
||||
value: 6777
|
||||
volumeMounts:
|
||||
- mountPath: /opt/ejabberd/conf/ejabberd.yml
|
||||
name: config
|
||||
readOnly: true
|
||||
|
||||
- name: second
|
||||
image: ghcr.io/processone/ejabberd
|
||||
env:
|
||||
- name: ERLANG_NODE_ARG
|
||||
value: replica@cluster
|
||||
- name: ERLANG_COOKIE
|
||||
value: dummycookie123
|
||||
- name: CTL_ON_CREATE
|
||||
value: join_cluster main@cluster ;
|
||||
started ;
|
||||
list_cluster
|
||||
- name: CTL_ON_START
|
||||
value: stats registeredusers ;
|
||||
check_password admin localhost asd ;
|
||||
status
|
||||
ports:
|
||||
- containerPort: 5222
|
||||
hostPort: 5222
|
||||
- containerPort: 5280
|
||||
hostPort: 5280
|
||||
volumeMounts:
|
||||
- mountPath: /opt/ejabberd/conf/ejabberd.yml
|
||||
name: config
|
||||
readOnly: true
|
||||
|
||||
volumes:
|
||||
- name: config
|
||||
hostPath:
|
||||
path: ./conf/ejabberd.yml
|
||||
type: File
|
||||
|
||||
```
|
||||
|
||||
Your configuration file should use those macros to allow each ejabberd node
|
||||
use different listening port numbers:
|
||||
|
||||
```diff
|
||||
diff --git a/ejabberd.yml.example b/ejabberd.yml.example
|
||||
index 39e423a64..6e875b48f 100644
|
||||
--- a/ejabberd.yml.example
|
||||
+++ b/ejabberd.yml.example
|
||||
@@ -24,9 +24,19 @@ loglevel: info
|
||||
# - /etc/letsencrypt/live/domain.tld/fullchain.pem
|
||||
# - /etc/letsencrypt/live/domain.tld/privkey.pem
|
||||
|
||||
+define_macro:
|
||||
+ PORT_C2S: 5222
|
||||
+ PORT_C2S_TLS: 5223
|
||||
+ PORT_S2S: 5269
|
||||
+ PORT_HTTP_TLS: 5443
|
||||
+ PORT_HTTP: 5280
|
||||
+ PORT_STUN: 5478
|
||||
+ PORT_MQTT: 1883
|
||||
+ PORT_PROXY65: 7777
|
||||
+
|
||||
listen:
|
||||
-
|
||||
- port: 5222
|
||||
+ port: PORT_C2S
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
max_stanza_size: 262144
|
||||
@@ -34,7 +44,7 @@ listen:
|
||||
access: c2s
|
||||
starttls_required: true
|
||||
-
|
||||
- port: 5223
|
||||
+ port: PORT_C2S_TLS
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
max_stanza_size: 262144
|
||||
@@ -42,13 +52,13 @@ listen:
|
||||
access: c2s
|
||||
tls: true
|
||||
-
|
||||
- port: 5269
|
||||
+ port: PORT_S2S
|
||||
ip: "::"
|
||||
module: ejabberd_s2s_in
|
||||
max_stanza_size: 524288
|
||||
shaper: s2s_shaper
|
||||
-
|
||||
- port: 5443
|
||||
+ port: PORT_HTTP_TLS
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
tls: true
|
||||
@@ -60,14 +70,14 @@ listen:
|
||||
/upload: mod_http_upload
|
||||
/ws: ejabberd_http_ws
|
||||
-
|
||||
- port: 5280
|
||||
+ port: PORT_HTTP
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
/admin: ejabberd_web_admin
|
||||
/.well-known/acme-challenge: ejabberd_acme
|
||||
-
|
||||
- port: 5478
|
||||
+ port: PORT_STUN
|
||||
ip: "::"
|
||||
transport: udp
|
||||
module: ejabberd_stun
|
||||
@@ -77,7 +87,7 @@ listen:
|
||||
## The server's public IPv6 address:
|
||||
# turn_ipv6_address: "2001:db8::3"
|
||||
-
|
||||
- port: 1883
|
||||
+ port: PORT_MQTT
|
||||
ip: "::"
|
||||
module: mod_mqtt
|
||||
backlog: 1000
|
||||
@@ -207,6 +217,7 @@ modules:
|
||||
mod_proxy65:
|
||||
access: local
|
||||
max_connections: 5
|
||||
+ port: PORT_PROXY65
|
||||
mod_pubsub:
|
||||
access_createnode: pubsub_createnode
|
||||
plugins:
|
||||
```
|
||||
|
|
|
@ -87,6 +87,11 @@ or some other method from [ProcessOne Contact][p1contact].
|
|||
For commercial offering and support, including [ejabberd Business Edition][p1home]
|
||||
and [Fluux (ejabberd in the Cloud)][fluux], please check [ProcessOne ejabberd page][p1home].
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
For information on how to report security vulnerabilities, please refer to the [SECURITY.md](SECURITY.md) file. It contains guidelines on how to report vulnerabilities privately and securely, ensuring that any issues are addressed in a timely and confidential manner.
|
||||
|
||||
Community
|
||||
---------
|
||||
|
||||
|
|
45
SECURITY.md
Normal file
45
SECURITY.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We recommend that all users always use the latest version of ejabberd.
|
||||
|
||||
To ensure the best experience and security, upgrade to the latest version available on [this repo](https://github.com/processone/ejabberd).
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
### Private Reporting
|
||||
|
||||
**Preferred Method**: Use GitHub's private vulnerability reporting system by clicking the "Report a Vulnerability" button in the [Security tab of this repository](https://github.com/processone/ejabberd/security). This ensures your report is securely transmitted and tracked.
|
||||
|
||||
**Alternative**: If you cannot use the GitHub system, send an email to **`contact@process-one.net`** with the following details:
|
||||
|
||||
- A clear description of the vulnerability.
|
||||
- Steps to reproduce the issue.
|
||||
- Any potential impact or exploitation scenarios.
|
||||
|
||||
### Response Time
|
||||
|
||||
We aim to acknowledge receipt of your report within 72 hours. You can expect regular updates on the status of your report.
|
||||
|
||||
### Resolution
|
||||
|
||||
If the vulnerability is confirmed, we will work on a patch or mitigation strategy.
|
||||
We will notify you once the issue is resolved and coordinate a public disclosure if needed.
|
||||
|
||||
### Acknowledgements
|
||||
|
||||
We value and appreciate the contributions of security researchers and community members.
|
||||
If you wish, we are happy to acknowledge your efforts publicly by listing your name (or alias) below in this document.
|
||||
Please let us know if you would like to be recognized when reporting the vulnerability.
|
||||
|
||||
## Public Discussion
|
||||
|
||||
For general inquiries or discussions about the project’s security, feel free to chat with us here:
|
||||
|
||||
- XMPP room: `ejabberd@conference.process-one.net`
|
||||
- [GitHub Discussions](https://github.com/processone/ejabberd/discussions)
|
||||
|
||||
However, please note that if the issue is **critical** or potentially exploitable, **do not share it publicly**. Instead, we strongly recommend you contact the maintainers directly via the private reporting methods outlined above to ensure a secure and timely response.
|
||||
|
||||
Thank you for helping us improve the security of ejabberd!
|
|
@ -776,12 +776,21 @@
|
|||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0480.html"/>
|
||||
<xmpp:version>0.1</xmpp:version>
|
||||
<xmpp:version>0.2.0</xmpp:version>
|
||||
<xmpp:since>24.10</xmpp:since>
|
||||
<xmpp:status>complete</xmpp:status>
|
||||
<xmpp:note>mod_scram_upgrade</xmpp:note>
|
||||
</xmpp:SupportedXep>
|
||||
</implements>
|
||||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0484.html"/>
|
||||
<xmpp:version>0.2.0</xmpp:version>
|
||||
<xmpp:since>24.12</xmpp:since>
|
||||
<xmpp:status>complete</xmpp:status>
|
||||
<xmpp:note>mod_auth_fast</xmpp:note>
|
||||
</xmpp:SupportedXep>
|
||||
</implements>
|
||||
<implements>
|
||||
<xmpp:SupportedXep>
|
||||
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0485.html"/>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
.\" Title: ejabberd.yml
|
||||
.\" Author: [see the "AUTHOR" section]
|
||||
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
|
||||
.\" Date: 10/28/2024
|
||||
.\" Date: 12/17/2024
|
||||
.\" Manual: \ \&
|
||||
.\" Source: \ \&
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "EJABBERD\&.YML" "5" "10/28/2024" "\ \&" "\ \&"
|
||||
.TH "EJABBERD\&.YML" "5" "12/17/2024" "\ \&" "\ \&"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * 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/24\&.10/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/24\&.12/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 24\&.10\&. The options that changed in this version are marked with 🟤\&.
|
||||
This section describes top level options of ejabberd 24\&.12\&. The options that changed in this version are marked with 🟤\&.
|
||||
.PP
|
||||
\fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLRules|ACLName}}\fR
|
||||
.RS 4
|
||||
|
@ -512,7 +512,8 @@ c2s_ciphers:
|
|||
.PP
|
||||
\fBc2s_dhfile\fR: \fIPath\fR
|
||||
.RS 4
|
||||
Full path to a file containing custom DH parameters to use for c2s connections\&. Such a file could be created with the command "openssl dhparam \-out dh\&.pem 2048"\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&.
|
||||
Full path to a file containing custom DH parameters to use for c2s connections\&. Such a file could be created with the command
|
||||
\fI"openssl dhparam \-out dh\&.pem 2048"\fR\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBc2s_protocol_options\fR: \fI[Option, \&.\&.\&.]\fR
|
||||
|
@ -639,7 +640,7 @@ listener as well\&. If set to
|
|||
already enabled, with encryption if available\&. If set to
|
||||
\fIundefined\fR, it builds the URL using the deprecated
|
||||
\fIcaptcha_host\fR
|
||||
+ /captcha\&. The default value is
|
||||
\fI+ /captcha\fR\&. The default value is
|
||||
\fIauto\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
|
@ -758,28 +759,43 @@ are:
|
|||
The number of components to balance\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBtype\fR: \fIrandom | source | destination | bare_source | bare_destination\fR
|
||||
\fBtype\fR: \fIValue\fR
|
||||
.RS 4
|
||||
How to deliver stanzas to connected components:
|
||||
\fIrandom\fR
|
||||
\- an instance is chosen at random;
|
||||
\fIdestination\fR
|
||||
\- an instance is chosen by the full JID of the packet\(cqs
|
||||
How to deliver stanzas to connected components\&. The default value is
|
||||
\fIrandom\fR\&. Possible values:
|
||||
.RE
|
||||
.PP
|
||||
\fB\- bare_destination\fR
|
||||
.RS 4
|
||||
by the bare JID (without resource) of the packet\(cqs
|
||||
\fIto\fR
|
||||
attribute;
|
||||
\fIsource\fR
|
||||
\- by the full JID of the packet\(cqs
|
||||
attribute
|
||||
.RE
|
||||
.PP
|
||||
\fB\- bare_source\fR
|
||||
.RS 4
|
||||
by the bare JID (without resource) of the packet\(cqs
|
||||
\fIfrom\fR
|
||||
attribute;
|
||||
\fIbare_destination\fR
|
||||
\- by the bare JID (without resource) of the packet\(cqs
|
||||
attribute is used
|
||||
.RE
|
||||
.PP
|
||||
\fB\- destination\fR
|
||||
.RS 4
|
||||
an instance is chosen by the full JID of the packet\(cqs
|
||||
\fIto\fR
|
||||
attribute;
|
||||
\fIbare_source\fR
|
||||
\- by the bare JID (without resource) of the packet\(cqs
|
||||
attribute
|
||||
.RE
|
||||
.PP
|
||||
\fB\- random\fR
|
||||
.RS 4
|
||||
an instance is chosen at random
|
||||
.RE
|
||||
.PP
|
||||
\fB\- source\fR
|
||||
.RS 4
|
||||
by the full JID of the packet\(cqs
|
||||
\fIfrom\fR
|
||||
attribute is used\&. The default value is
|
||||
\fIrandom\fR\&.
|
||||
attribute
|
||||
.RE
|
||||
.sp
|
||||
\fBExample\fR:
|
||||
|
@ -1104,13 +1120,16 @@ section for details\&.
|
|||
\fINote\fR
|
||||
about this option: added in 22\&.10\&. The number of messages to accept in
|
||||
log_burst_limit_window_time
|
||||
period before starting to drop them\&. Default 500
|
||||
period before starting to drop them\&. Default
|
||||
500
|
||||
.RE
|
||||
.PP
|
||||
\fBlog_burst_limit_window_time\fR: \fINumber\fR
|
||||
.RS 4
|
||||
\fINote\fR
|
||||
about this option: added in 22\&.10\&. The time period to rate\-limit log messages by\&. Defaults to 1 second\&.
|
||||
about this option: added in 22\&.10\&. The time period to rate\-limit log messages by\&. Defaults to
|
||||
1
|
||||
second\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBlog_modules_fully\fR: \fI[Module, \&.\&.\&.]\fR
|
||||
|
@ -1132,9 +1151,8 @@ crash\&.log\&.0\&.
|
|||
\fBlog_rotate_size\fR: \fIpos_integer() | infinity\fR
|
||||
.RS 4
|
||||
The size (in bytes) of a log file to trigger rotation\&. If set to
|
||||
\fIinfinity\fR, log rotation is disabled\&. The default value is
|
||||
\fI10485760\fR
|
||||
(that is, 10 Mb)\&.
|
||||
\fIinfinity\fR, log rotation is disabled\&. The default value is 10 Mb expressed in bytes:
|
||||
\fI10485760\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBloglevel\fR: \fInone | emergency | alert | critical | error | warning | notice | info | debug\fR
|
||||
|
@ -1175,7 +1193,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/24\&.10/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/24\&.12/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
|
||||
|
@ -1295,14 +1313,16 @@ which means it first tries connecting with IPv6, if that fails it tries using IP
|
|||
\fBoutgoing_s2s_ipv4_address\fR: \fIAddress\fR
|
||||
.RS 4
|
||||
\fINote\fR
|
||||
about this option: added in 20\&.12\&. Specify the IPv4 address that will be used when establishing an outgoing S2S IPv4 connection, for example "127\&.0\&.0\&.1"\&. The default value is
|
||||
about this option: added in 20\&.12\&. Specify the IPv4 address that will be used when establishing an outgoing S2S IPv4 connection, for example
|
||||
\fI"127\&.0\&.0\&.1"\fR\&. The default value is
|
||||
\fIundefined\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBoutgoing_s2s_ipv6_address\fR: \fIAddress\fR
|
||||
.RS 4
|
||||
\fINote\fR
|
||||
about this option: added in 20\&.12\&. Specify the IPv6 address that will be used when establishing an outgoing S2S IPv6 connection, for example "::FFFF:127\&.0\&.0\&.1"\&. The default value is
|
||||
about this option: added in 20\&.12\&. Specify the IPv6 address that will be used when establishing an outgoing S2S IPv6 connection, for example
|
||||
\fI"::FFFF:127\&.0\&.0\&.1"\fR\&. The default value is
|
||||
\fIundefined\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
|
@ -1414,11 +1434,13 @@ or
|
|||
if the latter is not set\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBredis_server\fR: \fIHostname\fR
|
||||
\fBredis_server 🟤\fR: \fIHost | IP Address | Unix Socket Path\fR
|
||||
.RS 4
|
||||
A hostname or an IP address of the
|
||||
\fINote\fR
|
||||
about this option: improved in 24\&.12\&. A hostname, IP address or unix domain socket file of the
|
||||
\fIdatabase\&.md#redis|Redis\fR
|
||||
server\&.The default is
|
||||
server\&. Setup the path to unix domain socket like:
|
||||
\fI"unix:/path/to/socket"\fR\&. The default value is
|
||||
\fIlocalhost\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
|
@ -1527,7 +1549,8 @@ s2s_ciphers:
|
|||
.PP
|
||||
\fBs2s_dhfile\fR: \fIPath\fR
|
||||
.RS 4
|
||||
Full path to a file containing custom DH parameters to use for s2s connections\&. Such a file could be created with the command "openssl dhparam \-out dh\&.pem 2048"\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&.
|
||||
Full path to a file containing custom DH parameters to use for s2s connections\&. Such a file could be created with the command
|
||||
\fI"openssl dhparam \-out dh\&.pem 2048"\fR\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBs2s_dns_retries\fR: \fINumber\fR
|
||||
|
@ -1775,7 +1798,8 @@ The password for SQL authentication\&. The default is empty string\&.
|
|||
.PP
|
||||
\fBsql_pool_size\fR: \fISize\fR
|
||||
.RS 4
|
||||
Number of connections to the SQL server that ejabberd will open for each virtual host\&. The default value is 10\&. WARNING: for SQLite this value is
|
||||
Number of connections to the SQL server that ejabberd will open for each virtual host\&. The default value is
|
||||
\fI10\fR\&. WARNING: for SQLite this value is
|
||||
\fI1\fR
|
||||
by default and it\(cqs not recommended to change it due to potential race conditions\&.
|
||||
.RE
|
||||
|
@ -1830,7 +1854,8 @@ this can also be an ODBC connection string\&. When
|
|||
is
|
||||
\fImysql\fR
|
||||
or
|
||||
\fIpgsql\fR, this can be the path to a unix domain socket expressed like: "unix:/path/to/socket"\&.The default value is
|
||||
\fIpgsql\fR, this can be the path to a unix domain socket expressed like:
|
||||
\fI"unix:/path/to/socket"\fR\&.The default value is
|
||||
\fIlocalhost\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
|
@ -1944,7 +1969,8 @@ This option enables validation for
|
|||
header to protect against connections from other domains than given in the configuration file\&. In this way, the lower layer load balancer can be chosen for a specific ejabberd implementation while still providing a secure WebSocket connection\&. The default value is
|
||||
\fIignore\fR\&. An example value of the
|
||||
\fIURL\fR
|
||||
is "https://test\&.example\&.org:8081"\&.
|
||||
is
|
||||
\fI"https://test\&.example\&.org:8081"\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBwebsocket_ping_interval\fR: \fItimeout()\fR
|
||||
|
@ -1964,7 +1990,7 @@ seconds\&.
|
|||
.RE
|
||||
.SH "MODULES"
|
||||
.sp
|
||||
This section describes modules options of ejabberd 24\&.10\&. The modules that changed in this version are marked with 🟤\&.
|
||||
This section describes modules options of ejabberd 24\&.12\&. The modules that changed in this version are marked with 🟤\&.
|
||||
.SS "mod_adhoc"
|
||||
.sp
|
||||
This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&.
|
||||
|
@ -1989,41 +2015,11 @@ This module provides additional administrative commands\&.
|
|||
.sp
|
||||
Details for some commands:
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
\fIban_account\fR: This command kicks all the connected sessions of the account from the server\&. It also changes their password to a randomly generated one, so they can\(cqt login anymore unless a server administrator changes their password again\&. It is possible to define the reason of the ban\&. The new password also includes the reason and the date and time of the ban\&. See an example below\&.
|
||||
.RE
|
||||
\fIban_account\fR API: This command kicks all the connected sessions of the account from the server\&. It also changes their password to a randomly generated one, so they can\(cqt login anymore unless a server administrator changes their password again\&. It is possible to define the reason of the ban\&. The new password also includes the reason and the date and time of the ban\&. See an example below\&.
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
\fIpushroster\fR: (and
|
||||
\fIpushroster\-all\fR) The roster file must be placed, if using Windows, on the directory where you installed ejabberd:
|
||||
C:/Program Files/ejabberd
|
||||
or similar\&. If you use other Operating System, place the file on the same directory where the \&.beam files are installed\&. See below an example roster file\&.
|
||||
.RE
|
||||
\fIpush_roster\fR API (and \fIpush_roster_all\fR API): The roster file must be placed, if using Windows, on the directory where you installed ejabberd: C:/Program Files/ejabberd or similar\&. If you use other Operating System, place the file on the same directory where the \&.beam files are installed\&. See below an example roster file\&.
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.sp -1
|
||||
.IP \(bu 2.3
|
||||
.\}
|
||||
\fIsrg_create\fR: If you want to put a group Name with blank spaces, use the characters "\*(Aq and \*(Aq" to define when the Name starts and ends\&. See an example below\&.
|
||||
.RE
|
||||
\fIsrg_create\fR API: If you want to put a group Name with blank spaces, use the characters \fI"\fR\*(Aq and \fI\*(Aq"\fR to define when the Name starts and ends\&. See an example below\&.
|
||||
.sp
|
||||
The module has no options\&.
|
||||
.sp
|
||||
|
@ -2056,7 +2052,7 @@ modules:
|
|||
.RE
|
||||
.\}
|
||||
.sp
|
||||
Content of roster file for \fIpushroster\fR command:
|
||||
Content of roster file for \fIpush_roster\fR API:
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
|
@ -2070,7 +2066,7 @@ Content of roster file for \fIpushroster\fR command:
|
|||
.RE
|
||||
.\}
|
||||
.sp
|
||||
With this call, the sessions of the local account which JID is boby@example\&.org will be kicked, and its password will be set to something like \fIBANNED_ACCOUNT\(em20080425T21:45:07\(em2176635\(emSpammed_rooms\fR
|
||||
With this call, the sessions of the local account which JID is \fIboby@example\&.org\fR will be kicked, and its password will be set to something like \fIBANNED_ACCOUNT\(em20080425T21:45:07\(em2176635\(emSpammed_rooms\fR
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
|
@ -2082,7 +2078,7 @@ ejabberdctl vhost example\&.org ban_account boby "Spammed rooms"
|
|||
.RE
|
||||
.\}
|
||||
.sp
|
||||
Call to srg_create using double\-quotes and single\-quotes:
|
||||
Call to \fIsrg_create\fR API using double\-quotes and single\-quotes:
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
|
@ -2215,6 +2211,58 @@ Same as top\-level
|
|||
option, but applied to this module only\&.
|
||||
.RE
|
||||
.RE
|
||||
.SS "mod_auth_fast 🟤"
|
||||
.sp
|
||||
\fINote\fR about this option: added in 24\&.12\&.
|
||||
.sp
|
||||
The module adds support for XEP\-0480: Fast Authentication Streamlining Tokens that allows users to authenticate using self managed tokens\&.
|
||||
.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
|
||||
\fBdb_type\fR: \fImnesia | sql\fR
|
||||
.RS 4
|
||||
Same as top\-level
|
||||
\fIdefault_db\fR
|
||||
option, but applied to this module only\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBtoken_lifetime\fR: \fItimeout()\fR
|
||||
.RS 4
|
||||
Time that tokens will be keept, measured from it\(cqs creation time\&. Default value set to 30 days
|
||||
.RE
|
||||
.PP
|
||||
\fBtoken_refresh_age\fR: \fItimeout()\fR
|
||||
.RS 4
|
||||
This time determines age of token, that qualifies for automatic refresh\&. Default value set to 1 day
|
||||
.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
|
||||
modules:
|
||||
mod_auth_fast:
|
||||
token_timeout: 14days
|
||||
.fi
|
||||
.if n \{\
|
||||
.RE
|
||||
.\}
|
||||
.RE
|
||||
.SS "mod_avatar"
|
||||
.sp
|
||||
The purpose of the module is to cope with legacy and modern XMPP clients posting avatars\&. The process is described in XEP\-0398: User Avatar to vCard\-Based Avatars Conversion\&.
|
||||
|
@ -2541,7 +2589,7 @@ This module serves a simple page for the Converse XMPP web browser client\&.
|
|||
.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\&.
|
||||
.sp
|
||||
Make sure either \fImod_bosh\fR or \fIejabberd_http_ws\fR \fIlisten\-options\&.md#request_handlers|request_handlers\fR are enabled\&.
|
||||
Make sure either \fImod_bosh\fR or \fIlisten\&.md#ejabberd_http_ws|ejabberd_http_ws\fR are enabled in at least one \fIrequest_handlers\fR\&.
|
||||
.sp
|
||||
When \fIconversejs_css\fR and \fIconversejs_script\fR are \fIauto\fR, by default they point to the public Converse client\&.
|
||||
.sp
|
||||
|
@ -2601,9 +2649,9 @@ is replaced with the hostname\&. The default value is
|
|||
.PP
|
||||
\fBwebsocket_url\fR: \fIauto | WebSocketURL\fR
|
||||
.RS 4
|
||||
A WebSocket URL to which Converse can connect to\&. The keyword
|
||||
A WebSocket URL to which Converse can connect to\&. The
|
||||
\fI@HOST@\fR
|
||||
is replaced with the real virtual host name\&. If set to
|
||||
keyword is replaced with the real virtual host name\&. If set to
|
||||
\fIauto\fR, it will build the URL of the first configured WebSocket request handler\&. The default value is
|
||||
\fIauto\fR\&.
|
||||
.RE
|
||||
|
@ -2930,7 +2978,7 @@ This module serves small \fIhost\-meta\fR files as described in XEP\-0156: Disco
|
|||
.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\&.
|
||||
.sp
|
||||
Notice it only works if ejabberd_http has tls enabled\&.
|
||||
Notice it only works if \fIlisten\&.md#ejabberd_http|ejabberd_http\fR has \fIlisten\-options\&.md#tls|tls\fR enabled\&.
|
||||
.sp
|
||||
.it 1 an-trap
|
||||
.nr an-no-space-flag 1
|
||||
|
@ -2998,11 +3046,26 @@ This module provides a ReST interface to call \fI\&.\&./\&.\&./developer/ejabber
|
|||
.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\&.
|
||||
.sp
|
||||
To use a specific API version N, when defining the URL path in the request_handlers, add a \fIvN\fR\&. For example: \fI/api/v2: mod_http_api\fR
|
||||
To use a specific API version N, when defining the URL path in the request_handlers, add a vN\&. For example: \fI/api/v2: mod_http_api\fR\&.
|
||||
.sp
|
||||
To run a command, send a POST request to the corresponding URL: \fIhttp://localhost:5280/api/<command_name>\fR
|
||||
To run a command, send a POST request to the corresponding URL: \fIhttp://localhost:5280/api/COMMAND\-NAME\fR
|
||||
.sp
|
||||
The module has no options\&.
|
||||
.it 1 an-trap
|
||||
.nr an-no-space-flag 1
|
||||
.nr an-break-flag 1
|
||||
.br
|
||||
.ps +1
|
||||
\fBAvailable options:\fR
|
||||
.RS 4
|
||||
.PP
|
||||
\fBdefault_version\fR: \fIinteger() | string()\fR
|
||||
.RS 4
|
||||
What API version to use when none is specified in the URL path\&. If setting an ejabberd version, it will use the latest API version that was available in that ejabberd version\&. For example, setting
|
||||
\fI"24\&.06"\fR
|
||||
in this option implies
|
||||
\fI2\fR\&. The default value is the latest version\&.
|
||||
.RE
|
||||
.RE
|
||||
.sp
|
||||
.it 1 an-trap
|
||||
.nr an-no-space-flag 1
|
||||
|
@ -3024,7 +3087,8 @@ listen:
|
|||
/api: mod_http_api
|
||||
|
||||
modules:
|
||||
mod_http_api: {}
|
||||
mod_http_api:
|
||||
default_version: 2
|
||||
.fi
|
||||
.if n \{\
|
||||
.RE
|
||||
|
@ -3176,26 +3240,34 @@ This option specifies additional header fields to be included in all HTTP respon
|
|||
.RS 4
|
||||
This option defines the permission bits of the
|
||||
\fIdocroot\fR
|
||||
directory and any directories created during file uploads\&. The bits are specified as an octal number (see the chmod(1) manual page) within double quotes\&. For example: "0755"\&. The default is undefined, which means no explicit permissions will be set\&.
|
||||
directory and any directories created during file uploads\&. The bits are specified as an octal number (see the
|
||||
\fIchmod(1)\fR
|
||||
manual page) within double quotes\&. For example:
|
||||
\fI"0755"\fR\&. The default is undefined, which means no explicit permissions will be set\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBdocroot\fR: \fIPath\fR
|
||||
.RS 4
|
||||
Uploaded files are stored below the directory specified (as an absolute path) with this option\&. The keyword @HOME@ is replaced with the home directory of the user running ejabberd, and the keyword @HOST@ with the virtual host name\&. The default value is "@HOME@/upload"\&.
|
||||
Uploaded files are stored below the directory specified (as an absolute path) with this option\&. The keyword
|
||||
\fI@HOME@\fR
|
||||
is replaced with the home directory of the user running ejabberd, and the keyword
|
||||
\fI@HOST@\fR
|
||||
with the virtual host name\&. The default value is
|
||||
\fI"@HOME@/upload"\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBexternal_secret\fR: \fIText\fR
|
||||
.RS 4
|
||||
This option makes it possible to offload all HTTP Upload processing to a separate HTTP server\&. Both ejabberd and the HTTP server should share this secret and behave exactly as described at
|
||||
Prosody\(cqs mod_http_upload_external
|
||||
in the
|
||||
\fIImplementation\fR
|
||||
section\&. There is no default value\&.
|
||||
Prosody\(cqs mod_http_upload_external: Implementation\&. There is no default value\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBfile_mode\fR: \fIPermission\fR
|
||||
.RS 4
|
||||
This option defines the permission bits of uploaded files\&. The bits are specified as an octal number (see the chmod(1) manual page) within double quotes\&. For example: "0644"\&. The default is undefined, which means no explicit permissions will be set\&.
|
||||
This option defines the permission bits of uploaded files\&. The bits are specified as an octal number (see the
|
||||
\fIchmod(1)\fR
|
||||
manual page) within double quotes\&. For example:
|
||||
\fI"0644"\fR\&. The default is undefined, which means no explicit permissions will be set\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBget_url\fR: \fIURL\fR
|
||||
|
@ -3203,8 +3275,9 @@ This option defines the permission bits of uploaded files\&. The bits are specif
|
|||
This option specifies the initial part of the GET URLs used for downloading the files\&. The default value is
|
||||
\fIundefined\fR\&. When this option is
|
||||
\fIundefined\fR, this option is set to the same value as
|
||||
\fIput_url\fR\&. The keyword @HOST@ is replaced with the virtual host name\&. NOTE: if GET requests are handled by
|
||||
\fImod_http_upload\fR, the
|
||||
\fIput_url\fR\&. The keyword
|
||||
\fI@HOST@\fR
|
||||
is replaced with the virtual host name\&. NOTE: if GET requests are handled by this module, the
|
||||
\fIget_url\fR
|
||||
must match the
|
||||
\fIput_url\fR\&. Setting it to a different value only makes sense if an external web server or
|
||||
|
@ -3223,7 +3296,8 @@ instead\&.
|
|||
.RS 4
|
||||
This option defines the Jabber IDs of the service\&. If the
|
||||
\fIhosts\fR
|
||||
option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "upload\&."\&. The keyword
|
||||
option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix
|
||||
\fI"upload\&."\fR\&. The keyword
|
||||
\fI@HOST@\fR
|
||||
is replaced with the real virtual host name\&.
|
||||
.RE
|
||||
|
@ -3246,12 +3320,16 @@ must be specified\&. The default value is
|
|||
.PP
|
||||
\fBname\fR: \fIName\fR
|
||||
.RS 4
|
||||
A name of the service in the Service Discovery\&. This will only be displayed by special XMPP clients\&. The default value is "HTTP File Upload"\&.
|
||||
A name of the service in the Service Discovery\&. The default value is
|
||||
\fI"HTTP File Upload"\fR\&. Please note this will only be displayed by some XMPP clients\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBput_url\fR: \fIURL\fR
|
||||
.RS 4
|
||||
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\&. The default value is "https://@HOST@:5443/upload"\&.
|
||||
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
|
||||
\fI"https://@HOST@:5443/upload"\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBrm_on_unregister\fR: \fItrue | false\fR
|
||||
|
@ -3263,7 +3341,9 @@ This option specifies whether files uploaded by a user should be removed when th
|
|||
\fBsecret_length\fR: \fILength\fR
|
||||
.RS 4
|
||||
This option defines the length of the random string included in the GET and PUT URLs generated by
|
||||
\fImod_http_upload\fR\&. The minimum length is 8 characters, but it is recommended to choose a larger value\&. The default value is
|
||||
\fImod_http_upload\fR\&. The minimum length is
|
||||
\fI8\fR
|
||||
characters, but it is recommended to choose a larger value\&. The default value is
|
||||
\fI40\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
|
@ -3358,7 +3438,7 @@ This module depends on \fImod_http_upload\fR\&.
|
|||
.PP
|
||||
\fBaccess_hard_quota\fR: \fIAccessName\fR
|
||||
.RS 4
|
||||
This option defines which access rule is used to specify the "hard quota" for the matching JIDs\&. That rule must yield a positive number for any JID that is supposed to have a quota limit\&. This is the number of megabytes a corresponding user may upload\&. When this threshold is exceeded, ejabberd deletes the oldest files uploaded by that user until their disk usage equals or falls below the specified soft quota (see
|
||||
This option defines which access rule is used to specify the "hard quota" for the matching JIDs\&. That rule must yield a positive number for any JID that is supposed to have a quota limit\&. This is the number of megabytes a corresponding user may upload\&. When this threshold is exceeded, ejabberd deletes the oldest files uploaded by that user until their disk usage equals or falls below the specified soft quota (see also option
|
||||
\fIaccess_soft_quota\fR)\&. The default value is
|
||||
\fIhard_upload_quota\fR\&.
|
||||
.RE
|
||||
|
@ -3388,7 +3468,7 @@ directory, once per day\&. The default value is
|
|||
\fBExamples:\fR
|
||||
.RS 4
|
||||
.sp
|
||||
Please note that it\(cqs not necessary to specify the \fIaccess_hard_quota\fR and \fIaccess_soft_quota\fR options in order to use the quota feature\&. You can stick to the default names and just specify access rules such as those in this example:
|
||||
Notice it\(cqs not necessary to specify the \fIaccess_hard_quota\fR and \fIaccess_soft_quota\fR options in order to use the quota feature\&. You can stick to the default names and just specify access rules such as those in this example:
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
|
@ -3600,7 +3680,7 @@ When this option is disabled, for each individual subscriber a separate mucsub m
|
|||
.sp
|
||||
\fINote\fR about this option: added in 24\&.02\&.
|
||||
.sp
|
||||
Matrix gateway\&.
|
||||
Matrix gateway\&. Erlang/OTP 25 or higher is required to use this module\&.
|
||||
.sp
|
||||
.it 1 an-trap
|
||||
.nr an-no-space-flag 1
|
||||
|
@ -3853,7 +3933,8 @@ instead\&.
|
|||
.RS 4
|
||||
This option defines the Jabber IDs of the service\&. If the
|
||||
\fIhosts\fR
|
||||
option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "mix\&."\&. The keyword
|
||||
option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix
|
||||
\fI"mix\&."\fR\&. The keyword
|
||||
\fI@HOST@\fR
|
||||
is replaced with the real virtual host name\&.
|
||||
.RE
|
||||
|
@ -4048,15 +4129,40 @@ This module adds ability to synchronize local MQTT topics with data on remote se
|
|||
Identifier of a user that will be assigned as owner of local changes\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBservers\fR: \fI{ServerUrl: {publish: [TopicPairs, subscribe: [TopicPairs], authentication: [AuthInfo]}}]\fR
|
||||
\fBservers\fR: \fI{ServerUrl: {Key: Value}}\fR
|
||||
.RS 4
|
||||
Declaration of data to share, must contain
|
||||
\fIpublish\fR
|
||||
or
|
||||
\fIsubscribe\fR
|
||||
or both, and
|
||||
\fIauthentication\fR
|
||||
section with username/password field or certfile pointing to client certificate\&. Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), ws, wss, ws5, wss5\&. Certificate authentication can be only used with mqtts, mqtt5s, wss, wss5\&.
|
||||
Declaration of data to share for each ServerUrl\&. Server URLs can use schemas:
|
||||
\fImqtt\fR,
|
||||
\fImqtts\fR
|
||||
(mqtt with tls),
|
||||
\fImqtt5\fR,
|
||||
\fImqtt5s\fR
|
||||
(both to trigger v5 protocol),
|
||||
\fIws\fR,
|
||||
\fIwss\fR,
|
||||
\fIws5\fR,
|
||||
\fIwss5\fR\&. Keys must be:
|
||||
.PP
|
||||
\fBauthentication\fR: \fI{AuthKey: AuthValue}\fR
|
||||
.RS 4
|
||||
List of authentication information, where AuthKey can be:
|
||||
\fIusername\fR
|
||||
and
|
||||
\fIpassword\fR
|
||||
fields, or
|
||||
\fIcertfile\fR
|
||||
pointing to client certificate\&. Certificate authentication can be used only with mqtts, mqtt5s, wss, wss5\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBpublish\fR: \fI{LocalTopic: RemoteTopic}\fR
|
||||
.RS 4
|
||||
Either publish or subscribe must be set, or both\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBsubscribe\fR: \fI{RemoteTopic: LocalTopic}\fR
|
||||
.RS 4
|
||||
Either publish or subscribe must be set, or both\&.
|
||||
.RE
|
||||
.RE
|
||||
.RE
|
||||
.sp
|
||||
|
@ -4074,16 +4180,16 @@ section with username/password field or certfile pointing to client certificate\
|
|||
.nf
|
||||
modules:
|
||||
mod_mqtt_bridge:
|
||||
replication_user: "mqtt@xmpp\&.server\&.com"
|
||||
servers:
|
||||
"mqtt://server\&.com":
|
||||
authentication:
|
||||
certfile: "/etc/ejabberd/mqtt_server\&.pem"
|
||||
publish:
|
||||
"localA": "remoteA" # local changes to \*(AqlocalA\*(Aq will be replicated on remote server as \*(AqremoteA\*(Aq
|
||||
"topicB": "topicB"
|
||||
subscribe:
|
||||
"remoteB": "localB" # changes to \*(AqremoteB\*(Aq on remote server will be stored as \*(AqlocalB\*(Aq on local server
|
||||
authentication:
|
||||
certfile: "/etc/ejabberd/mqtt_server\&.pem"
|
||||
replication_user: "mqtt@xmpp\&.server\&.com"
|
||||
.fi
|
||||
.if n \{\
|
||||
.RE
|
||||
|
@ -4311,7 +4417,7 @@ The room persists even if the last participant leaves\&. The default value is
|
|||
\fIfalse\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBpresence_broadcast\fR: \fI[moderator | participant | visitor, \&.\&.\&.]\fR
|
||||
\fBpresence_broadcast\fR: \fI[Role]\fR
|
||||
.RS 4
|
||||
List of roles for which presence is broadcasted\&. The list can contain one or several of:
|
||||
\fImoderator\fR,
|
||||
|
@ -4376,7 +4482,10 @@ Timeout before hibernating the room process, expressed in seconds\&. The default
|
|||
.PP
|
||||
\fBhistory_size\fR: \fISize\fR
|
||||
.RS 4
|
||||
A small history of the current discussion is sent to users when they enter the room\&. With this option you can define the number of history messages to keep and send to users joining the room\&. The value is a non\-negative integer\&. Setting the value to 0 disables the history feature and, as a result, nothing is kept in memory\&. The default value is 20\&. This value affects all rooms on the service\&. NOTE: modern XMPP clients rely on Message Archives (XEP\-0313), so feel free to disable the history feature if you\(cqre only using modern clients and have
|
||||
A small history of the current discussion is sent to users when they enter the room\&. With this option you can define the number of history messages to keep and send to users joining the room\&. The value is a non\-negative integer\&. Setting the value to
|
||||
\fI0\fR
|
||||
disables the history feature and, as a result, nothing is kept in memory\&. The default value is
|
||||
\fI20\fR\&. This value affects all rooms on the service\&. NOTE: modern XMPP clients rely on Message Archives (XEP\-0313), so feel free to disable the history feature if you\(cqre only using modern clients and have
|
||||
\fImod_mam\fR
|
||||
module loaded\&.
|
||||
.RE
|
||||
|
@ -4462,12 +4571,16 @@ This option defines after how many users in the room, it is considered overcrowd
|
|||
.PP
|
||||
\fBmin_message_interval\fR: \fINumber\fR
|
||||
.RS 4
|
||||
This option defines the minimum interval between two messages send by an occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, message rate is not limited\&. This feature can be used to protect a MUC service from occupant abuses and limit number of messages that will be broadcasted by the service\&. A good value for this minimum message interval is 0\&.4 second\&. If an occupant tries to send messages faster, an error is send back explaining that the message has been discarded and describing the reason why the message is not acceptable\&.
|
||||
This option defines the minimum interval between two messages send by an occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, message rate is not limited\&. This feature can be used to protect a MUC service from occupant abuses and limit number of messages that will be broadcasted by the service\&. A good value for this minimum message interval is
|
||||
\fI0\&.4\fR
|
||||
second\&. If an occupant tries to send messages faster, an error is send back explaining that the message has been discarded and describing the reason why the message is not acceptable\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBmin_presence_interval\fR: \fINumber\fR
|
||||
.RS 4
|
||||
This option defines the minimum of time between presence changes coming from a given occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, no restriction is applied\&. This option can be used to protect a MUC service for occupants abuses\&. If an occupant tries to change its presence more often than the specified interval, the presence is cached by ejabberd and only the last presence is broadcasted to all occupants in the room after expiration of the interval delay\&. Intermediate presence packets are silently discarded\&. A good value for this option is 4 seconds\&.
|
||||
This option defines the minimum of time between presence changes coming from a given occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, no restriction is applied\&. This option can be used to protect a MUC service for occupants abuses\&. If an occupant tries to change its presence more often than the specified interval, the presence is cached by ejabberd and only the last presence is broadcasted to all occupants in the room after expiration of the interval delay\&. Intermediate presence packets are silently discarded\&. A good value for this option is
|
||||
\fI4\fR
|
||||
seconds\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBname\fR: \fIstring()\fR
|
||||
|
@ -4573,7 +4686,7 @@ This module depends on \fImod_muc\fR\&.
|
|||
\fINote\fR
|
||||
about this option: added in 22\&.05\&. How many users can be subscribed to a room at once using the
|
||||
\fIsubscribe_room_many\fR
|
||||
command\&. The default value is
|
||||
API\&. The default value is
|
||||
\fI50\fR\&.
|
||||
.RE
|
||||
.RE
|
||||
|
@ -5031,22 +5144,8 @@ modules:
|
|||
.SS "mod_offline"
|
||||
.sp
|
||||
This module implements XEP\-0160: Best Practices for Handling Offline Messages and XEP\-0013: Flexible Offline Message Retrieval\&. This means that all messages sent to an offline user will be stored on the server until that user comes online again\&. Thus it is very similar to how email works\&. A user is considered offline if no session presence priority > 0 are currently open\&.
|
||||
.if n \{\
|
||||
.sp
|
||||
.\}
|
||||
.RS 4
|
||||
.it 1 an-trap
|
||||
.nr an-no-space-flag 1
|
||||
.nr an-break-flag 1
|
||||
.br
|
||||
.ps +1
|
||||
\fBNote\fR
|
||||
.ps -1
|
||||
.br
|
||||
.sp
|
||||
\fIejabberdctl\fR has a command to delete expired messages (see chapter \fI\&.\&./guide/managing\&.md|Managing an ejabberd server\fR in online documentation\&.
|
||||
.sp .5v
|
||||
.RE
|
||||
The \fIdelete_expired_messages\fR API allows to delete expired messages, and \fIdelete_old_messages\fR API deletes older ones\&.
|
||||
.sp
|
||||
.it 1 an-trap
|
||||
.nr an-no-space-flag 1
|
||||
|
@ -5058,7 +5157,9 @@ This module implements XEP\-0160: Best Practices for Handling Offline Messages a
|
|||
.PP
|
||||
\fBaccess_max_user_messages\fR: \fIAccessName\fR
|
||||
.RS 4
|
||||
This option defines which access rule will be enforced to limit the maximum number of offline messages that a user can have (quota)\&. When a user has too many offline messages, any new messages that they receive are discarded, and a <resource\-constraint/> error is returned to the sender\&. The default value is
|
||||
This option defines which access rule will be enforced to limit the maximum number of offline messages that a user can have (quota)\&. When a user has too many offline messages, any new messages that they receive are discarded, and a
|
||||
\fI<resource\-constraint/>\fR
|
||||
error is returned to the sender\&. The default value is
|
||||
\fImax_user_offline_messages\fR\&.
|
||||
.RE
|
||||
.PP
|
||||
|
@ -5092,8 +5193,12 @@ option, but applied to this module only\&.
|
|||
.PP
|
||||
\fBstore_empty_body\fR: \fItrue | false | unless_chat_state\fR
|
||||
.RS 4
|
||||
Whether or not to store messages that lack a <body/> element\&. The default value is
|
||||
\fIunless_chat_state\fR, which tells ejabberd to store messages even if they lack the <body/> element, unless they only contain a chat state notification (as defined in
|
||||
Whether or not to store messages that lack a
|
||||
\fI<body/>\fR
|
||||
element\&. The default value is
|
||||
\fIunless_chat_state\fR, which tells ejabberd to store messages even if they lack the
|
||||
\fI<body/>\fR
|
||||
element, unless they only contain a chat state notification (as defined in
|
||||
XEP\-0085: Chat State Notifications\&.
|
||||
.RE
|
||||
.PP
|
||||
|
@ -5112,11 +5217,11 @@ option, but applied to this module only\&.
|
|||
.PP
|
||||
\fBuse_mam_for_storage\fR: \fItrue | false\fR
|
||||
.RS 4
|
||||
This is an experimental option\&. Enabling this option,
|
||||
\fImod_offline\fR
|
||||
uses the
|
||||
This is an experimental option\&. By enabling the option, this module uses the
|
||||
\fIarchive\fR
|
||||
table from
|
||||
\fImod_mam\fR
|
||||
archive table instead of its own spool table to retrieve the messages received when the user was offline\&. This allows client developers to slowly drop XEP\-0160 and rely on XEP\-0313 instead\&. It also further reduces the storage required when you enable MucSub\&. Enabling this option has a known drawback for the moment: most of flexible message retrieval queries don\(cqt work (those that allow retrieval/deletion of messages by id), but this specification is not widely used\&. The default value is
|
||||
instead of its own spool table to retrieve the messages received when the user was offline\&. This allows client developers to slowly drop XEP\-0160 and rely on XEP\-0313 instead\&. It also further reduces the storage required when you enable MucSub\&. Enabling this option has a known drawback for the moment: most of flexible message retrieval queries don\(cqt work (those that allow retrieval/deletion of messages by id), but this specification is not widely used\&. The default value is
|
||||
\fIfalse\fR
|
||||
to keep former behaviour as default\&.
|
||||
.RE
|
||||
|
@ -5307,7 +5412,7 @@ This module implements XEP\-0016: Privacy Lists\&.
|
|||
.ps -1
|
||||
.br
|
||||
.sp
|
||||
Nowadays modern XMPP clients rely on XEP\-0191: Blocking Command which is implemented by \fImod_blocking\fR module\&. However, you still need \fImod_privacy\fR loaded in order for \fImod_blocking\fR to work\&.
|
||||
Nowadays modern XMPP clients rely on XEP\-0191: Blocking Command which is implemented by \fImod_blocking\fR\&. However, you still need \fImod_privacy\fR loaded in order for \fImod_blocking\fR to work\&.
|
||||
.sp .5v
|
||||
.RE
|
||||
.sp
|
||||
|
@ -5360,7 +5465,7 @@ This module adds support for XEP\-0049: Private XML Storage\&.
|
|||
.sp
|
||||
Using this method, XMPP entities can store private data on the server, retrieve it whenever necessary and share it between multiple connected clients of the same user\&. The data stored might be anything, as long as it is a valid XML\&. One typical usage is storing a bookmark of all user\(cqs conferences (XEP\-0048: Bookmarks)\&.
|
||||
.sp
|
||||
It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see the command \fIbookmarks_to_pep\fR API\&.
|
||||
It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see \fIbookmarks_to_pep\fR API\&.
|
||||
.sp
|
||||
.it 1 an-trap
|
||||
.nr an-no-space-flag 1
|
||||
|
@ -6628,7 +6733,7 @@ This module enables you to create shared roster groups: groups of accounts that
|
|||
.sp
|
||||
The big advantages of this feature are that end users do not need to manually add all users to their rosters, and that they cannot permanently delete users from the shared roster groups\&. A shared roster group can have members from any XMPP server, but the presence will only be available from and to members of the same virtual host where the group is created\&. It still allows the users to have / add their own contacts, as it does not replace the standard roster\&. Instead, the shared roster contacts are merged to the relevant users at retrieval time\&. The standard user rosters thus stay unmodified\&.
|
||||
.sp
|
||||
Shared roster groups can be edited via the Web Admin, and some API commands called \fIsrg_*\fR\&. Each group has a unique name and those parameters:
|
||||
Shared roster groups can be edited via the Web Admin, and some API commands called \fIsrg_\fR, for example \fIsrg_add\fR API\&. Each group has a unique name and those parameters:
|
||||
.sp
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
|
@ -7980,7 +8085,7 @@ Should the operating system be revealed or not\&. The default value is
|
|||
.RE
|
||||
.SH "LISTENERS"
|
||||
.sp
|
||||
This section describes listeners options of ejabberd 24\&.10\&.
|
||||
This section describes listeners options of ejabberd 24\&.12\&.
|
||||
.sp
|
||||
TODO
|
||||
.SH "AUTHOR"
|
||||
|
@ -7988,13 +8093,13 @@ TODO
|
|||
ProcessOne\&.
|
||||
.SH "VERSION"
|
||||
.sp
|
||||
This document describes the configuration file of ejabberd 24\&.10\&. Configuration options of other ejabberd versions may differ significantly\&.
|
||||
This document describes the configuration file of ejabberd 24\&.12\&. 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/24\&.10/ejabberd\&.yml\&.example
|
||||
Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.12/ejabberd\&.yml\&.example
|
||||
.sp
|
||||
Main site: https://ejabberd\&.im
|
||||
.sp
|
||||
|
|
4
mix.exs
4
mix.exs
|
@ -135,7 +135,7 @@ defmodule Ejabberd.MixProject do
|
|||
{:eimp, "~> 1.0"},
|
||||
{:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false},
|
||||
{:fast_tls, "~> 1.1.22"},
|
||||
{:fast_xml, "~> 1.1.53"},
|
||||
{:fast_xml, "~> 1.1.53", override: true},
|
||||
{:fast_yaml, "~> 1.0"},
|
||||
{:idna, "~> 6.0"},
|
||||
{:mqtree, "~> 1.0"},
|
||||
|
@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do
|
|||
{:p1_utils, "~> 1.0"},
|
||||
{:pkix, "~> 1.0"},
|
||||
{:stringprep, ">= 1.0.26"},
|
||||
{:xmpp, "~> 1.9"},
|
||||
{:xmpp, git: "https://github.com/processone/xmpp", ref: "8071c86f33b9a8e9d66a10e058be088eecdc670b", override: true},
|
||||
{:yconf, git: "https://github.com/processone/yconf.git", ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938", override: true}]
|
||||
++ cond_deps()
|
||||
end
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -34,6 +34,6 @@
|
|||
"stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"},
|
||||
"stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||
"xmpp": {:hex, :xmpp, "1.9.0", "d92446bf51d36adda02db63b963fe6d4a1ede33e59b38a43d9b90afd20c25b74", [: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.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "c1b91be74a9a9503afa6766f756477516920ffbfeea0c260c2fa171355f53c27"},
|
||||
"xmpp": {:git, "https://github.com/processone/xmpp", "8071c86f33b9a8e9d66a10e058be088eecdc670b", [ref: "8071c86f33b9a8e9d66a10e058be088eecdc670b"]},
|
||||
"yconf": {:git, "https://github.com/processone/yconf.git", "9898754f16cbd4585a1c2061d72fa441ecb2e938", [ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938"]},
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@
|
|||
{"Get Pending","Виж чакащи"}.
|
||||
{"Get User Last Login Time","Покажи времето, когато потребителят е влязъл за последно"}.
|
||||
{"Get User Statistics","Покажи статистика за потребителя"}.
|
||||
{"Given Name","Име"}.
|
||||
{"Given Name","Наименование"}.
|
||||
{"Grant voice to this person?","Предоставяне на глас за потребителя?"}.
|
||||
{"has been banned","е със забранен достъп"}.
|
||||
{"has been kicked because of a system shutdown","е отстранен поради изключване на системата"}.
|
||||
|
@ -295,6 +295,7 @@
|
|||
{"No pending subscriptions found","Не са намерени чакащи абонаменти"}.
|
||||
{"No privacy list with this name found","Не е намерен списък за поверителност с това име"}.
|
||||
{"No private data found in this query","Няма открити лични данни в тази заявка"}.
|
||||
{"No <privileged_iq/> element found","Елементът <privileged_iq/> не е намерен"}.
|
||||
{"No running node found","Не е намерен работещ нод"}.
|
||||
{"No services available","Няма налични услуги"}.
|
||||
{"No statistics found for this item","Не е налична статистика за този елемент"}.
|
||||
|
@ -512,7 +513,7 @@
|
|||
{"The stanza MUST contain only one <active/> element, one <default/> element, or one <list/> element","Строфата ТРЯБВА да съдържа само един <active/> елемент, един <default/> елемент или един <list/> елемент"}.
|
||||
{"The subscription identifier associated with the subscription request","Идентификаторът на абонамента, свързан със заявката за абонамент"}.
|
||||
{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL адрес на XSL трансформацията, която може да се приложи към прикачените данни, за да се генерира подходящ елемент от тялото на съобщението."}.
|
||||
{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL адрес на XSL трансформацията, която може да се приложи към формата на прикачените данни, за да се генерира валиден резултат от Data Forms, който клиентът може да покаже с помощта на общ механизъм за визуализация на Data Forms."}.
|
||||
{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL адрес на XSL трансформацията, която може да се приложи към формата на прикачените данни, за да се генерира валиден резултат от Data Forms, който клиентът може да покаже с помощта на общ механизъм за визуализация на Data Forms"}.
|
||||
{"There was an error changing the password: ","Възникна грешка при промяна на паролата: "}.
|
||||
{"There was an error creating the account: ","Възникна грешка при създаването на профила: "}.
|
||||
{"There was an error deleting the account: ","Възникна грешка при изтриването на профила: "}.
|
||||
|
@ -537,6 +538,7 @@
|
|||
{"Too many unacked stanzas","Твърде много непотвърдени строфи"}.
|
||||
{"Too many users in this conference","Твърде много потребители в тази конференция"}.
|
||||
{"Traffic rate limit is exceeded","Лимитът за трафик е надвишен"}.
|
||||
{"~ts's MAM Archive","~ts's MAM архив"}.
|
||||
{"~ts's Offline Messages Queue","Офлайн съобщения на ~ts"}.
|
||||
{"Tuesday","Вторник"}.
|
||||
{"Unable to generate a CAPTCHA","Не може да се генерира CAPTCHA"}.
|
||||
|
@ -553,12 +555,14 @@
|
|||
{"Update message of the day on all hosts (don't send)","Актуализирай съобщението на деня на всички хостове (не изпращай)"}.
|
||||
{"Update specs to get modules source, then install desired ones.","Актуализирайте спецификациите, за да получите източник на модули, след което инсталирайте желаните."}.
|
||||
{"Update Specs","Актуализирай спецификациите"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","Актуализирането на vCard не се поддържа от настройката за съхранение на vCard"}.
|
||||
{"Upgrade","Обнови"}.
|
||||
{"URL for Archived Discussion Logs","URL адрес за дневници на архивирани дискусии"}.
|
||||
{"User already exists","Потребителят вече съществува"}.
|
||||
{"User (jid)","Потребител (jid)"}.
|
||||
{"User JID","Потребител JID"}.
|
||||
{"User Management","Управление на потребители"}.
|
||||
{"User not allowed to perform an IQ set on another user's vCard.","Потребителят не може да извършва IQ настрийка на vCard на друг потребител."}.
|
||||
{"User removed","Потребителят е премахнат"}.
|
||||
{"User session not found","Потребителската сесия не е намерена"}.
|
||||
{"User session terminated","Потребителската сесия е прекратена"}.
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
{"No pending subscriptions found","No s'han trobat subscripcions pendents"}.
|
||||
{"No privacy list with this name found","No s'ha trobat cap llista de privacitat amb aquest nom"}.
|
||||
{"No private data found in this query","No s'ha trobat dades privades en esta petició"}.
|
||||
{"No <privileged_iq/> element found","No s'ha trobat cap element <privileged_iq/>"}.
|
||||
{"No running node found","No s'ha trobat node en marxa"}.
|
||||
{"No services available","No n'hi ha serveis disponibles"}.
|
||||
{"No statistics found for this item","No n'hi ha estadístiques disponibles per a aquest element"}.
|
||||
|
@ -537,6 +538,7 @@
|
|||
{"Too many unacked stanzas","Massa missatges sense haver reconegut la seva recepció"}.
|
||||
{"Too many users in this conference","N'hi ha massa usuaris en esta sala de conferència"}.
|
||||
{"Traffic rate limit is exceeded","El límit de tràfic ha sigut sobrepassat"}.
|
||||
{"~ts's MAM Archive","Arxiu MAM de ~ts"}.
|
||||
{"~ts's Offline Messages Queue","~ts's cua de missatges offline"}.
|
||||
{"Tuesday","Dimarts"}.
|
||||
{"Unable to generate a CAPTCHA","No s'ha pogut generar un CAPTCHA"}.
|
||||
|
@ -553,12 +555,14 @@
|
|||
{"Update message of the day on all hosts (don't send)","Actualitza el missatge del dia en tots els hosts (no enviar)"}.
|
||||
{"Update specs to get modules source, then install desired ones.","Actualitza les especificacions per obtindre el codi font dels mòduls, després instal·la els que vulgues."}.
|
||||
{"Update Specs","Actualitzar Especificacions"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","El sistema d'almacenament de vCard no te capacitat per a actualitzar la vCard"}.
|
||||
{"Upgrade","Actualitza"}.
|
||||
{"URL for Archived Discussion Logs","URL dels Arxius de Discussions"}.
|
||||
{"User already exists","El usuari ja existeix"}.
|
||||
{"User JID","JID del usuari"}.
|
||||
{"User (jid)","Usuari (jid)"}.
|
||||
{"User Management","Gestió d'Usuaris"}.
|
||||
{"User not allowed to perform an IQ set on another user's vCard.","L'usuari no te permis per a modificar la vCard d'altre usuari."}.
|
||||
{"User removed","Usuari borrat"}.
|
||||
{"User session not found","Sessió d'usuari no trobada"}.
|
||||
{"User session terminated","Sessió d'usuari terminada"}.
|
||||
|
|
|
@ -223,6 +223,7 @@
|
|||
{"leaves the room","verlässt den Raum"}.
|
||||
{"List of users with hats","Liste der Benutzer mit Funktionen"}.
|
||||
{"List users with hats","Benutzer mit Funktionen auflisten"}.
|
||||
{"Logged Out","Abgemeldet"}.
|
||||
{"Logging","Protokollierung"}.
|
||||
{"Make participants list public","Teilnehmerliste öffentlich machen"}.
|
||||
{"Make room CAPTCHA protected","Raum mittels CAPTCHA schützen"}.
|
||||
|
@ -293,6 +294,7 @@
|
|||
{"No pending subscriptions found","Keine ausstehenden Abonnements gefunden"}.
|
||||
{"No privacy list with this name found","Keine Privacy-Liste mit diesem Namen gefunden"}.
|
||||
{"No private data found in this query","Keine privaten Daten in dieser Anfrage gefunden"}.
|
||||
{"No <privileged_iq/> element found","Kein <privileged_iq/>-Element gefunden"}.
|
||||
{"No running node found","Kein laufender Knoten gefunden"}.
|
||||
{"No services available","Keine Dienste verfügbar"}.
|
||||
{"No statistics found for this item","Keine Statistiken für dieses Item gefunden"}.
|
||||
|
@ -532,6 +534,7 @@
|
|||
{"Too many unacked stanzas","Zu viele unbestätigte Stanzas"}.
|
||||
{"Too many users in this conference","Zu viele Benutzer in dieser Konferenz"}.
|
||||
{"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}.
|
||||
{"~ts's MAM Archive","~ts's MAM Archiv"}.
|
||||
{"~ts's Offline Messages Queue","Offline-Nachrichten-Warteschlange von ~ts"}.
|
||||
{"Tuesday","Dienstag"}.
|
||||
{"Unable to generate a CAPTCHA","Konnte kein CAPTCHA erstellen"}.
|
||||
|
@ -588,6 +591,7 @@
|
|||
{"Whether to allow subscriptions","Ob Abonnements erlaubt sind"}.
|
||||
{"Whether to make all subscriptions temporary, based on subscriber presence","Ob alle Abonnements temporär gemacht werden sollen, basierend auf der Abonnentenpräsenz"}.
|
||||
{"Whether to notify owners about new subscribers and unsubscribes","Ob Besitzer über neue Abonnenten und Abbestellungen benachrichtigt werden sollen"}.
|
||||
{"Who can send private messages","Wer kann private Nachrichten senden"}.
|
||||
{"Who may associate leaf nodes with a collection","Wer Blattknoten mit einer Sammlung verknüpfen darf"}.
|
||||
{"Wrong parameters in the web formulary","Falsche Parameter im Webformular"}.
|
||||
{"Wrong xmlns","Falscher xmlns"}.
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
{"No pending subscriptions found","No se han encontrado suscripciones pendientes"}.
|
||||
{"No privacy list with this name found","No se ha encontrado una lista de privacidad con este nombre"}.
|
||||
{"No private data found in this query","No se ha encontrado ningún elemento de dato privado en esta petición"}.
|
||||
{"No <privileged_iq/> element found","No se encontró ningún elemento <privileged_iq/>"}.
|
||||
{"No running node found","No se ha encontrado ningún nodo activo"}.
|
||||
{"No services available","No hay servicios disponibles"}.
|
||||
{"No statistics found for this item","No se han encontrado estadísticas para este elemento"}.
|
||||
|
@ -537,6 +538,7 @@
|
|||
{"Too many unacked stanzas","Demasiados mensajes sin haber reconocido recibirlos"}.
|
||||
{"Too many users in this conference","Demasiados usuarios en esta sala"}.
|
||||
{"Traffic rate limit is exceeded","Se ha excedido el límite de tráfico"}.
|
||||
{"~ts's MAM Archive","Archivo MAM de ~ts"}.
|
||||
{"~ts's Offline Messages Queue","Cola de mensajes diferidos de ~ts"}.
|
||||
{"Tuesday","Martes"}.
|
||||
{"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}.
|
||||
|
@ -553,12 +555,14 @@
|
|||
{"Update message of the day on all hosts (don't send)","Actualizar el mensaje del día en todos los dominos (pero no enviarlo)"}.
|
||||
{"Update specs to get modules source, then install desired ones.","Actualizar Especificaciones para conseguir el código fuente de los módulos, luego instala los que quieras."}.
|
||||
{"Update Specs","Actualizar Especificaciones"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","La actualización de la vCard no es compatible con el vCard almacenamiento backend"}.
|
||||
{"Upgrade","Actualizar"}.
|
||||
{"URL for Archived Discussion Logs","URL del registro de discusiones archivadas"}.
|
||||
{"User already exists","El usuario ya existe"}.
|
||||
{"User JID","Jabber ID del usuario"}.
|
||||
{"User (jid)","Usuario (jid)"}.
|
||||
{"User Management","Administración de usuarios"}.
|
||||
{"User not allowed to perform an IQ set on another user's vCard.","No se permite al usuario realizar un IQ establecido en la vCard de otro usuario."}.
|
||||
{"User removed","Usuario eliminado"}.
|
||||
{"User session not found","Sesión de usuario no encontrada"}.
|
||||
{"User session terminated","Sesión de usuario terminada"}.
|
||||
|
|
|
@ -184,6 +184,7 @@
|
|||
{"Incorrect value of 'action' attribute","Valeur de l'attribut 'action' incorrecte"}.
|
||||
{"Incorrect value of 'action' in data form","Valeur de l'attribut 'action' incorrecte dans le formulaire"}.
|
||||
{"Incorrect value of 'path' in data form","Valeur de l'attribut 'path' incorrecte dans le formulaire"}.
|
||||
{"Install","Installer"}.
|
||||
{"Insufficient privilege","Droits insuffisants"}.
|
||||
{"Internal server error","Erreur interne du serveur"}.
|
||||
{"Invalid 'from' attribute in forwarded message","L'attribut 'from' du message transféré est incorrect"}.
|
||||
|
@ -480,12 +481,14 @@
|
|||
{"Unauthorized","Non autorisé"}.
|
||||
{"Unexpected action","Action inattendu"}.
|
||||
{"Unexpected error condition: ~p","Condition d’erreur inattendue : ~p"}.
|
||||
{"Uninstall","Désinstaller"}.
|
||||
{"Unregister an XMPP account","Annuler l’enregistrement d’un compte XMPP"}.
|
||||
{"Unregister","Désinscrire"}.
|
||||
{"Unsupported <index/> element","Elément <index/> non supporté"}.
|
||||
{"Unsupported version","Version non prise en charge"}.
|
||||
{"Update message of the day (don't send)","Mise à jour du message du jour (pas d'envoi)"}.
|
||||
{"Update message of the day on all hosts (don't send)","Mettre à jour le message du jour sur tous les domaines (ne pas envoyer)"}.
|
||||
{"Upgrade","Mise à niveau"}.
|
||||
{"URL for Archived Discussion Logs","URL des journaux de discussion archivés"}.
|
||||
{"User already exists","L'utilisateur existe déjà"}.
|
||||
{"User JID","JID de l'utilisateur"}.
|
||||
|
|
|
@ -168,6 +168,7 @@
|
|||
{"has been kicked because of an affiliation change","è stato espulso a causa di un cambiamento di appartenenza"}.
|
||||
{"has been kicked because the room has been changed to members-only","è stato espulso per la limitazione della stanza ai soli membri"}.
|
||||
{"has been kicked","è stata/o espulsa/o"}.
|
||||
{"Hash of the vCard-temp avatar of this room","Hash dell'avatar vCard-temp di questa stanza"}.
|
||||
{"Hat title","Titolo del Cappello"}.
|
||||
{"Hat URI","URI Cappello"}.
|
||||
{"Hats limit exceeded","Limite di cappelli superato"}.
|
||||
|
@ -294,6 +295,7 @@
|
|||
{"No pending subscriptions found","Nessuna sottoscrizione in attesa trovata"}.
|
||||
{"No privacy list with this name found","Nessun elenco di privacy con questo nome trovato"}.
|
||||
{"No private data found in this query","Non sono stati trovati dati privati in questa query"}.
|
||||
{"No <privileged_iq/> element found","Nessun elemento <privileged_iq/> trovato"}.
|
||||
{"No running node found","Nessun nodo in esecuzione trovato"}.
|
||||
{"No services available","Nessun servizio disponibile"}.
|
||||
{"No statistics found for this item","Nessuna statistica trovata per questa voce"}.
|
||||
|
@ -536,6 +538,7 @@
|
|||
{"Too many unacked stanzas","Troppe stanze non riconosciute"}.
|
||||
{"Too many users in this conference","Troppi utenti in questa conferenza"}.
|
||||
{"Traffic rate limit is exceeded","Limite di traffico superato"}.
|
||||
{"~ts's MAM Archive","Archivio MAM di ~ts"}.
|
||||
{"~ts's Offline Messages Queue","La Coda dei Messaggi Offline di ~ts"}.
|
||||
{"Tuesday","Martedì"}.
|
||||
{"Unable to generate a CAPTCHA","Impossibile generare un CAPTCHA"}.
|
||||
|
@ -552,12 +555,14 @@
|
|||
{"Update message of the day on all hosts (don't send)","Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)"}.
|
||||
{"Update specs to get modules source, then install desired ones.","Aggiorna le specifiche per ottenere il sorgente dei moduli, quindi installa quelli desiderati."}.
|
||||
{"Update Specs","Aggiorna Specifiche"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","L'aggiornamento della vCard non è supportato dal backend di archiviazione vCard"}.
|
||||
{"Upgrade","Aggiornamento"}.
|
||||
{"URL for Archived Discussion Logs","URL per i Registri delle Discussioni Archiviati"}.
|
||||
{"User already exists","L'utente esiste già"}.
|
||||
{"User JID","JID utente"}.
|
||||
{"User (jid)","Utente (jid)"}.
|
||||
{"User Management","Gestione degli utenti"}.
|
||||
{"User not allowed to perform an IQ set on another user's vCard.","L'utente non è autorizzato a eseguire un set IQ sulla vCard di un altro utente."}.
|
||||
{"User removed","Utente rimosso"}.
|
||||
{"User session not found","Sessione utente non trovata"}.
|
||||
{"User session terminated","Sessione utente terminata"}.
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
{"No pending subscriptions found","Não foram encontradas subscrições"}.
|
||||
{"No privacy list with this name found","Nenhuma lista de privacidade encontrada com este nome"}.
|
||||
{"No private data found in this query","Nenhum dado privado encontrado nesta consulta"}.
|
||||
{"No <privileged_iq/> element found","Nenhum elemento <privileged_iq/> foi encontrado"}.
|
||||
{"No running node found","Nenhum nó em execução foi encontrado"}.
|
||||
{"No services available","Não há serviços disponíveis"}.
|
||||
{"No statistics found for this item","Não foram encontradas estatísticas para este item"}.
|
||||
|
@ -537,6 +538,7 @@
|
|||
{"Too many unacked stanzas","Número excessivo de instâncias sem confirmação"}.
|
||||
{"Too many users in this conference","Há uma quantidade excessiva de usuários nesta conferência"}.
|
||||
{"Traffic rate limit is exceeded","Limite de banda excedido"}.
|
||||
{"~ts's MAM Archive","Arquivo ~ts's MAM"}.
|
||||
{"~ts's Offline Messages Queue","~s's Fila de Mensagens Offline"}.
|
||||
{"Tuesday","Terça"}.
|
||||
{"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}.
|
||||
|
@ -553,12 +555,14 @@
|
|||
{"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}.
|
||||
{"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}.
|
||||
{"Update Specs","Atualizar as especificações"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","A atualização do vCard não é compatível com o back-end de armazenamento do vCard"}.
|
||||
{"Upgrade","Atualização"}.
|
||||
{"URL for Archived Discussion Logs","A URL para o arquivamento dos registros da discussão"}.
|
||||
{"User already exists","Usuário já existe"}.
|
||||
{"User (jid)","Usuário (jid)"}.
|
||||
{"User JID","Usuário JID"}.
|
||||
{"User Management","Gerenciamento de Usuários"}.
|
||||
{"User not allowed to perform an IQ set on another user's vCard.","O usuário não tem permissão para executar um conjunto de QI no vCard de outro usuário."}.
|
||||
{"User removed","O usuário foi removido"}.
|
||||
{"User session not found","A sessão do usuário não foi encontrada"}.
|
||||
{"User session terminated","Sessão de usuário terminada"}.
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
{"Changing role/affiliation is not allowed","Nuk lejohet ndryshim roli/përkatësie"}.
|
||||
{"Channel already exists","Kanali ekziston tashmë"}.
|
||||
{"Channel does not exist","Kanali s’ekziston"}.
|
||||
{"Channel JID","JID Kanali"}.
|
||||
{"Channels","Kanale"}.
|
||||
{"Characters not allowed:","Shenja të palejuara:"}.
|
||||
{"Chatroom configuration modified","Ndryshoi formësimi i dhomës së fjalosjeve"}.
|
||||
|
@ -99,6 +100,8 @@
|
|||
{"Full List of Room Admins","Listë e Plotë Përgjegjësish Dhome"}.
|
||||
{"Full List of Room Owners","Listë e Plotë të Zotësh Dhome"}.
|
||||
{"Full Name","Emër i Plotë"}.
|
||||
{"Get List of Online Users","Merr Listë Përdoruesish Në Linjë"}.
|
||||
{"Get List of Registered Users","Merr Listë Përdoruesish të Regjistruar"}.
|
||||
{"Get Number of Online Users","Merr Numër Përdoruesish Në Linjë"}.
|
||||
{"Get Number of Registered Users","Merr Numër Përdoruesish të Regjistruar"}.
|
||||
{"Get User Statistics","Merr Statistika Përdoruesi"}.
|
||||
|
@ -106,6 +109,7 @@
|
|||
{"Grant voice to this person?","T’i akordohet zë këtij personi?"}.
|
||||
{"has been banned","është dëbuar"}.
|
||||
{"has been kicked","është përzënë"}.
|
||||
{"Hat title","Titull kapeleje"}.
|
||||
{"Host unknown","Strehë e panjohur"}.
|
||||
{"HTTP File Upload","Ngarkim Kartelash HTTP"}.
|
||||
{"Idle connection","Lidhje e plogësht"}.
|
||||
|
@ -182,6 +186,7 @@
|
|||
{"No node specified","S’u përcaktua nyjë"}.
|
||||
{"No pending subscriptions found","S’u gjetën pajtime pezull"}.
|
||||
{"No privacy list with this name found","S’u gjet listë privatësie me atë emër"}.
|
||||
{"No <privileged_iq/> element found","S’u gjetën elementë <privileged_iq/>"}.
|
||||
{"No running node found","S’u gjet nyjë në funksionim"}.
|
||||
{"No services available","S’ka shërbime të gatshme"}.
|
||||
{"No statistics found for this item","S’u gjetën statistika për këtë objekt"}.
|
||||
|
@ -191,6 +196,7 @@
|
|||
{"Node index not found","S’u gjet tregues nyje"}.
|
||||
{"Node not found","S’u gjet nyjë"}.
|
||||
{"Node ~p","Nyjë ~p"}.
|
||||
{"Node","Nyjë"}.
|
||||
{"Nodes","Nyja"}.
|
||||
{"None","Asnjë"}.
|
||||
{"Not allowed","E palejuar"}.
|
||||
|
@ -203,6 +209,7 @@
|
|||
{"Number of online users","Numër përdoruesish në linjë"}.
|
||||
{"Number of registered users","Numër përdoruesish të regjistruar"}.
|
||||
{"Occupants are allowed to invite others","Të pranishmëve u është lejuar të ftojnë të tjerë"}.
|
||||
{"Occupants are allowed to query others","Të pranishmëve u është lejuar t’u bëjnë kërkim të tjerëve"}.
|
||||
{"Occupants May Change the Subject","Të pranishmit Mund të Ndryshojnë Subjektin"}.
|
||||
{"October","Tetor"}.
|
||||
{"OK","OK"}.
|
||||
|
@ -210,12 +217,14 @@
|
|||
{"Online Users","Përdorues Në Linjë"}.
|
||||
{"Online","Në linjë"}.
|
||||
{"Only deliver notifications to available users","Dorëzo njoftime vetëm te përdoruesit e pranishëm"}.
|
||||
{"Only moderators are allowed to retract messages","Vetëm të moderatorëve u lejohet të tërheqin mbrapsht mesazhe"}.
|
||||
{"Only occupants are allowed to send messages to the conference","Vetëm të pranishmëve u lejohet të dërgojnë mesazhe te konferenca"}.
|
||||
{"Only publishers may publish","Vetëm botuesit mund të botojnë"}.
|
||||
{"Organization Name","Emër Enti"}.
|
||||
{"Organization Unit","Njësi Organizative"}.
|
||||
{"Outgoing s2s Connections","Lidhje s2s Ikëse"}.
|
||||
{"Owner privileges required","Lypset privilegje të zoti"}.
|
||||
{"Participant ID","ID Pjesëmarrësi"}.
|
||||
{"Participant","Pjesëmarrës"}.
|
||||
{"Password Verification","Verifikim Fjalëkalimi"}.
|
||||
{"Password Verification:","Verifikim Fjalëkalimi:"}.
|
||||
|
@ -266,6 +275,7 @@
|
|||
{"Specify the access model","Specifikoni model hyrjeje"}.
|
||||
{"Specify the event message type","Përcaktoni llojin e mesazhit për aktin"}.
|
||||
{"Specify the publisher model","Përcaktoni model botuesi"}.
|
||||
{"Stanza id is not valid","ID Stanza s’është i vlefshëm"}.
|
||||
{"Stopped Nodes","Nyja të Ndalura"}.
|
||||
{"Subject","Subjekti"}.
|
||||
{"Submitted","Parashtruar"}.
|
||||
|
@ -277,6 +287,7 @@
|
|||
{"The default language of the node","Gjuha parazgjedhje e nyjës"}.
|
||||
{"The feature requested is not supported by the conference","Veçoria e kërkuar nuk mbulohen nga konferenca"}.
|
||||
{"The JID of the node creator","JID i krijjuesit të nyjës"}.
|
||||
{"The list of all online users","Lista e krejt përdoruesve në linjë"}.
|
||||
{"The name of the node","Emri i nyjës"}.
|
||||
{"The number of subscribers to the node","Numri i pajtimtarëve te nyja"}.
|
||||
{"The number of unread or undelivered messages","Numri i mesazheve të palexuar ose të padorëzuar"}.
|
||||
|
@ -303,6 +314,7 @@
|
|||
{"Unregister an XMPP account","Çregjistroni një llogari XMPP"}.
|
||||
{"Unregister","Çregjistrohuni"}.
|
||||
{"Unsupported version","Version i pambuluar"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","Përditësimi i vCard-it nuk mbulohet nga mekanizmi i depozitimit të vCard-ve"}.
|
||||
{"User already exists","Ka tashmë një përdorues të tillë"}.
|
||||
{"User JID","JID përdoruesi"}.
|
||||
{"User (jid)","Përdorues (jid)"}.
|
||||
|
@ -319,9 +331,11 @@
|
|||
{"Wednesday","E mërkurë"}.
|
||||
{"When a new subscription is processed","Kur përpunohet një pajtim i ri"}.
|
||||
{"Whether to allow subscriptions","Nëse duhen lejuar apo jo pajtime"}.
|
||||
{"Who can send private messages","Cilët mund të dërgojnë mesazhe private"}.
|
||||
{"Wrong parameters in the web formulary","Parametër i gabuar në formular web"}.
|
||||
{"XMPP Account Registration","Regjistrim Llogarish XMPP"}.
|
||||
{"XMPP Domains","Përkatësi XMPP"}.
|
||||
{"You are not allowed to send private messages","S’keni leje të dërgoni mesazhe private"}.
|
||||
{"You are not joined to the channel","S’keni hyrë te kanali"}.
|
||||
{"You have been banned from this room","Jeni dëbuar prej kësaj dhome"}.
|
||||
{"You have joined too many conferences","Keni hyrë në shumë konferenca"}.
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
{"No pending subscriptions found","未找到待处理的订阅"}.
|
||||
{"No privacy list with this name found","未找到具有此名称的隐私列表"}.
|
||||
{"No private data found in this query","在此查询中找不到专用数据"}.
|
||||
{"No <privileged_iq/> element found","未找到 <privileged_iq/> 元素"}.
|
||||
{"No running node found","找不到正在运行的节点"}.
|
||||
{"No services available","无可用服务"}.
|
||||
{"No statistics found for this item","未找到此项目的统计数据"}.
|
||||
|
@ -537,6 +538,7 @@
|
|||
{"Too many unacked stanzas","太多未确认的节"}.
|
||||
{"Too many users in this conference","此群聊中的用户太多"}.
|
||||
{"Traffic rate limit is exceeded","超过流量速率限制"}.
|
||||
{"~ts's MAM Archive","~ts 的 MAM 存档"}.
|
||||
{"~ts's Offline Messages Queue","~ts 的离线消息队列"}.
|
||||
{"Tuesday","周二"}.
|
||||
{"Unable to generate a CAPTCHA","无法生成验证码"}.
|
||||
|
@ -553,12 +555,14 @@
|
|||
{"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}.
|
||||
{"Update specs to get modules source, then install desired ones.","更新规格以获取模块源,然后安装所需的模块。"}.
|
||||
{"Update Specs","更新规格"}.
|
||||
{"Updating the vCard is not supported by the vCard storage backend","vCard 存储后端不支持更新 vCard"}.
|
||||
{"Upgrade","升级"}.
|
||||
{"URL for Archived Discussion Logs","存档讨论日志的 URL"}.
|
||||
{"User already exists","用户已存在"}.
|
||||
{"User (jid)","用户 (jid)"}.
|
||||
{"User JID","用户 JID"}.
|
||||
{"User Management","用户管理"}.
|
||||
{"User not allowed to perform an IQ set on another user's vCard.","不允许用户在其他用户的 vCard 上执行 IQ 设置。"}.
|
||||
{"User removed","用户已移除"}.
|
||||
{"User session not found","用户会话未找到"}.
|
||||
{"User session terminated","用户会话已终止"}.
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
{stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}},
|
||||
{if_var_true, stun,
|
||||
{stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}},
|
||||
{xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", {tag, "1.9.0"}}},
|
||||
{xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "8071c86f33b9a8e9d66a10e058be088eecdc670b"}},
|
||||
{yconf, ".*", {git, "https://github.com/processone/yconf", "9898754f16cbd4585a1c2061d72fa441ecb2e938"}}
|
||||
]}.
|
||||
|
||||
|
|
13
rebar.lock
13
rebar.lock
|
@ -10,7 +10,7 @@
|
|||
{<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.53">>},0},
|
||||
{<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0},
|
||||
{<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0},
|
||||
{<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1},
|
||||
{<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},0},
|
||||
{<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0},
|
||||
{<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0},
|
||||
{<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0},
|
||||
|
@ -24,7 +24,10 @@
|
|||
{<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0},
|
||||
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
|
||||
{<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.0">>},0},
|
||||
{<<"xmpp">>,
|
||||
{git,"https://github.com/processone/xmpp",
|
||||
{ref,"8071c86f33b9a8e9d66a10e058be088eecdc670b"}},
|
||||
0},
|
||||
{<<"yconf">>,
|
||||
{git,"https://github.com/processone/yconf",
|
||||
{ref,"9898754f16cbd4585a1c2061d72fa441ecb2e938"}},
|
||||
|
@ -55,8 +58,7 @@
|
|||
{<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>},
|
||||
{<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>},
|
||||
{<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>},
|
||||
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>},
|
||||
{<<"xmpp">>, <<"D92446BF51D36ADDA02DB63B963FE6D4A1EDE33E59B38A43D9B90AFD20C25B74">>}]},
|
||||
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>},
|
||||
{<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>},
|
||||
|
@ -82,6 +84,5 @@
|
|||
{<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>},
|
||||
{<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>},
|
||||
{<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>},
|
||||
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>},
|
||||
{<<"xmpp">>, <<"C1B91BE74A9A9503AFA6766F756477516920FFBFEEA0C260C2FA171355F53C27">>}]}
|
||||
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]}
|
||||
].
|
||||
|
|
|
@ -175,7 +175,7 @@ get_commands_spec() ->
|
|||
"announcement quoted, for example: \n"
|
||||
"`ejabberdctl evacuate_kindly 60 "
|
||||
"\\\"The server will stop in one minute.\\\"`",
|
||||
note = "added in 24.xx",
|
||||
note = "added in 24.12",
|
||||
module = ?MODULE, function = evacuate_kindly,
|
||||
args_desc = ["Seconds to wait", "Announcement to send, with quotes"],
|
||||
args_example = [60, <<"Server will stop now.">>],
|
||||
|
|
|
@ -45,7 +45,8 @@
|
|||
handle_unbinded_packet/2, inline_stream_features/1,
|
||||
handle_sasl2_inline/2, handle_sasl2_inline_post/3,
|
||||
handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1,
|
||||
handle_sasl2_task_next/4, handle_sasl2_task_data/3]).
|
||||
handle_sasl2_task_next/4, handle_sasl2_task_data/3,
|
||||
get_fast_tokens_fun/2, fast_mechanisms/1]).
|
||||
%% Hooks
|
||||
-export([handle_unexpected_cast/2, handle_unexpected_call/3,
|
||||
process_auth_result/3, reject_unauthenticated_packet/2,
|
||||
|
@ -465,6 +466,20 @@ check_password_digest_fun(_Mech, #{lserver := LServer}) ->
|
|||
ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P, D, DG)
|
||||
end.
|
||||
|
||||
get_fast_tokens_fun(_Mech, #{lserver := LServer}) ->
|
||||
fun(User, UA) ->
|
||||
case gen_mod:is_loaded(LServer, mod_auth_fast) of
|
||||
false -> false;
|
||||
_ -> mod_auth_fast:get_tokens(LServer, User, UA)
|
||||
end
|
||||
end.
|
||||
|
||||
fast_mechanisms(#{lserver := LServer}) ->
|
||||
case gen_mod:is_loaded(LServer, mod_auth_fast) of
|
||||
false -> [];
|
||||
_ -> mod_auth_fast:get_mechanisms(LServer)
|
||||
end.
|
||||
|
||||
bind(<<"">>, State) ->
|
||||
bind(new_uniq_id(), State);
|
||||
bind(R, #{user := U, server := S, access := Access, lang := Lang,
|
||||
|
|
|
@ -1146,7 +1146,7 @@ doc() ->
|
|||
"or 'ram' if the latter is not set.")}},
|
||||
{redis_server,
|
||||
#{value => "Host | IP Address | Unix Socket Path",
|
||||
note => "improved in 24.xx",
|
||||
note => "improved in 24.12",
|
||||
desc =>
|
||||
?T("A hostname, IP address or unix domain socket file of the "
|
||||
"_`database.md#redis|Redis`_ server. "
|
||||
|
|
|
@ -1034,8 +1034,8 @@ get_commands_spec() ->
|
|||
desc = "List all established sessions",
|
||||
policy = admin,
|
||||
module = ?MODULE, function = connected_users, args = [],
|
||||
result_desc = "List of users sessions",
|
||||
result_example = [<<"user1@example.com">>, <<"user2@example.com">>],
|
||||
result_desc = "List of users sessions full JID",
|
||||
result_example = [<<"user1@example.com/Home">>, <<"user2@example.com/54134">>],
|
||||
result = {connected_users, {list, {sessions, string}}}},
|
||||
#ejabberd_commands{name = connected_users_number, tags = [session, statistics],
|
||||
desc = "Get the number of established sessions",
|
||||
|
|
|
@ -238,7 +238,7 @@ get_auth_admin(Auth, HostHTTP, RPath, Method) ->
|
|||
{SJID, Pass} ->
|
||||
{HostOfRule, AccessRule} = get_acl_rule(RPath, Method),
|
||||
try jid:decode(SJID) of
|
||||
#jid{user = <<"">>, server = User} ->
|
||||
#jid{luser = <<"">>, lserver = User} ->
|
||||
case ejabberd_router:is_my_host(HostHTTP) of
|
||||
true ->
|
||||
get_auth_account(HostOfRule, AccessRule, User, HostHTTP,
|
||||
|
@ -246,7 +246,7 @@ get_auth_admin(Auth, HostHTTP, RPath, Method) ->
|
|||
_ ->
|
||||
{unauthorized, <<"missing-server">>}
|
||||
end;
|
||||
#jid{user = User, server = Server} ->
|
||||
#jid{luser = User, lserver = Server} ->
|
||||
get_auth_account(HostOfRule, AccessRule, User, Server,
|
||||
Pass)
|
||||
catch _:{bad_jid, _} ->
|
||||
|
@ -2286,7 +2286,7 @@ make_result(Binary,
|
|||
Second = proplists:get_value(second, ArgumentsUsed),
|
||||
FirstUrlencoded =
|
||||
list_to_binary(string:replace(
|
||||
misc:url_encode(First), "%40", "@")),
|
||||
misc:url_encode(First), "%40", "@")),
|
||||
{GroupId, Host} =
|
||||
case jid:decode(FirstUrlencoded) of
|
||||
#jid{luser = <<"">>, server = G} ->
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
% Sessions
|
||||
num_resources/2, resource_num/3,
|
||||
kick_session/4, status_num/2, status_num/1,
|
||||
status_list/2, status_list/1, connected_users_info/0,
|
||||
status_list/2, status_list_v3/2,
|
||||
status_list/1, status_list_v3/1, connected_users_info/0,
|
||||
connected_users_vhost/1, set_presence/7,
|
||||
get_presence/2, user_sessions_info/2, get_last/2, set_last/4,
|
||||
|
||||
|
@ -380,6 +381,21 @@ get_commands_spec() ->
|
|||
{status, string}
|
||||
]}}
|
||||
}}},
|
||||
#ejabberd_commands{name = status_list_host, tags = [session],
|
||||
desc = "List of users logged in host with their statuses",
|
||||
module = ?MODULE, function = status_list_v3,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args = [{host, binary}, {status, binary}],
|
||||
args_example = [<<"myserver.com">>, <<"dnd">>],
|
||||
args_desc = ["Server name", "Status type to check"],
|
||||
result_example = [{<<"peter@myserver.com/tka">>,6,<<"Busy">>}],
|
||||
result = {users, {list,
|
||||
{userstatus, {tuple, [{jid, string},
|
||||
{priority, integer},
|
||||
{status, string}
|
||||
]}}
|
||||
}}},
|
||||
#ejabberd_commands{name = status_list, tags = [session],
|
||||
desc = "List of logged users with this status",
|
||||
module = ?MODULE, function = status_list,
|
||||
|
@ -396,6 +412,21 @@ get_commands_spec() ->
|
|||
{status, string}
|
||||
]}}
|
||||
}}},
|
||||
#ejabberd_commands{name = status_list, tags = [session],
|
||||
desc = "List of logged users with this status",
|
||||
module = ?MODULE, function = status_list_v3,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args = [{status, binary}],
|
||||
args_example = [<<"dnd">>],
|
||||
args_desc = ["Status type to check"],
|
||||
result_example = [{<<"peter@myserver.com/tka">>,6,<<"Busy">>}],
|
||||
result = {users, {list,
|
||||
{userstatus, {tuple, [{jid, string},
|
||||
{priority, integer},
|
||||
{status, string}
|
||||
]}}
|
||||
}}},
|
||||
#ejabberd_commands{name = connected_users_info,
|
||||
tags = [session],
|
||||
desc = "List all established sessions and their information",
|
||||
|
@ -426,8 +457,9 @@ get_commands_spec() ->
|
|||
module = ?MODULE, function = connected_users_vhost,
|
||||
args_example = [<<"myexample.com">>],
|
||||
args_desc = ["Server name"],
|
||||
result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>],
|
||||
args = [{host, binary}],
|
||||
result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>],
|
||||
result_desc = "List of sessions full JIDs",
|
||||
result = {connected_users_vhost, {list, {sessions, string}}}},
|
||||
#ejabberd_commands{name = user_sessions_info,
|
||||
tags = [session],
|
||||
|
@ -683,6 +715,7 @@ get_commands_spec() ->
|
|||
module = ?MODULE, function = get_roster,
|
||||
args = [],
|
||||
args_rename = [{server, host}],
|
||||
result_example = [{<<"user2@localhost">>, <<"User 2">>, <<"none">>, <<"subscribe">>, [<<"Group1">>]}],
|
||||
result = {contacts, {list, {contact, {tuple, [
|
||||
{jid, string},
|
||||
{nick, string},
|
||||
|
@ -696,6 +729,7 @@ get_commands_spec() ->
|
|||
policy = user,
|
||||
module = ?MODULE, function = get_roster_count,
|
||||
args = [],
|
||||
args_example = [<<"sun">>, <<"localhost">>],
|
||||
args_rename = [{server, host}],
|
||||
result_example = 5,
|
||||
result_desc = "Number",
|
||||
|
@ -1333,6 +1367,14 @@ status_list(Host, Status) ->
|
|||
status_list(Status) ->
|
||||
status_list(<<"all">>, Status).
|
||||
|
||||
status_list_v3(ArgHost, Status) ->
|
||||
List = status_list(ArgHost, Status),
|
||||
[{jid:encode(jid:make(User, Host, Resource)), Priority, StatusText}
|
||||
|| {User, Host, Resource, Priority, StatusText} <- List].
|
||||
|
||||
status_list_v3(Status) ->
|
||||
status_list_v3(<<"all">>, Status).
|
||||
|
||||
|
||||
get_status_list(Host, Status_required) ->
|
||||
%% Get list of all logged users
|
||||
|
@ -2283,9 +2325,9 @@ web_page_node(_, Node, #request{path = [<<"stats">>]} = R) ->
|
|||
ejabberd_web_admin,
|
||||
make_command,
|
||||
[stats, R, [{<<"name">>, <<"uptimeseconds">>}], [{only, value}]]),
|
||||
UpDaysBin = integer_to_binary(
|
||||
binary_to_integer(fxml:get_tag_cdata(UpSecs))
|
||||
div 86400), % 24*60*60
|
||||
UpDaysBin =
|
||||
integer_to_binary(binary_to_integer(fxml:get_tag_cdata(UpSecs))
|
||||
div 86400), % 24*60*60
|
||||
UpDays =
|
||||
#xmlel{name = <<"code">>,
|
||||
attrs = [],
|
||||
|
|
167
src/mod_auth_fast.erl
Normal file
167
src/mod_auth_fast.erl
Normal file
|
@ -0,0 +1,167 @@
|
|||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_auth_fast.erl
|
||||
%%% Author : Pawel Chmielowski <pawel@process-one.net>
|
||||
%%% Created : 1 Dec 2024 by Pawel Chmielowski <pawel@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2024 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.
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(mod_auth_fast).
|
||||
-behaviour(gen_mod).
|
||||
-protocol({xep, 484, '0.2.0', '24.12', "complete", ""}).
|
||||
|
||||
%% gen_mod API
|
||||
-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,
|
||||
get_tokens/3, get_mechanisms/1]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include_lib("xmpp/include/scram.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-callback get_tokens(binary(), binary(), binary()) ->
|
||||
[{current | next, binary(), non_neg_integer()}].
|
||||
-callback rotate_token(binary(), binary(), binary()) ->
|
||||
ok | {error, atom()}.
|
||||
-callback del_token(binary(), binary(), binary(), current | next) ->
|
||||
ok | {error, atom()}.
|
||||
-callback set_token(binary(), binary(), binary(), current | next, binary(), non_neg_integer()) ->
|
||||
ok | {error, atom()}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
-spec start(binary(), gen_mod:opts()) -> {ok, [gen_mod:registration()]}.
|
||||
start(Host, Opts) ->
|
||||
Mod = gen_mod:db_mod(Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
{ok, [{hook, c2s_inline_features, c2s_inline_features, 50},
|
||||
{hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 10}]}.
|
||||
|
||||
-spec stop(binary()) -> ok.
|
||||
stop(_Host) ->
|
||||
ok.
|
||||
|
||||
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok.
|
||||
reload(Host, NewOpts, OldOpts) ->
|
||||
NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
|
||||
OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
|
||||
if NewMod /= OldMod ->
|
||||
NewMod:init(Host, NewOpts);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
ok.
|
||||
|
||||
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
-spec mod_opt_type(atom()) -> econf:validator().
|
||||
mod_opt_type(db_type) ->
|
||||
econf:db_type(?MODULE);
|
||||
mod_opt_type(token_lifetime) ->
|
||||
econf:timeout(second);
|
||||
mod_opt_type(token_refresh_age) ->
|
||||
econf:timeout(second).
|
||||
|
||||
-spec mod_options(binary()) -> [{atom(), any()}].
|
||||
mod_options(Host) ->
|
||||
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{token_lifetime, 30*24*60*60},
|
||||
{token_refresh_age, 24*60*60}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("The module adds support for "
|
||||
"https://xmpp.org/extensions/xep-0484.html"
|
||||
"[XEP-0480: Fast Authentication Streamlining Tokens] that allows users to authenticate "
|
||||
"using self managed tokens.")],
|
||||
note => "added in 24.12",
|
||||
opts =>
|
||||
[{db_type,
|
||||
#{value => "mnesia | sql",
|
||||
desc =>
|
||||
?T("Same as top-level _`default_db`_ option, but applied to this module only.")}},
|
||||
{token_lifetime,
|
||||
#{value => "timeout()",
|
||||
desc => ?T("Time that tokens will be keept, measured from it's creation time. "
|
||||
"Default value set to 30 days")}},
|
||||
{token_refresh_age,
|
||||
#{value => "timeout()",
|
||||
desc => ?T("This time determines age of token, that qualifies for automatic refresh. "
|
||||
"Default value set to 1 day")}}],
|
||||
example =>
|
||||
["modules:",
|
||||
" mod_auth_fast:",
|
||||
" token_timeout: 14days"]}.
|
||||
|
||||
get_mechanisms(_LServer) ->
|
||||
[<<"HT-SHA-256-NONE">>, <<"HT-SHA-256-UNIQ">>, <<"HT-SHA-256-EXPR">>, <<"HT-SHA-256-ENDP">>].
|
||||
|
||||
ua_hash(UA) ->
|
||||
crypto:hash(sha256, UA).
|
||||
|
||||
get_tokens(LServer, LUser, UA) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
ToRefresh = erlang:system_time(second) - mod_auth_fast_opt:token_refresh_age(LServer),
|
||||
lists:map(
|
||||
fun({Type, Token, CreatedAt}) ->
|
||||
{{Type, CreatedAt < ToRefresh}, Token}
|
||||
end, Mod:get_tokens(LServer, LUser, ua_hash(UA))).
|
||||
|
||||
c2s_inline_features({Sasl, Bind, Extra}, Host) ->
|
||||
{Sasl ++ [#fast{mechs = get_mechanisms(Host)}], Bind, Extra}.
|
||||
|
||||
gen_token(#{sasl2_ua_id := UA, server := Server, user := User}) ->
|
||||
Mod = gen_mod:db_mod(Server, ?MODULE),
|
||||
Token = base64:encode(ua_hash(<<UA/binary, (p1_rand:get_string())/binary>>)),
|
||||
ExpiresAt = erlang:system_time(second) + mod_auth_fast_opt:token_lifetime(Server),
|
||||
Mod:set_token(Server, User, ua_hash(UA), next, Token, ExpiresAt),
|
||||
#fast_token{token = Token, expiry = misc:usec_to_now(ExpiresAt*1000000)}.
|
||||
|
||||
c2s_handle_sasl2_inline({#{server := Server, user := User, sasl2_ua_id := UA,
|
||||
sasl2_axtra_auth_info := Extra} = State, Els, Results} = Acc) ->
|
||||
Mod = gen_mod:db_mod(Server, ?MODULE),
|
||||
NeedRegen =
|
||||
case Extra of
|
||||
{token, {next, Rotate}} ->
|
||||
Mod:rotate_token(Server, User, ua_hash(UA)),
|
||||
Rotate;
|
||||
{token, {_, true}} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
case {lists:keyfind(fast_request_token, 1, Els), lists:keyfind(fast, 1, Els)} of
|
||||
{#fast_request_token{mech = _Mech}, #fast{invalidate = true}} ->
|
||||
Mod:del_token(Server, User, ua_hash(UA), current),
|
||||
{State, Els, [gen_token(State) | Results]};
|
||||
{_, #fast{invalidate = true}} ->
|
||||
Mod:del_token(Server, User, ua_hash(UA), current),
|
||||
Acc;
|
||||
{#fast_request_token{mech = _Mech}, _} ->
|
||||
{State, Els, [gen_token(State) | Results]};
|
||||
_ when NeedRegen ->
|
||||
{State, Els, [gen_token(State) | Results]};
|
||||
_ ->
|
||||
Acc
|
||||
end.
|
123
src/mod_auth_fast_mnesia.erl
Normal file
123
src/mod_auth_fast_mnesia.erl
Normal file
|
@ -0,0 +1,123 @@
|
|||
%%%-------------------------------------------------------------------
|
||||
%%% File : mod_auth_fast_mnesia.erl
|
||||
%%% Author : Pawel Chmielowski <pawel@process-one.net>
|
||||
%%% Created : 1 Dec 2024 by Pawel Chmielowski <pawel@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2024 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.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_auth_fast_mnesia).
|
||||
|
||||
-behaviour(mod_auth_fast).
|
||||
|
||||
%% API
|
||||
-export([init/2]).
|
||||
-export([get_tokens/3, del_token/4, set_token/6, rotate_token/3]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(mod_auth_fast, {key = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary()} | '$1',
|
||||
token = <<>> :: binary() | '_',
|
||||
created_at = 0 :: non_neg_integer() | '_',
|
||||
expires_at = 0 :: non_neg_integer() | '_'}).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
init(_Host, _Opts) ->
|
||||
ejabberd_mnesia:create(?MODULE, mod_auth_fast,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes,
|
||||
record_info(fields, mod_auth_fast)}]).
|
||||
|
||||
-spec get_tokens(binary(), binary(), binary()) ->
|
||||
[{current | next, binary(), non_neg_integer()}].
|
||||
get_tokens(LServer, LUser, UA) ->
|
||||
Now = erlang:system_time(second),
|
||||
case mnesia:dirty_read(mod_auth_fast, {LServer, LUser, token_id(UA, next)}) of
|
||||
[#mod_auth_fast{token = Token, created_at = Created, expires_at = Expires}] when Expires > Now ->
|
||||
[{next, Token, Created}];
|
||||
[#mod_auth_fast{}] ->
|
||||
del_token(LServer, LUser, UA, next),
|
||||
[];
|
||||
_ ->
|
||||
[]
|
||||
end ++
|
||||
case mnesia:dirty_read(mod_auth_fast, {LServer, LUser, token_id(UA, current)}) of
|
||||
[#mod_auth_fast{token = Token, created_at = Created, expires_at = Expires}] when Expires > Now ->
|
||||
[{current, Token, Created}];
|
||||
[#mod_auth_fast{}] ->
|
||||
del_token(LServer, LUser, UA, current),
|
||||
[];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec rotate_token(binary(), binary(), binary()) ->
|
||||
ok | {error, atom()}.
|
||||
rotate_token(LServer, LUser, UA) ->
|
||||
F = fun() ->
|
||||
case mnesia:dirty_read(mod_auth_fast, {LServer, LUser, token_id(UA, next)}) of
|
||||
[#mod_auth_fast{token = Token, created_at = Created, expires_at = Expires}] ->
|
||||
mnesia:write(#mod_auth_fast{key = {LServer, LUser, token_id(UA, current)},
|
||||
token = Token, created_at = Created,
|
||||
expires_at = Expires}),
|
||||
mnesia:delete({mod_auth_fast, {LServer, LUser, token_id(UA, next)}});
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end,
|
||||
transaction(F).
|
||||
|
||||
-spec del_token(binary(), binary(), binary(), current | next) ->
|
||||
ok | {error, atom()}.
|
||||
del_token(LServer, LUser, UA, Type) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({mod_auth_fast, {LServer, LUser, token_id(UA, Type)}})
|
||||
end,
|
||||
transaction(F).
|
||||
|
||||
-spec set_token(binary(), binary(), binary(), current | next, binary(), non_neg_integer()) ->
|
||||
ok | {error, atom()}.
|
||||
set_token(LServer, LUser, UA, Type, Token, Expires) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#mod_auth_fast{key = {LServer, LUser, token_id(UA, Type)},
|
||||
token = Token, created_at = erlang:system_time(second),
|
||||
expires_at = Expires})
|
||||
end,
|
||||
transaction(F).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
||||
token_id(UA, current) ->
|
||||
<<"c:", UA/binary>>;
|
||||
token_id(UA, _) ->
|
||||
<<"n:", UA/binary>>.
|
||||
|
||||
transaction(F) ->
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||
{error, db_failure}
|
||||
end.
|
27
src/mod_auth_fast_opt.erl
Normal file
27
src/mod_auth_fast_opt.erl
Normal file
|
@ -0,0 +1,27 @@
|
|||
%% Generated automatically
|
||||
%% DO NOT EDIT: run `make options` instead
|
||||
|
||||
-module(mod_auth_fast_opt).
|
||||
|
||||
-export([db_type/1]).
|
||||
-export([token_lifetime/1]).
|
||||
-export([token_refresh_age/1]).
|
||||
|
||||
-spec db_type(gen_mod:opts() | global | binary()) -> atom().
|
||||
db_type(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(db_type, Opts);
|
||||
db_type(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_auth_fast, db_type).
|
||||
|
||||
-spec token_lifetime(gen_mod:opts() | global | binary()) -> pos_integer().
|
||||
token_lifetime(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(token_lifetime, Opts);
|
||||
token_lifetime(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_auth_fast, token_lifetime).
|
||||
|
||||
-spec token_refresh_age(gen_mod:opts() | global | binary()) -> pos_integer().
|
||||
token_refresh_age(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(token_refresh_age, Opts);
|
||||
token_refresh_age(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_auth_fast, token_refresh_age).
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
-export([start/2, stop/1, reload/3, process/2, depends/2,
|
||||
format_arg/2,
|
||||
mod_options/1, mod_doc/0]).
|
||||
mod_opt_type/1, mod_options/1, mod_doc/0]).
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
|
@ -201,19 +201,25 @@ extract_args(Data) ->
|
|||
maps:to_list(Maps).
|
||||
|
||||
% get API version N from last "vN" element in URL path
|
||||
get_api_version(#request{path = Path}) ->
|
||||
get_api_version(lists:reverse(Path));
|
||||
get_api_version([<<"v", String/binary>> | Tail]) ->
|
||||
get_api_version(#request{path = Path, host = Host}) ->
|
||||
get_api_version(lists:reverse(Path), Host).
|
||||
|
||||
get_api_version([<<"v", String/binary>> | Tail], Host) ->
|
||||
case catch binary_to_integer(String) of
|
||||
N when is_integer(N) ->
|
||||
N;
|
||||
_ ->
|
||||
get_api_version(Tail)
|
||||
get_api_version(Tail, Host)
|
||||
end;
|
||||
get_api_version([_Head | Tail]) ->
|
||||
get_api_version(Tail);
|
||||
get_api_version([]) ->
|
||||
?DEFAULT_API_VERSION.
|
||||
get_api_version([_Head | Tail], Host) ->
|
||||
get_api_version(Tail, Host);
|
||||
get_api_version([], Host) ->
|
||||
try mod_http_api_opt:default_version(Host)
|
||||
catch error:{module_not_loaded, ?MODULE, Host} ->
|
||||
?WARNING_MSG("Using module ~p for host ~s, but it isn't configured "
|
||||
"in the configuration file", [?MODULE, Host]),
|
||||
?DEFAULT_API_VERSION
|
||||
end.
|
||||
|
||||
%% ----------------
|
||||
%% command handlers
|
||||
|
@ -549,8 +555,24 @@ hide_sensitive_args(Args=[_H|_T]) ->
|
|||
hide_sensitive_args(NonListArgs) ->
|
||||
NonListArgs.
|
||||
|
||||
mod_opt_type(default_version) ->
|
||||
econf:either(
|
||||
econf:int(0, 3),
|
||||
econf:and_then(
|
||||
econf:binary(),
|
||||
fun(Binary) ->
|
||||
case binary_to_list(Binary) of
|
||||
F when F >= "24.06" ->
|
||||
2;
|
||||
F when (F > "23.10") and (F < "24.06") ->
|
||||
1;
|
||||
F when F =< "23.10" ->
|
||||
0
|
||||
end
|
||||
end)).
|
||||
|
||||
mod_options(_) ->
|
||||
[].
|
||||
[{default_version, ?DEFAULT_API_VERSION}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
|
@ -565,6 +587,15 @@ mod_doc() ->
|
|||
"For example: '/api/v2: mod_http_api'."), "",
|
||||
?T("To run a command, send a POST request to the corresponding "
|
||||
"URL: 'http://localhost:5280/api/COMMAND-NAME'")],
|
||||
opts =>
|
||||
[{default_version,
|
||||
#{value => "integer() | string()",
|
||||
desc =>
|
||||
?T("What API version to use when none is specified in the URL path. "
|
||||
"If setting an ejabberd version, it will use the latest API "
|
||||
"version that was available in that ejabberd version. "
|
||||
"For example, setting '\"24.06\"' in this option implies '2'. "
|
||||
"The default value is the latest version.")}}],
|
||||
example =>
|
||||
["listen:",
|
||||
" -",
|
||||
|
@ -574,4 +605,5 @@ mod_doc() ->
|
|||
" /api: mod_http_api",
|
||||
"",
|
||||
"modules:",
|
||||
" mod_http_api: {}"]}.
|
||||
" mod_http_api:",
|
||||
" default_version: 2"]}.
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
-module(mod_http_api_opt).
|
||||
|
||||
-export([admin_ip_access/1]).
|
||||
-export([default_version/1]).
|
||||
|
||||
-spec admin_ip_access(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
|
||||
admin_ip_access(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(admin_ip_access, Opts);
|
||||
admin_ip_access(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_http_api, admin_ip_access).
|
||||
-spec default_version(gen_mod:opts() | global | binary()) -> any().
|
||||
default_version(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(default_version, Opts);
|
||||
default_version(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_http_api, default_version).
|
||||
|
||||
|
|
|
@ -994,15 +994,38 @@ iq_disco_items(ServerHost, Host, From, Lang, MaxRoomsDiscoItems, Node, RSM)
|
|||
#rsm_set{max = Max} ->
|
||||
Max
|
||||
end,
|
||||
{Items, HitMax} = lists:foldr(
|
||||
fun(_, {Acc, _}) when length(Acc) >= MaxItems ->
|
||||
{Acc, true};
|
||||
(R, {Acc, _}) ->
|
||||
case get_room_disco_item(R, Query) of
|
||||
{ok, Item} -> {[Item | Acc], false};
|
||||
{error, _} -> {Acc, false}
|
||||
RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
|
||||
RsmSupported = RMod:rsm_supported(),
|
||||
GetRooms =
|
||||
fun GetRooms(AccInit, Rooms) ->
|
||||
{Items, HitMax, DidSkip, Last, First} = lists:foldr(
|
||||
fun(_, {Acc, _, Skip, F, L}) when length(Acc) >= MaxItems ->
|
||||
{Acc, true, Skip, F, L};
|
||||
({RN, _, _} = R, {Acc, _, Skip, F, _}) ->
|
||||
F2 = if F == undefined -> RN; true -> F end,
|
||||
case get_room_disco_item(R, Query) of
|
||||
{ok, Item} -> {[Item | Acc], false, Skip, F2, RN};
|
||||
{error, _} -> {Acc, false, true, F2, RN}
|
||||
end
|
||||
end, AccInit, Rooms),
|
||||
if RsmSupported andalso not HitMax andalso DidSkip ->
|
||||
RSM2 = case RSM of
|
||||
#rsm_set{'after' = undefined, before = undefined} ->
|
||||
#rsm_set{max = MaxItems - length(Items), 'after' = Last};
|
||||
#rsm_set{'after' = undefined} ->
|
||||
#rsm_set{max = MaxItems - length(Items), 'before' = First};
|
||||
_ ->
|
||||
#rsm_set{max = MaxItems - length(Items), 'after' = Last}
|
||||
end,
|
||||
GetRooms({Items, false, false, undefined, undefined},
|
||||
get_online_rooms(ServerHost, Host, RSM2));
|
||||
true -> {Items, HitMax}
|
||||
end
|
||||
end, {[], false}, get_online_rooms(ServerHost, Host, RSM)),
|
||||
end,
|
||||
|
||||
{Items, HitMax} =
|
||||
GetRooms({[], false, false, undefined, undefined},
|
||||
get_online_rooms(ServerHost, Host, RSM)),
|
||||
ResRSM = case Items of
|
||||
[_|_] when RSM /= undefined; HitMax ->
|
||||
#disco_item{jid = #jid{luser = First}} = hd(Items),
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
|
||||
-export([start/2, stop/1, reload/3, depends/2, mod_doc/0,
|
||||
muc_online_rooms/1, muc_online_rooms_by_regex/2,
|
||||
muc_register_nick/3, muc_unregister_nick/2,
|
||||
muc_register_nick/3, muc_register_nick/4,
|
||||
muc_unregister_nick/2, muc_unregister_nick/3,
|
||||
create_room_with_opts/4, create_room/3, destroy_room/2,
|
||||
create_rooms_file/1, destroy_rooms_file/1,
|
||||
rooms_unused_list/2, rooms_unused_destroy/2,
|
||||
|
@ -38,9 +39,11 @@
|
|||
get_user_rooms/2, get_user_subscriptions/2, get_room_occupants/2,
|
||||
get_room_occupants_number/2, send_direct_invitation/5,
|
||||
change_room_option/4, get_room_options/2,
|
||||
set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
|
||||
subscribe_room/4, subscribe_room_many/3,
|
||||
unsubscribe_room/2, get_subscribers/2,
|
||||
set_room_affiliation/4, set_room_affiliation/5, get_room_affiliations/2,
|
||||
get_room_affiliations_v3/2, get_room_affiliation/3,
|
||||
subscribe_room/4, subscribe_room/6,
|
||||
subscribe_room_many/3, subscribe_room_many_v3/4,
|
||||
unsubscribe_room/2, unsubscribe_room/4, get_subscribers/2,
|
||||
get_room_serverhost/1,
|
||||
web_menu_main/2, web_page_main/2,
|
||||
web_menu_host/3, web_page_host/3,
|
||||
|
@ -102,7 +105,7 @@ get_commands_spec() ->
|
|||
module = ?MODULE, function = muc_online_rooms,
|
||||
args_desc = ["MUC service, or `global` for all"],
|
||||
args_example = ["conference.example.com"],
|
||||
result_desc = "List of rooms",
|
||||
result_desc = "List of rooms JIDs",
|
||||
result_example = ["room1@conference.example.com", "room2@conference.example.com"],
|
||||
args = [{service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
|
@ -133,6 +136,16 @@ get_commands_spec() ->
|
|||
args = [{nick, binary}, {jid, binary}, {service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = muc_register_nick, tags = [muc],
|
||||
desc = "Register a nick to a User JID in a MUC service",
|
||||
module = ?MODULE, function = muc_register_nick,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args_desc = ["nick", "user name", "user host", "MUC service"],
|
||||
args_example = [<<"Tim">>, <<"tim">>, <<"example.org">>, <<"conference.example.org">>],
|
||||
args = [{nick, binary}, {user, binary}, {host, binary}, {service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = muc_unregister_nick, tags = [muc],
|
||||
desc = "Unregister the nick registered by that account in the MUC service",
|
||||
module = ?MODULE, function = muc_unregister_nick,
|
||||
|
@ -141,26 +154,38 @@ get_commands_spec() ->
|
|||
args = [{jid, binary}, {service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = muc_unregister_nick, tags = [muc],
|
||||
desc = "Unregister the nick registered by that account in the MUC service",
|
||||
module = ?MODULE, function = muc_unregister_nick,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args_desc = ["user name", "user host", "MUC service"],
|
||||
args_example = [<<"tim">>, <<"example.org">>, <<"conference.example.org">>],
|
||||
args = [{user, binary}, {host, binary}, {service, binary}],
|
||||
args_rename = [{host, service}],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = create_room, tags = [muc_room],
|
||||
desc = "Create a MUC room name@service in host",
|
||||
module = ?MODULE, function = create_room,
|
||||
args_desc = ["Room name", "MUC service", "Server host"],
|
||||
args_example = ["room1", "conference.example.com", "example.com"],
|
||||
args = [{name, binary}, {service, binary},
|
||||
args = [{room, binary}, {service, binary},
|
||||
{host, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = destroy_room, tags = [muc_room],
|
||||
desc = "Destroy a MUC room",
|
||||
module = ?MODULE, function = destroy_room,
|
||||
args_desc = ["Room name", "MUC service"],
|
||||
args_example = ["room1", "conference.example.com"],
|
||||
args = [{name, binary}, {service, binary}],
|
||||
args = [{room, binary}, {service, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = create_rooms_file, tags = [muc],
|
||||
desc = "Create the rooms indicated in file",
|
||||
longdesc = "Provide one room JID per line. Rooms will be created after restart.",
|
||||
note = "improved in 24.xx",
|
||||
note = "improved in 24.12",
|
||||
module = ?MODULE, function = create_rooms_file,
|
||||
args_desc = ["Path to the text file with one room JID per line"],
|
||||
args_example = ["/home/ejabberd/rooms.txt"],
|
||||
|
@ -177,7 +202,7 @@ get_commands_spec() ->
|
|||
[{"members_only","true"},
|
||||
{"affiliations", "owner:bob@example.com,member:peter@example.com"},
|
||||
{"subscribers", "bob@example.com:Bob:messages:subject,anne@example.com:Anne:messages"}]],
|
||||
args = [{name, binary}, {service, binary},
|
||||
args = [{room, binary}, {service, binary},
|
||||
{host, binary},
|
||||
{options, {list,
|
||||
{option, {tuple,
|
||||
|
@ -185,6 +210,7 @@ get_commands_spec() ->
|
|||
{value, binary}
|
||||
]}}
|
||||
}}],
|
||||
args_rename = [{name, room}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = destroy_rooms_file, tags = [muc],
|
||||
desc = "Destroy the rooms indicated in file",
|
||||
|
@ -290,7 +316,8 @@ get_commands_spec() ->
|
|||
args_example = ["room1", "conference.example.com"],
|
||||
result_desc = "The list of occupants with JID, nick and affiliation",
|
||||
result_example = [{"user1@example.com/psi", "User 1", "owner"}],
|
||||
args = [{name, binary}, {service, binary}],
|
||||
args = [{room, binary}, {service, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {occupants, {list,
|
||||
{occupant, {tuple,
|
||||
[{jid, string},
|
||||
|
@ -306,7 +333,8 @@ get_commands_spec() ->
|
|||
args_example = ["room1", "conference.example.com"],
|
||||
result_desc = "Number of room occupants",
|
||||
result_example = 7,
|
||||
args = [{name, binary}, {service, binary}],
|
||||
args = [{room, binary}, {service, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {occupants, integer}},
|
||||
|
||||
#ejabberd_commands{name = send_direct_invitation, tags = [muc_room],
|
||||
|
@ -322,8 +350,9 @@ get_commands_spec() ->
|
|||
args_example = [<<"room1">>, <<"conference.example.com">>,
|
||||
<<>>, <<"Check this out!">>,
|
||||
"user2@localhost:user3@example.com"],
|
||||
args = [{name, binary}, {service, binary}, {password, binary},
|
||||
args = [{room, binary}, {service, binary}, {password, binary},
|
||||
{reason, binary}, {users, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = send_direct_invitation, tags = [muc_room],
|
||||
desc = "Send a direct invitation to several destinations",
|
||||
|
@ -339,8 +368,9 @@ get_commands_spec() ->
|
|||
args_example = [<<"room1">>, <<"conference.example.com">>,
|
||||
<<>>, <<"Check this out!">>,
|
||||
["user2@localhost", "user3@example.com"]],
|
||||
args = [{name, binary}, {service, binary}, {password, binary},
|
||||
args = [{room, binary}, {service, binary}, {password, binary},
|
||||
{reason, binary}, {users, {list, {jid, binary}}}],
|
||||
args_rename = [{name, room}],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = change_room_option, tags = [muc_room],
|
||||
|
@ -348,8 +378,9 @@ get_commands_spec() ->
|
|||
module = ?MODULE, function = change_room_option,
|
||||
args_desc = ["Room name", "MUC service", "Option name", "Value to assign"],
|
||||
args_example = ["room1", "conference.example.com", "members_only", "true"],
|
||||
args = [{name, binary}, {service, binary},
|
||||
args = [{room, binary}, {service, binary},
|
||||
{option, binary}, {value, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = get_room_options, tags = [muc_room],
|
||||
desc = "Get options from a MUC room",
|
||||
|
@ -358,7 +389,8 @@ get_commands_spec() ->
|
|||
args_example = ["room1", "conference.example.com"],
|
||||
result_desc = "List of room options tuples with name and value",
|
||||
result_example = [{"members_only", "true"}],
|
||||
args = [{name, binary}, {service, binary}],
|
||||
args = [{room, binary}, {service, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {options, {list,
|
||||
{option, {tuple,
|
||||
[{name, string},
|
||||
|
@ -393,6 +425,21 @@ get_commands_spec() ->
|
|||
args = [{user, binary}, {nick, binary}, {room, binary},
|
||||
{nodes, {list, {node, binary}}}],
|
||||
result = {nodes, {list, {node, string}}}},
|
||||
#ejabberd_commands{name = subscribe_room, tags = [muc_room, muc_sub],
|
||||
desc = "Subscribe to a MUC conference",
|
||||
module = ?MODULE, function = subscribe_room,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args_desc = ["user name", "user host", "user nick",
|
||||
"room name", "MUC service", "list of nodes"],
|
||||
args_example = ["tom", "localhost", "Tom", "room1", "conference.localhost",
|
||||
["urn:xmpp:mucsub:nodes:messages", "urn:xmpp:mucsub:nodes:affiliations"]],
|
||||
result_desc = "The list of nodes that has subscribed",
|
||||
result_example = ["urn:xmpp:mucsub:nodes:messages",
|
||||
"urn:xmpp:mucsub:nodes:affiliations"],
|
||||
args = [{user, binary}, {host, binary}, {nick, binary}, {room, binary},
|
||||
{service, binary}, {nodes, {list, {node, binary}}}],
|
||||
result = {nodes, {list, {node, string}}}},
|
||||
#ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub],
|
||||
desc = "Subscribe several users to a MUC conference",
|
||||
note = "added in 22.05",
|
||||
|
@ -440,6 +487,32 @@ get_commands_spec() ->
|
|||
{room, binary},
|
||||
{nodes, {list, {node, binary}}}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub],
|
||||
desc = "Subscribe several users to a MUC conference",
|
||||
longdesc = "This command accepts up to 50 users at once "
|
||||
"(this is configurable with the _`mod_muc_admin`_ option "
|
||||
"`subscribe_room_many_max_users`)",
|
||||
module = ?MODULE, function = subscribe_room_many_v3,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args_desc = ["List of tuples with users name, host and nick",
|
||||
"room name",
|
||||
"MUC service",
|
||||
"nodes separated by commas: `,`"],
|
||||
args_example = [[{"tom", "localhost", "Tom"},
|
||||
{"jerry", "localhost", "Jerry"}],
|
||||
"room1", "conference.localhost",
|
||||
["urn:xmpp:mucsub:nodes:messages", "urn:xmpp:mucsub:nodes:affiliations"]],
|
||||
args = [{users, {list,
|
||||
{user, {tuple,
|
||||
[{user, binary},
|
||||
{host, binary},
|
||||
{nick, binary}
|
||||
]}}
|
||||
}},
|
||||
{room, binary}, {service, binary},
|
||||
{nodes, {list, {node, binary}}}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = unsubscribe_room, tags = [muc_room, muc_sub],
|
||||
desc = "Unsubscribe from a MUC conference",
|
||||
module = ?MODULE, function = unsubscribe_room,
|
||||
|
@ -447,6 +520,15 @@ get_commands_spec() ->
|
|||
args_example = ["tom@localhost", "room1@conference.localhost"],
|
||||
args = [{user, binary}, {room, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = unsubscribe_room, tags = [muc_room, muc_sub],
|
||||
desc = "Unsubscribe from a MUC conference",
|
||||
module = ?MODULE, function = unsubscribe_room,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args_desc = ["user name", "user host", "room name", "MUC service"],
|
||||
args_example = ["tom", "localhost", "room1", "conference.localhost"],
|
||||
args = [{user, binary}, {host, binary}, {room, binary}, {service, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = get_subscribers, tags = [muc_room, muc_sub],
|
||||
desc = "List subscribers of a MUC conference",
|
||||
module = ?MODULE, function = get_subscribers,
|
||||
|
@ -454,7 +536,8 @@ get_commands_spec() ->
|
|||
args_example = ["room1", "conference.example.com"],
|
||||
result_desc = "The list of users that are subscribed to that room",
|
||||
result_example = ["user2@example.com", "user3@example.com"],
|
||||
args = [{name, binary}, {service, binary}],
|
||||
args = [{room, binary}, {service, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {subscribers, {list, {jid, string}}}},
|
||||
#ejabberd_commands{name = set_room_affiliation, tags = [muc_room],
|
||||
desc = "Change an affiliation in a MUC room",
|
||||
|
@ -464,6 +547,19 @@ get_commands_spec() ->
|
|||
args = [{name, binary}, {service, binary},
|
||||
{jid, binary}, {affiliation, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = set_room_affiliation, tags = [muc_room],
|
||||
desc = "Change an affiliation in a MUC room",
|
||||
longdesc = "If affiliation is `none`, then the affiliation is removed.",
|
||||
module = ?MODULE, function = set_room_affiliation,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args_desc = ["room name", "MUC service", "user name", "user host", "affiliation to set"],
|
||||
args_example = ["room1", "conference.example.com", "sun", "localhost", "member"],
|
||||
args = [{room, binary}, {service, binary},
|
||||
{user, binary}, {host, binary}, {affiliation, binary}],
|
||||
result = {res, rescode}},
|
||||
|
||||
|
||||
#ejabberd_commands{name = get_room_affiliations, tags = [muc_room],
|
||||
desc = "Get the list of affiliations of a MUC room",
|
||||
module = ?MODULE, function = get_room_affiliations,
|
||||
|
@ -480,6 +576,25 @@ get_commands_spec() ->
|
|||
{reason, string}
|
||||
]}}
|
||||
}}},
|
||||
#ejabberd_commands{name = get_room_affiliations, tags = [muc_room],
|
||||
desc = "Get the list of affiliations of a MUC room",
|
||||
module = ?MODULE, function = get_room_affiliations_v3,
|
||||
version = 3,
|
||||
note = "updated in 24.12",
|
||||
args_desc = ["Room name", "MUC service"],
|
||||
args_example = ["room1", "conference.example.com"],
|
||||
result_desc = "The list of affiliations with jid, affiliation and reason",
|
||||
result_example = [{"user1@example.com", member, "member"}],
|
||||
args = [{room, binary}, {service, binary}],
|
||||
result = {affiliations, {list,
|
||||
{affiliation, {tuple,
|
||||
[{jid, string},
|
||||
{affiliation, atom},
|
||||
{reason, string}
|
||||
]}}
|
||||
}}},
|
||||
|
||||
|
||||
#ejabberd_commands{name = get_room_affiliation, tags = [muc_room],
|
||||
desc = "Get affiliation of a user in MUC room",
|
||||
module = ?MODULE, function = get_room_affiliation,
|
||||
|
@ -487,7 +602,8 @@ get_commands_spec() ->
|
|||
args_example = ["room1", "conference.example.com", "user1@example.com"],
|
||||
result_desc = "Affiliation of the user",
|
||||
result_example = member,
|
||||
args = [{name, binary}, {service, binary}, {jid, binary}],
|
||||
args = [{room, binary}, {service, binary}, {jid, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {affiliation, atom}},
|
||||
#ejabberd_commands{name = get_room_history, tags = [muc_room],
|
||||
desc = "Get history of messages stored inside MUC room state",
|
||||
|
@ -495,7 +611,8 @@ get_commands_spec() ->
|
|||
module = ?MODULE, function = get_room_history,
|
||||
args_desc = ["Room name", "MUC service"],
|
||||
args_example = ["room1", "conference.example.com"],
|
||||
args = [{name, binary}, {service, binary}],
|
||||
args = [{room, binary}, {service, binary}],
|
||||
args_rename = [{name, room}],
|
||||
result = {history, {list,
|
||||
{entry, {tuple,
|
||||
[{timestamp, string},
|
||||
|
@ -547,6 +664,9 @@ build_summary_room(Name, Host, Pid) ->
|
|||
Participants
|
||||
}.
|
||||
|
||||
muc_register_nick(Nick, User, Host, Service) ->
|
||||
muc_register_nick(Nick, makeencode(User, Host), Service).
|
||||
|
||||
muc_register_nick(Nick, FromBinary, Service) ->
|
||||
try {get_room_serverhost(Service), jid:decode(FromBinary)} of
|
||||
{ServerHost, From} ->
|
||||
|
@ -569,6 +689,9 @@ muc_register_nick(Nick, FromBinary, Service) ->
|
|||
throw({error, "Internal error"})
|
||||
end.
|
||||
|
||||
muc_unregister_nick(User, Host, Service) ->
|
||||
muc_unregister_nick(makeencode(User, Host), Service).
|
||||
|
||||
muc_unregister_nick(FromBinary, Service) ->
|
||||
muc_register_nick(<<"">>, FromBinary, Service).
|
||||
|
||||
|
@ -728,12 +851,13 @@ webadmin_muc_host(_Host,
|
|||
make_breadcrumb({room_section, Level, Service, <<"Affiliations">>, Name, R, RPath}),
|
||||
Set = [make_command(set_room_affiliation,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Get = [make_command(get_room_affiliations,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {20, RPath}}])],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {20, RPath}},
|
||||
{result_links, [{jid, user, 3 + Level, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Get ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
Service,
|
||||
|
@ -747,7 +871,7 @@ webadmin_muc_host(_Host,
|
|||
make_breadcrumb({room_section, Level, Service, <<"History">>, Name, R, RPath}),
|
||||
Get = [make_command(get_room_history,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {10, RPath}},
|
||||
{result_links, [{message, paragraph, 1, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Get;
|
||||
|
@ -763,7 +887,7 @@ webadmin_muc_host(_Host,
|
|||
make_breadcrumb({room_section, Level, Service, <<"Invite">>, Name, R, RPath}),
|
||||
Set = [make_command(send_direct_invitation,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
|
@ -778,7 +902,7 @@ webadmin_muc_host(_Host,
|
|||
make_breadcrumb({room_section, Level, Service, <<"Occupants">>, Name, R, RPath}),
|
||||
Get = [make_command(get_room_occupants,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {20, RPath}},
|
||||
{result_links, [{jid, user, 3 + Level, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Get;
|
||||
|
@ -794,11 +918,11 @@ webadmin_muc_host(_Host,
|
|||
make_breadcrumb({room_section, Level, Service, <<"Options">>, Name, R, RPath}),
|
||||
Set = [make_command(change_room_option,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Get = [make_command(get_room_options,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[])],
|
||||
Title ++ Breadcrumb ++ Get ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
|
@ -816,15 +940,15 @@ webadmin_muc_host(_Host,
|
|||
make_breadcrumb({room_section, Level, Service, <<"Subscribers">>, Name, R, RPath}),
|
||||
Set = [make_command(subscribe_room,
|
||||
R,
|
||||
[{<<"room">>, jid:encode({Name, Service, <<"">>})}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[]),
|
||||
make_command(unsubscribe_room,
|
||||
R,
|
||||
[{<<"room">>, jid:encode({Name, Service, <<"">>})}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[{style, danger}])],
|
||||
Get = [make_command(get_subscribers,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[{table_options, {20, RPath}},
|
||||
{result_links, [{jid, user, 3 + Level, <<"">>}]}])],
|
||||
Title ++ Breadcrumb ++ Get ++ Set;
|
||||
|
@ -840,7 +964,7 @@ webadmin_muc_host(_Host,
|
|||
make_breadcrumb({room_section, Level, Service, <<"Destroy">>, Name, R, RPath}),
|
||||
Set = [make_command(destroy_room,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[{style, danger}])],
|
||||
Title ++ Breadcrumb ++ Set;
|
||||
webadmin_muc_host(_Host,
|
||||
|
@ -876,7 +1000,7 @@ webadmin_muc_host(_Host, Service, [<<"rooms">> | RPath], R, _Lang, Level, PageTi
|
|||
{result_links, [{sentence, room, 3 + Level, <<"">>}]}]),
|
||||
make_command(get_room_occupants_number,
|
||||
R,
|
||||
[{<<"name">>, Name}, {<<"service">>, Service}],
|
||||
[{<<"room">>, Name}, {<<"service">>, Service}],
|
||||
[{only, raw_and_value}])}
|
||||
end,
|
||||
make_command_raw_value(muc_online_rooms, R, [{<<"service">>, Service}])),
|
||||
|
@ -1149,7 +1273,7 @@ create_room(Name1, Host1, ServerHost) ->
|
|||
|
||||
create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) ->
|
||||
ServerHost = validate_host(ServerHost1, <<"serverhost">>),
|
||||
case get_room_pid_validate(Name1, Host1, <<"name">>, <<"host">>) of
|
||||
case get_room_pid_validate(Name1, Host1, <<"service">>) of
|
||||
{room_not_found, Name, Host} ->
|
||||
%% Get the default room options from the muc configuration
|
||||
DefRoomOpts = mod_muc_opt:default_room_options(ServerHost),
|
||||
|
@ -1179,7 +1303,7 @@ muc_create_room(ServerHost, {Name, Host, _}, DefRoomOpts) ->
|
|||
%% If the room has participants, they are not notified that the room was destroyed;
|
||||
%% they will notice when they try to chat and receive an error that the room doesn't exist.
|
||||
destroy_room(Name1, Service1) ->
|
||||
case get_room_pid_validate(Name1, Service1, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name1, Service1, <<"service">>) of
|
||||
{room_not_found, _, _} ->
|
||||
throw({error, "Room doesn't exists"});
|
||||
{Pid, _, _} ->
|
||||
|
@ -1475,7 +1599,7 @@ act_on_room(_Method, list, _) ->
|
|||
%%----------------------------
|
||||
|
||||
get_room_occupants(Room, Host) ->
|
||||
case get_room_pid_validate(Room, Host, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Room, Host, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) -> get_room_occupants(Pid);
|
||||
_ -> throw({error, room_not_found})
|
||||
end.
|
||||
|
@ -1491,7 +1615,7 @@ get_room_occupants(Pid) ->
|
|||
maps:to_list(S#state.users)).
|
||||
|
||||
get_room_occupants_number(Room, Host) ->
|
||||
case get_room_pid_validate(Room, Host, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Room, Host, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid)->
|
||||
{ok, #{occupants_number := N}} = mod_muc_room:get_info(Pid),
|
||||
N;
|
||||
|
@ -1570,7 +1694,7 @@ send_direct_invitation(FromJid, UserJid, Msg) ->
|
|||
%% For example:
|
||||
%% `change_room_option(<<"testroom">>, <<"conference.localhost">>, <<"title">>, <<"Test Room">>)'
|
||||
change_room_option(Name, Service, OptionString, ValueString) ->
|
||||
case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name, Service, <<"service">>) of
|
||||
{room_not_found, _, _} ->
|
||||
throw({error, "Room not found"});
|
||||
{Pid, _, _} ->
|
||||
|
@ -1667,10 +1791,10 @@ parse_nodes([<<"subscribers">> | Rest], Acc) ->
|
|||
parse_nodes(_, _) ->
|
||||
throw({error, "Invalid 'subscribers' - unknown node name used"}).
|
||||
|
||||
-spec get_room_pid_validate(binary(), binary(), binary(), binary()) ->
|
||||
-spec get_room_pid_validate(binary(), binary(), binary()) ->
|
||||
{pid() | room_not_found, binary(), binary()}.
|
||||
get_room_pid_validate(Name, Service, NameArg, ServiceArg) ->
|
||||
Name2 = validate_room(Name, NameArg),
|
||||
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 ->
|
||||
|
@ -1761,7 +1885,7 @@ change_option(Option, Value, Config) ->
|
|||
%%----------------------------
|
||||
|
||||
get_room_options(Name, Service) ->
|
||||
case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name, Service, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) -> get_room_options(Pid);
|
||||
_ -> []
|
||||
end.
|
||||
|
@ -1784,10 +1908,10 @@ get_options(Config) ->
|
|||
%%----------------------------
|
||||
|
||||
%% @spec(Name::binary(), Service::binary()) ->
|
||||
%% [{JID::string(), Domain::string(), Role::string(), Reason::string()}]
|
||||
%% [{Username::string(), Domain::string(), Role::string(), Reason::string()}]
|
||||
%% @doc Get the affiliations of the room Name@Service.
|
||||
get_room_affiliations(Name, Service) ->
|
||||
case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name, Service, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
%% Get the PID of the online room, then request its state
|
||||
{ok, StateData} = mod_muc_room:get_state(Pid),
|
||||
|
@ -1802,8 +1926,29 @@ get_room_affiliations(Name, Service) ->
|
|||
throw({error, "The room does not exist."})
|
||||
end.
|
||||
|
||||
%% @spec(Name::binary(), Service::binary()) ->
|
||||
%% [{JID::string(), Role::string(), Reason::string()}]
|
||||
%% @doc Get the affiliations of the room Name@Service.
|
||||
get_room_affiliations_v3(Name, Service) ->
|
||||
case get_room_pid_validate(Name, Service, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
%% Get the PID of the online room, then request its state
|
||||
{ok, StateData} = mod_muc_room:get_state(Pid),
|
||||
Affiliations = maps:to_list(StateData#state.affiliations),
|
||||
lists:map(
|
||||
fun({{Uname, Domain, _Res}, {Aff, Reason}}) when is_atom(Aff)->
|
||||
Jid = makeencode(Uname, Domain),
|
||||
{Jid, Aff, Reason};
|
||||
({{Uname, Domain, _Res}, Aff}) when is_atom(Aff)->
|
||||
Jid = makeencode(Uname, Domain),
|
||||
{Jid, Aff, <<>>}
|
||||
end, Affiliations);
|
||||
_ ->
|
||||
throw({error, "The room does not exist."})
|
||||
end.
|
||||
|
||||
get_room_history(Name, Service) ->
|
||||
case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name, Service, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
case mod_muc_room:get_state(Pid) of
|
||||
{ok, StateData} ->
|
||||
|
@ -1829,7 +1974,7 @@ get_room_history(Name, Service) ->
|
|||
%% @doc Get affiliation of a user in the room Name@Service.
|
||||
|
||||
get_room_affiliation(Name, Service, JID) ->
|
||||
case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name, Service, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
%% Get the PID of the online room, then request its state
|
||||
{ok, StateData} = mod_muc_room:get_state(Pid),
|
||||
|
@ -1843,6 +1988,9 @@ get_room_affiliation(Name, Service, JID) ->
|
|||
%% Change Room Affiliation
|
||||
%%----------------------------
|
||||
|
||||
set_room_affiliation(Name, Service, User, Host, AffiliationString) ->
|
||||
set_room_affiliation(Name, Service, makeencode(User, Host), AffiliationString).
|
||||
|
||||
%% @spec(Name, Service, JID, AffiliationString) -> ok | {error, Error}
|
||||
%% Name = binary()
|
||||
%% Service = binary()
|
||||
|
@ -1861,7 +2009,7 @@ set_room_affiliation(Name, Service, JID, AffiliationString) ->
|
|||
_ ->
|
||||
throw({error, "Invalid affiliation"})
|
||||
end,
|
||||
case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name, Service, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
%% Get the PID for the online room so we can get the state of the room
|
||||
case mod_muc_room:change_item(Pid, jid:decode(JID), affiliation, Affiliation, <<"">>) of
|
||||
|
@ -1880,6 +2028,10 @@ set_room_affiliation(Name, Service, JID, AffiliationString) ->
|
|||
%%% MUC Subscription
|
||||
%%%
|
||||
|
||||
subscribe_room(Username, Host, Nick, Name, Service, Nodes) ->
|
||||
subscribe_room(makeencode(Username, Host), Nick,
|
||||
makeencode(Name, Service), Nodes).
|
||||
|
||||
subscribe_room(_User, Nick, _Room, _Nodes) when Nick == <<"">> ->
|
||||
throw({error, "Nickname must be set"});
|
||||
subscribe_room(User, Nick, Room, Nodes) when is_binary(Nodes) ->
|
||||
|
@ -1891,7 +2043,7 @@ subscribe_room(User, Nick, Room, NodeList) ->
|
|||
try jid:decode(User) of
|
||||
UserJID1 ->
|
||||
UserJID = jid:replace_resource(UserJID1, <<"modmucadmin">>),
|
||||
case get_room_pid_validate(Name, Host, <<"name">>, <<"room">>) of
|
||||
case get_room_pid_validate(Name, Host, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
case mod_muc_room:subscribe(
|
||||
Pid, UserJID, Nick, NodeList) of
|
||||
|
@ -1912,6 +2064,10 @@ subscribe_room(User, Nick, Room, NodeList) ->
|
|||
throw({error, "Malformed room JID"})
|
||||
end.
|
||||
|
||||
subscribe_room_many_v3(List, Name, Service, Nodes) ->
|
||||
List2 = [{makeencode(User, Host), Nick} || {User, Host, Nick} <- List],
|
||||
subscribe_room_many(List2, makeencode(Name, Service), Nodes).
|
||||
|
||||
subscribe_room_many(Users, Room, Nodes) ->
|
||||
MaxUsers = mod_muc_admin_opt:subscribe_room_many_max_users(global),
|
||||
if
|
||||
|
@ -1924,12 +2080,16 @@ subscribe_room_many(Users, Room, Nodes) ->
|
|||
end, Users)
|
||||
end.
|
||||
|
||||
unsubscribe_room(User, Host, Name, Service) ->
|
||||
unsubscribe_room(makeencode(User, Host),
|
||||
makeencode(Name, Service)).
|
||||
|
||||
unsubscribe_room(User, Room) ->
|
||||
try jid:decode(Room) of
|
||||
#jid{luser = Name, lserver = Host} when Name /= <<"">> ->
|
||||
try jid:decode(User) of
|
||||
UserJID ->
|
||||
case get_room_pid_validate(Name, Host, <<"name">>, <<"room">>) of
|
||||
case get_room_pid_validate(Name, Host, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
case mod_muc_room:unsubscribe(Pid, UserJID) of
|
||||
ok ->
|
||||
|
@ -1950,7 +2110,7 @@ unsubscribe_room(User, Room) ->
|
|||
end.
|
||||
|
||||
get_subscribers(Name, Host) ->
|
||||
case get_room_pid_validate(Name, Host, <<"name">>, <<"service">>) of
|
||||
case get_room_pid_validate(Name, Host, <<"service">>) of
|
||||
{Pid, _, _} when is_pid(Pid) ->
|
||||
{ok, JIDList} = mod_muc_room:get_subscribers(Pid),
|
||||
[jid:encode(jid:remove_resource(J)) || J <- JIDList];
|
||||
|
@ -1962,6 +2122,9 @@ get_subscribers(Name, Host) ->
|
|||
%% Utils
|
||||
%%----------------------------
|
||||
|
||||
makeencode(User, Host) ->
|
||||
jid:encode(jid:make(User, Host)).
|
||||
|
||||
-spec validate_host(Name :: binary(), ArgName::binary()) -> binary().
|
||||
validate_host(Name, ArgName) ->
|
||||
case jid:nameprep(Name) of
|
||||
|
@ -2017,11 +2180,11 @@ validate_muc2(Name, ArgName) ->
|
|||
end
|
||||
end.
|
||||
|
||||
-spec validate_room(Name :: binary(), ArgName :: binary()) -> binary().
|
||||
validate_room(Name, ArgName) ->
|
||||
-spec validate_room(Name :: binary()) -> binary().
|
||||
validate_room(Name) ->
|
||||
case jid:nodeprep(Name) of
|
||||
error ->
|
||||
throw({error, <<"Invalid value of '",ArgName/binary,"'">>});
|
||||
throw({error, <<"Invalid value of room name">>});
|
||||
Name2 ->
|
||||
Name2
|
||||
end.
|
||||
|
|
|
@ -308,8 +308,7 @@ component_send_packet({#iq{from = From,
|
|||
[]),
|
||||
drop;
|
||||
{_, {error, no_privileged_iq, _Err}} ->
|
||||
?INFO_MSG("IQ not forwarded: Component tried to send not wrapped IQ stanza.",
|
||||
[]),
|
||||
?INFO_MSG("IQ not forwarded: Component tried to send not wrapped IQ stanza.", []),
|
||||
drop;
|
||||
{_, {error, roster_query, _Err}} ->
|
||||
IQ;
|
||||
|
|
|
@ -259,7 +259,9 @@ set_room_affiliation(Config) ->
|
|||
RequestURL = "http://" ++ ServerHost ++ ":" ++ integer_to_list(WebPort) ++ "/api/set_room_affiliation",
|
||||
Headers = [{"X-Admin", "true"}],
|
||||
ContentType = "application/json",
|
||||
Body = misc:json_encode(#{name => RoomName, service => RoomService, jid => jid:encode(PeerJID), affiliation => member}),
|
||||
Body = misc:json_encode(#{room => RoomName, service => RoomService,
|
||||
user => PeerJID#jid.luser, host => PeerJID#jid.lserver,
|
||||
affiliation => member}),
|
||||
{ok, {{_, 200, _}, _, _}} = httpc:request(post, {RequestURL, Headers, ContentType, Body}, [], []),
|
||||
|
||||
#message{id = _, from = RoomJID, to = MyJID, sub_els = [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue