aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/acl.erl2
-rw-r--r--src/econf.erl2
-rw-r--r--src/ejabberd.erl2
-rw-r--r--src/ejabberd_access_permissions.erl2
-rw-r--r--src/ejabberd_acme.erl2
-rw-r--r--src/ejabberd_admin.erl22
-rw-r--r--src/ejabberd_app.erl2
-rw-r--r--src/ejabberd_auth.erl2
-rw-r--r--src/ejabberd_auth_anonymous.erl2
-rw-r--r--src/ejabberd_auth_external.erl2
-rw-r--r--src/ejabberd_auth_jwt.erl2
-rw-r--r--src/ejabberd_auth_ldap.erl2
-rw-r--r--src/ejabberd_auth_mnesia.erl2
-rw-r--r--src/ejabberd_auth_pam.erl2
-rw-r--r--src/ejabberd_auth_sql.erl10
-rw-r--r--src/ejabberd_backend_sup.erl2
-rw-r--r--src/ejabberd_bosh.erl2
-rw-r--r--src/ejabberd_c2s.erl2
-rw-r--r--src/ejabberd_c2s_config.erl2
-rw-r--r--src/ejabberd_captcha.erl6
-rw-r--r--src/ejabberd_cluster.erl2
-rw-r--r--src/ejabberd_cluster_mnesia.erl2
-rw-r--r--src/ejabberd_commands.erl2
-rw-r--r--src/ejabberd_commands_doc.erl2
-rw-r--r--src/ejabberd_config.erl4
-rw-r--r--src/ejabberd_config_transformer.erl2
-rw-r--r--src/ejabberd_ctl.erl2
-rw-r--r--src/ejabberd_db_sup.erl2
-rw-r--r--src/ejabberd_doc.erl459
-rw-r--r--src/ejabberd_hooks.erl2
-rw-r--r--src/ejabberd_http.erl20
-rw-r--r--src/ejabberd_http_ws.erl2
-rw-r--r--src/ejabberd_iq.erl2
-rw-r--r--src/ejabberd_listener.erl2
-rw-r--r--src/ejabberd_local.erl2
-rw-r--r--src/ejabberd_logger.erl2
-rw-r--r--src/ejabberd_mnesia.erl2
-rw-r--r--src/ejabberd_oauth.erl2
-rw-r--r--src/ejabberd_oauth_mnesia.erl2
-rw-r--r--src/ejabberd_oauth_rest.erl2
-rw-r--r--src/ejabberd_oauth_sql.erl2
-rw-r--r--src/ejabberd_old_config.erl2
-rw-r--r--src/ejabberd_option.erl8
-rw-r--r--src/ejabberd_options.erl24
-rw-r--r--src/ejabberd_options_doc.erl1256
-rw-r--r--src/ejabberd_piefxis.erl33
-rw-r--r--src/ejabberd_pkix.erl2
-rw-r--r--src/ejabberd_redis.erl2
-rw-r--r--src/ejabberd_redis_sup.erl2
-rw-r--r--src/ejabberd_regexp.erl2
-rw-r--r--src/ejabberd_router.erl2
-rw-r--r--src/ejabberd_router_mnesia.erl2
-rw-r--r--src/ejabberd_router_multicast.erl2
-rw-r--r--src/ejabberd_router_redis.erl2
-rw-r--r--src/ejabberd_router_sql.erl2
-rw-r--r--src/ejabberd_s2s.erl2
-rw-r--r--src/ejabberd_s2s_in.erl2
-rw-r--r--src/ejabberd_s2s_out.erl2
-rw-r--r--src/ejabberd_service.erl2
-rw-r--r--src/ejabberd_shaper.erl2
-rw-r--r--src/ejabberd_sip.erl2
-rw-r--r--src/ejabberd_sm.erl2
-rw-r--r--src/ejabberd_sm_mnesia.erl2
-rw-r--r--src/ejabberd_sm_redis.erl2
-rw-r--r--src/ejabberd_sm_sql.erl2
-rw-r--r--src/ejabberd_sql.erl46
-rw-r--r--src/ejabberd_sql_pt.erl18
-rw-r--r--src/ejabberd_sql_sup.erl2
-rw-r--r--src/ejabberd_stun.erl2
-rw-r--r--src/ejabberd_sup.erl2
-rw-r--r--src/ejabberd_system_monitor.erl2
-rw-r--r--src/ejabberd_tmp_sup.erl2
-rw-r--r--src/ejabberd_update.erl2
-rw-r--r--src/ejabberd_web.erl2
-rw-r--r--src/ejabberd_web_admin.erl4
-rw-r--r--src/ejabberd_websocket.erl2
-rw-r--r--src/ejabberd_xmlrpc.erl4
-rw-r--r--src/ejd2sql.erl2
-rw-r--r--src/eldap_filter.erl2
-rw-r--r--src/eldap_pool.erl2
-rw-r--r--src/eldap_utils.erl2
-rw-r--r--src/elixir_logger_backend.erl2
-rw-r--r--src/ext_mod.erl2
-rw-r--r--src/extauth.erl2
-rw-r--r--src/extauth_sup.erl2
-rw-r--r--src/gen_iq_handler.erl2
-rw-r--r--src/gen_mod.erl8
-rw-r--r--src/gen_pubsub_node.erl2
-rw-r--r--src/gen_pubsub_nodetree.erl2
-rw-r--r--src/jd2ejd.erl2
-rw-r--r--src/misc.erl2
-rw-r--r--src/mod_adhoc.erl16
-rw-r--r--src/mod_admin_extra.erl61
-rw-r--r--src/mod_admin_update_sql.erl11
-rw-r--r--src/mod_announce.erl65
-rw-r--r--src/mod_announce_mnesia.erl2
-rw-r--r--src/mod_announce_sql.erl2
-rw-r--r--src/mod_avatar.erl37
-rw-r--r--src/mod_block_strangers.erl55
-rw-r--r--src/mod_blocking.erl12
-rw-r--r--src/mod_bosh.erl50
-rw-r--r--src/mod_bosh_mnesia.erl2
-rw-r--r--src/mod_bosh_redis.erl2
-rw-r--r--src/mod_bosh_sql.erl2
-rw-r--r--src/mod_caps.erl34
-rw-r--r--src/mod_caps_mnesia.erl2
-rw-r--r--src/mod_caps_sql.erl2
-rw-r--r--src/mod_carboncopy.erl134
-rw-r--r--src/mod_client_state.erl36
-rw-r--r--src/mod_configure.erl11
-rw-r--r--src/mod_delegation.erl48
-rw-r--r--src/mod_disco.erl72
-rw-r--r--src/mod_fail2ban.erl38
-rw-r--r--src/mod_http_api.erl10
-rw-r--r--src/mod_http_fileserver.erl105
-rw-r--r--src/mod_http_upload.erl185
-rw-r--r--src/mod_http_upload_quota.erl55
-rw-r--r--src/mod_jidprep.erl19
-rw-r--r--src/mod_last.erl34
-rw-r--r--src/mod_last_mnesia.erl2
-rw-r--r--src/mod_last_sql.erl2
-rw-r--r--src/mod_legacy_auth.erl13
-rw-r--r--src/mod_mam.erl78
-rw-r--r--src/mod_mam_mnesia.erl2
-rw-r--r--src/mod_mam_sql.erl95
-rw-r--r--src/mod_metrics.erl34
-rw-r--r--src/mod_mix.erl38
-rw-r--r--src/mod_mix_pam.erl35
-rw-r--r--src/mod_mqtt.erl89
-rw-r--r--src/mod_mqtt_mnesia.erl2
-rw-r--r--src/mod_mqtt_session.erl2
-rw-r--r--src/mod_mqtt_sql.erl2
-rw-r--r--src/mod_mqtt_ws.erl2
-rw-r--r--src/mod_muc.erl380
-rw-r--r--src/mod_muc_admin.erl25
-rw-r--r--src/mod_muc_log.erl125
-rw-r--r--src/mod_muc_mnesia.erl2
-rw-r--r--src/mod_muc_room.erl24
-rw-r--r--src/mod_muc_sql.erl2
-rw-r--r--src/mod_muc_sup.erl2
-rw-r--r--src/mod_multicast.erl85
-rw-r--r--src/mod_offline.erl154
-rw-r--r--src/mod_offline_mnesia.erl2
-rw-r--r--src/mod_offline_sql.erl2
-rw-r--r--src/mod_ping.erl57
-rw-r--r--src/mod_pres_counter.erl36
-rw-r--r--src/mod_privacy.erl36
-rw-r--r--src/mod_privacy_mnesia.erl2
-rw-r--r--src/mod_privacy_sql.erl2
-rw-r--r--src/mod_private.erl39
-rw-r--r--src/mod_private_mnesia.erl2
-rw-r--r--src/mod_private_sql.erl2
-rw-r--r--src/mod_privilege.erl87
-rw-r--r--src/mod_proxy65.erl151
-rw-r--r--src/mod_proxy65_lib.erl2
-rw-r--r--src/mod_proxy65_mnesia.erl2
-rw-r--r--src/mod_proxy65_redis.erl2
-rw-r--r--src/mod_proxy65_service.erl2
-rw-r--r--src/mod_proxy65_sql.erl2
-rw-r--r--src/mod_proxy65_stream.erl2
-rw-r--r--src/mod_pubsub.erl2
-rw-r--r--src/mod_pubsub_mnesia.erl2
-rw-r--r--src/mod_pubsub_sql.erl2
-rw-r--r--src/mod_push.erl80
-rw-r--r--src/mod_push_keepalive.erl44
-rw-r--r--src/mod_push_mnesia.erl2
-rw-r--r--src/mod_push_sql.erl2
-rw-r--r--src/mod_register.erl75
-rw-r--r--src/mod_register_web.erl45
-rw-r--r--src/mod_roster.erl69
-rw-r--r--src/mod_roster_mnesia.erl2
-rw-r--r--src/mod_roster_sql.erl2
-rw-r--r--src/mod_s2s_dialback.erl41
-rw-r--r--src/mod_service_log.erl29
-rw-r--r--src/mod_shared_roster.erl84
-rw-r--r--src/mod_shared_roster_ldap.erl2
-rw-r--r--src/mod_shared_roster_mnesia.erl2
-rw-r--r--src/mod_shared_roster_sql.erl2
-rw-r--r--src/mod_sic.erl15
-rw-r--r--src/mod_sip.erl124
-rw-r--r--src/mod_sip_proxy.erl2
-rw-r--r--src/mod_sip_registrar.erl2
-rw-r--r--src/mod_stats.erl24
-rw-r--r--src/mod_stream_mgmt.erl98
-rw-r--r--src/mod_time.erl11
-rw-r--r--src/mod_vcard.erl95
-rw-r--r--src/mod_vcard_ldap.erl101
-rw-r--r--src/mod_vcard_mnesia.erl13
-rw-r--r--src/mod_vcard_sql.erl2
-rw-r--r--src/mod_vcard_xupdate.erl53
-rw-r--r--src/mod_version.erl17
-rw-r--r--src/mqtt_codec.erl2
-rw-r--r--src/node_flat.erl2
-rw-r--r--src/node_flat_sql.erl35
-rw-r--r--src/node_pep.erl2
-rw-r--r--src/node_pep_sql.erl10
-rw-r--r--src/nodetree_tree.erl2
-rw-r--r--src/nodetree_tree_sql.erl6
-rw-r--r--src/nodetree_virtual.erl2
-rw-r--r--src/prosody2ejabberd.erl2
-rw-r--r--src/proxy_protocol.erl2
-rw-r--r--src/pubsub_db_sql.erl2
-rw-r--r--src/pubsub_index.erl2
-rw-r--r--src/pubsub_migrate.erl2
-rw-r--r--src/pubsub_subscription.erl10
-rw-r--r--src/pubsub_subscription_sql.erl10
-rw-r--r--src/rest.erl2
-rw-r--r--src/str.erl2
-rw-r--r--src/translate.erl2
-rw-r--r--src/win32_dns.erl2
210 files changed, 5431 insertions, 513 deletions
diff --git a/src/acl.erl b/src/acl.erl
index cf6861a6f..d13c05601 100644
--- a/src/acl.erl
+++ b/src/acl.erl
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/econf.erl b/src/econf.erl
index 75817a641..04143a480 100644
--- a/src/econf.erl
+++ b/src/econf.erl
@@ -3,7 +3,7 @@
%%% Purpose : Validator for ejabberd configuration options
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd.erl b/src/ejabberd.erl
index 4758fd2f9..688ce4a73 100644
--- a/src/ejabberd.erl
+++ b/src/ejabberd.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl
index 06e28ba25..a15f2d0a6 100644
--- a/src/ejabberd_access_permissions.erl
+++ b/src/ejabberd_access_permissions.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Sep 2016 by Paweł Chmielowski <pawel@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl
index 851c6d00e..eb3af7686 100644
--- a/src/ejabberd_acme.erl
+++ b/src/ejabberd_acme.erl
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl
index 64f8fba7c..50e4f35f7 100644
--- a/src/ejabberd_admin.erl
+++ b/src/ejabberd_admin.erl
@@ -5,7 +5,7 @@
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -388,7 +388,11 @@ get_commands_spec() ->
#ejabberd_commands{name = gc, tags = [server],
desc = "Force full garbage collection",
module = ?MODULE, function = gc,
- args = [], result = {res, rescode}}
+ args = [], result = {res, rescode}},
+ #ejabberd_commands{name = man, tags = [documentation],
+ desc = "Generate Unix manpage for current ejabberd version",
+ module = ejabberd_doc, function = man,
+ args = [], result = {res, restuple}}
].
@@ -405,7 +409,7 @@ status() ->
false ->
{ejabberd_not_running, "ejabberd is not running in that node."};
{value, {_, _, Version}} ->
- {ok, io_lib:format("ejabberd ~ts is running in that node", [Version])}
+ {ok, io_lib:format("ejabberd ~s is running in that node", [Version])}
end,
{Is_running, String1 ++ String2}.
@@ -468,7 +472,7 @@ stop_kindly(DelaySeconds, AnnouncementTextString) ->
ok.
send_service_message_all_mucs(Subject, AnnouncementText) ->
- Message = str:format("~ts~n~ts", [Subject, AnnouncementText]),
+ Message = str:format("~s~n~s", [Subject, AnnouncementText]),
lists:foreach(
fun(ServerHost) ->
MUCHosts = gen_mod:get_module_opt_hosts(ServerHost, mod_muc),
@@ -512,12 +516,12 @@ register(User, Host, Password) ->
true ->
case ejabberd_auth:try_register(User, Host, Password) of
ok ->
- {ok, io_lib:format("User ~ts@~ts successfully registered", [User, Host])};
+ {ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
{error, exists} ->
- Msg = io_lib:format("User ~ts@~ts already registered", [User, Host]),
+ Msg = io_lib:format("User ~s@~s already registered", [User, Host]),
{error, conflict, 10090, Msg};
{error, Reason} ->
- String = io_lib:format("Can't register user ~ts@~ts at node ~p: ~ts",
+ String = io_lib:format("Can't register user ~s@~s at node ~p: ~s",
[User, Host, node(),
mod_register:format_error(Reason)]),
{error, cannot_register, 10001, String}
@@ -550,7 +554,7 @@ registered_vhosts() ->
reload_config() ->
case ejabberd_config:reload() of
- ok -> {ok, ""};
+ ok -> ok;
Err ->
Reason = ejabberd_config:format_error(Err),
{error, Reason}
@@ -558,7 +562,7 @@ reload_config() ->
dump_config(Path) ->
case ejabberd_config:dump(Path) of
- ok -> {ok, ""};
+ ok -> ok;
Err ->
Reason = ejabberd_config:format_error(Err),
{error, Reason}
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index 7988077c0..1473a09c2 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index 1fc05f8cb..bb11b4823 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl
index efccdd8a7..b5523285c 100644
--- a/src/ejabberd_auth_anonymous.erl
+++ b/src/ejabberd_auth_anonymous.erl
@@ -5,7 +5,7 @@
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl
index a965bf124..437416ae8 100644
--- a/src/ejabberd_auth_external.erl
+++ b/src/ejabberd_auth_external.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_jwt.erl b/src/ejabberd_auth_jwt.erl
index 8fce8e39c..a155b7f9e 100644
--- a/src/ejabberd_auth_jwt.erl
+++ b/src/ejabberd_auth_jwt.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Mar 2019 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl
index 6966e3289..b52d24cdb 100644
--- a/src/ejabberd_auth_ldap.erl
+++ b/src/ejabberd_auth_ldap.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl
index f3f1b80b7..b5aa0b607 100644
--- a/src/ejabberd_auth_mnesia.erl
+++ b/src/ejabberd_auth_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl
index 9051f4c88..38fd95dd4 100644
--- a/src/ejabberd_auth_pam.erl
+++ b/src/ejabberd_auth_pam.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl
index 3fa96b735..dd020d534 100644
--- a/src/ejabberd_auth_sql.erl
+++ b/src/ejabberd_auth_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -205,12 +205,12 @@ list_users(LServer,
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
when is_binary(Prefix) and is_integer(Limit) and
is_integer(Offset) ->
- SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
+ SPrefix = ejabberd_sql:escape_like_arg(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_sql:sql_query(
LServer,
?SQL("select @(username)s from users "
- "where username like %(SPrefix2)s escape '^' and %(LServer)H "
+ "where username like %(SPrefix2)s %ESCAPE and %(LServer)H "
"order by username "
"limit %(Limit)d offset %(Offset)d")).
@@ -235,12 +235,12 @@ users_number(LServer) ->
users_number(LServer, [{prefix, Prefix}])
when is_binary(Prefix) ->
- SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
+ SPrefix = ejabberd_sql:escape_like_arg(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_sql:sql_query(
LServer,
?SQL("select @(count(*))d from users "
- "where username like %(SPrefix2)s escape '^' and %(LServer)H"));
+ "where username like %(SPrefix2)s %ESCAPE and %(LServer)H"));
users_number(LServer, []) ->
users_number(LServer).
diff --git a/src/ejabberd_backend_sup.erl b/src/ejabberd_backend_sup.erl
index 832c4e3e6..31a377c18 100644
--- a/src/ejabberd_backend_sup.erl
+++ b/src/ejabberd_backend_sup.erl
@@ -2,7 +2,7 @@
%%% Created : 24 Feb 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl
index a4621e93e..893e9230f 100644
--- a/src/ejabberd_bosh.erl
+++ b/src/ejabberd_bosh.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Jul 2011 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 2d89197a3..f0eb8efdb 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -2,7 +2,7 @@
%%% Created : 8 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_c2s_config.erl b/src/ejabberd_c2s_config.erl
index e3f982ebf..580500525 100644
--- a/src/ejabberd_c2s_config.erl
+++ b/src/ejabberd_c2s_config.erl
@@ -6,7 +6,7 @@
%%% Created : 2 Nov 2007 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl
index 8190e6931..ee42784c0 100644
--- a/src/ejabberd_captcha.erl
+++ b/src/ejabberd_captcha.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov@gmail.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -94,8 +94,8 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
Lang, [challenge]),
X = #xdata{type = form, fields = Fs},
Captcha = #xcaptcha{xdata = X},
- BodyString = {?T("Your subscription request and/or messages to ~ts have been blocked. "
- "To unblock your subscription request, visit ~ts"), [JID, get_url(Id)]},
+ BodyString = {?T("Your subscription request and/or messages to ~s have been blocked. "
+ "To unblock your subscription request, visit ~s"), [JID, get_url(Id)]},
Body = xmpp:mk_text(BodyString, Lang),
OOB = #oob_x{url = get_url(Id)},
Hint = #hint{type = 'no-store'},
diff --git a/src/ejabberd_cluster.erl b/src/ejabberd_cluster.erl
index b29beeb2e..f323e6be9 100644
--- a/src/ejabberd_cluster.erl
+++ b/src/ejabberd_cluster.erl
@@ -3,7 +3,7 @@
%%% Created : 5 Jul 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_cluster_mnesia.erl b/src/ejabberd_cluster_mnesia.erl
index 60539330c..726cd04e3 100644
--- a/src/ejabberd_cluster_mnesia.erl
+++ b/src/ejabberd_cluster_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Oct 2015 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl
index 08faff22e..93ae1fdcc 100644
--- a/src/ejabberd_commands.erl
+++ b/src/ejabberd_commands.erl
@@ -5,7 +5,7 @@
%%% Created : 20 May 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl
index bff6d028f..e2e0b3459 100644
--- a/src/ejabberd_commands_doc.erl
+++ b/src/ejabberd_commands_doc.erl
@@ -5,7 +5,7 @@
%%% Created : 20 May 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 4463e4caa..98edd9a48 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -5,7 +5,7 @@
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,6 +36,7 @@
-export([default_db/2, default_db/3, default_ram_db/2, default_ram_db/3]).
-export([beams/1, validators/1, globals/0, may_hide_data/1]).
-export([dump/0, dump/1, convert_to_yaml/1, convert_to_yaml/2]).
+-export([callback_modules/1]).
%% Deprecated functions
-export([get_option/2, set_option/2]).
@@ -63,6 +64,7 @@
-callback opt_type(atom()) -> econf:validator().
-callback options() -> [atom() | {atom(), term()}].
-callback globals() -> [atom()].
+-callback doc() -> any().
-optional_callbacks([globals/0]).
diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl
index 2deae8e04..26a475f3d 100644
--- a/src/ejabberd_config_transformer.erl
+++ b/src/ejabberd_config_transformer.erl
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index dfc5ca9e7..9dd4a2981 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_db_sup.erl b/src/ejabberd_db_sup.erl
index f16e60adf..f3cd21618 100644
--- a/src/ejabberd_db_sup.erl
+++ b/src/ejabberd_db_sup.erl
@@ -2,7 +2,7 @@
%%% Created : 13 June 2019 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl
new file mode 100644
index 000000000..9a3e57c68
--- /dev/null
+++ b/src/ejabberd_doc.erl
@@ -0,0 +1,459 @@
+%%%----------------------------------------------------------------------
+%%% File : ejabberd_doc.erl
+%%% Purpose : Options documentation generator
+%%%
+%%% ejabberd, Copyright (C) 2002-2020 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(ejabberd_doc).
+
+%% API
+-export([man/0, man/1, have_a2x/0]).
+
+-include("translate.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+man() ->
+ man(<<"en">>).
+
+man(Lang) when is_list(Lang) ->
+ man(list_to_binary(Lang));
+man(Lang) ->
+ {ModDoc, SubModDoc} =
+ lists:foldl(
+ fun(M, {Mods, SubMods} = Acc) ->
+ case lists:prefix("mod_", atom_to_list(M)) orelse
+ lists:prefix("Elixir.Mod", atom_to_list(M)) of
+ true ->
+ try M:mod_doc() of
+ #{desc := Descr} = Map ->
+ DocOpts = maps:get(opts, Map, []),
+ Example = maps:get(example, Map, []),
+ {[{M, Descr, DocOpts, #{example => Example}}|Mods], SubMods};
+ #{opts := DocOpts} ->
+ {ParentMod, Backend} = strip_backend_suffix(M),
+ {Mods, dict:append(ParentMod, {M, Backend, DocOpts}, SubMods)}
+ catch _:undef ->
+ case erlang:function_exported(
+ M, mod_options, 1) of
+ true ->
+ warn("module ~s is not documented", [M]);
+ false ->
+ ok
+ end,
+ Acc
+ end;
+ false ->
+ Acc
+ end
+ end, {[], dict:new()}, ejabberd_config:beams(all)),
+ Doc = lists:flatmap(
+ fun(M) ->
+ try M:doc()
+ catch _:undef -> []
+ end
+ end, ejabberd_config:callback_modules(all)),
+ Options =
+ ["TOP LEVEL OPTIONS",
+ "-----------------",
+ tr(Lang, ?T("This section describes top level options of ejabberd.")),
+ io_lib:nl()] ++
+ lists:flatmap(
+ fun(Opt) ->
+ opt_to_man(Lang, Opt, 1)
+ end, lists:keysort(1, Doc)),
+ ModDoc1 = lists:map(
+ fun({M, Descr, DocOpts, Ex}) ->
+ case dict:find(M, SubModDoc) of
+ {ok, Backends} ->
+ {M, Descr, DocOpts, Backends, Ex};
+ error ->
+ {M, Descr, DocOpts, [], Ex}
+ end
+ end, ModDoc),
+ ModOptions =
+ [io_lib:nl(),
+ "MODULES",
+ "-------",
+ "[[modules]]",
+ tr(Lang, ?T("This section describes options of all ejabberd modules.")),
+ io_lib:nl()] ++
+ lists:flatmap(
+ fun({M, Descr, DocOpts, Backends, Example}) ->
+ ModName = atom_to_list(M),
+ [io_lib:nl(),
+ ModName,
+ lists:duplicate(length(atom_to_list(M)), $~),
+ "[[" ++ ModName ++ "]]",
+ io_lib:nl()] ++
+ tr_multi(Lang, Descr) ++ [io_lib:nl()] ++
+ opts_to_man(Lang, [{M, '', DocOpts}|Backends]) ++
+ format_example(0, Lang, Example)
+ end, lists:keysort(1, ModDoc1)),
+ ListenOptions =
+ [io_lib:nl(),
+ "LISTENERS",
+ "-------",
+ "[[listeners]]",
+ tr(Lang, ?T("This section describes options of all ejabberd listeners.")),
+ io_lib:nl(),
+ "TODO"],
+ AsciiData =
+ [[unicode:characters_to_binary(Line), io_lib:nl()]
+ || Line <- man_header(Lang) ++ Options ++ [io_lib:nl()]
+ ++ ModOptions ++ ListenOptions ++ man_footer(Lang)],
+ warn_undocumented_modules(ModDoc1),
+ warn_undocumented_options(Doc),
+ write_man(AsciiData).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+opts_to_man(Lang, [{_, _, []}]) ->
+ Text = tr(Lang, ?T("The module has no options.")),
+ [Text, io_lib:nl()];
+opts_to_man(Lang, Backends) ->
+ lists:flatmap(
+ fun({_, Backend, DocOpts}) when DocOpts /= [] ->
+ Text = if Backend == '' ->
+ tr(Lang, ?T("Available options"));
+ true ->
+ lists:flatten(
+ io_lib:format(
+ tr(Lang, ?T("Available options for '~s' backend")),
+ [Backend]))
+ end,
+ [Text ++ ":", lists:duplicate(length(Text)+1, $^)|
+ lists:flatmap(
+ fun(Opt) -> opt_to_man(Lang, Opt, 1) end,
+ lists:keysort(1, DocOpts))] ++ [io_lib:nl()];
+ (_) ->
+ []
+ end, Backends).
+
+opt_to_man(Lang, {Option, Options}, Level) ->
+ [format_option(Lang, Option, Options)|format_desc(Lang, Options)] ++
+ format_example(Level, Lang, Options);
+opt_to_man(Lang, {Option, Options, Children}, Level) ->
+ [format_option(Lang, Option, Options)|format_desc(Lang, Options)] ++
+ lists:append(
+ [[H ++ ":"|T]
+ || [H|T] <- lists:map(
+ fun(Opt) -> opt_to_man(Lang, Opt, Level+1) end,
+ lists:keysort(1, Children))]) ++
+ [io_lib:nl()|format_example(Level, Lang, Options)].
+
+format_option(Lang, Option, #{value := Val}) ->
+ "*" ++ atom_to_list(Option) ++ "*: 'pass:[" ++
+ tr(Lang, Val) ++ "]'::";
+format_option(_Lang, Option, #{}) ->
+ "*" ++ atom_to_list(Option) ++ "*::".
+
+format_desc(Lang, #{desc := Desc}) ->
+ tr_multi(Lang, Desc).
+
+format_example(Level, Lang, #{example := [_|_] = Example}) ->
+ case lists:all(fun is_list/1, Example) of
+ true ->
+ if Level == 0 ->
+ ["*Example*:",
+ "^^^^^^^^^^"];
+ true ->
+ ["+", "*Example*:", "+"]
+ end ++ format_yaml(Example);
+ false when Level == 0 ->
+ ["Examples:",
+ "^^^^^^^^^"] ++
+ lists:flatmap(
+ fun({Text, Lines}) ->
+ [tr(Lang, Text)] ++ format_yaml(Lines)
+ end, Example);
+ false ->
+ lists:flatmap(
+ fun(Block) ->
+ ["+", "''''", "+"|Block]
+ end,
+ lists:map(
+ fun({Text, Lines}) ->
+ [tr(Lang, Text), "+"] ++ format_yaml(Lines)
+ end, Example))
+ end;
+format_example(_, _, _) ->
+ [].
+
+format_yaml(Lines) ->
+ ["==========================",
+ "[source,yaml]",
+ "----"|Lines] ++
+ ["----",
+ "=========================="].
+
+man_header(Lang) ->
+ ["ejabberd.yml(5)",
+ "===============",
+ ":doctype: manpage",
+ ":version: " ++ binary_to_list(ejabberd_config:version()),
+ io_lib:nl(),
+ "NAME",
+ "----",
+ "ejabberd.yml - " ++ tr(Lang, ?T("main configuration file for ejabberd.")),
+ io_lib:nl(),
+ "SYNOPSIS",
+ "--------",
+ "ejabberd.yml",
+ io_lib:nl(),
+ "DESCRIPTION",
+ "-----------",
+ tr(Lang, ?T("The configuration file is written in "
+ "https://en.wikipedia.org/wiki/YAML[YAML] language.")),
+ io_lib:nl(),
+ tr(Lang, ?T("WARNING: YAML is indentation sensitive, so make sure you respect "
+ "indentation, or otherwise you will get pretty cryptic "
+ "configuration errors.")),
+ io_lib:nl(),
+ tr(Lang, ?T("Logically, configuration options are splitted into 3 main categories: "
+ "'Modules', 'Listeners' and everything else called 'Top Level' options. "
+ "Thus this document is splitted into 3 main chapters describing each "
+ "category separately. So, the contents of ejabberd.yml will typically "
+ "look like this:")),
+ io_lib:nl(),
+ "==========================",
+ "[source,yaml]",
+ "----",
+ "hosts:",
+ " - example.com",
+ " - domain.tld",
+ "loglevel: info",
+ "...",
+ "listen:",
+ " -",
+ " port: 5222",
+ " module: ejabberd_c2s",
+ " ...",
+ "modules:",
+ " mod_roster: {}",
+ " ...",
+ "----",
+ "==========================",
+ io_lib:nl(),
+ tr(Lang, ?T("Any configuration error (such as syntax error, unknown option "
+ "or invalid option value) is fatal in the sense that ejabberd will "
+ "refuse to load the whole configuration file and will not start or will "
+ "abort configuration reload.")),
+ io_lib:nl(),
+ tr(Lang, ?T("All options can be changed in runtime by running 'ejabberdctl "
+ "reload-config' command. Configuration reload is atomic: either all options "
+ "are accepted and applied simultaneously or the new configuration is "
+ "refused without any impact on currently running configuration.")),
+ io_lib:nl(),
+ tr(Lang, ?T("Some options can be specified for particular virtual host(s) only "
+ "using 'host_config' or 'append_host_config' options. Such options "
+ "are called 'local'. Examples are 'modules', 'auth_method' and 'default_db'. "
+ "The options that cannot be defined per virtual host are called 'global'. "
+ "Examples are 'loglevel', 'certfiles' and 'listen'. It is a configuration "
+ "mistake to put 'global' options under 'host_config' or 'append_host_config' "
+ "section - ejabberd will refuse to load such configuration.")),
+ io_lib:nl(),
+ str:format(
+ tr(Lang, ?T("It is not recommended to write ejabberd.yml from scratch. Instead it is "
+ "better to start from \"default\" configuration file available at ~s. "
+ "Once you get ejabberd running you can start changing configuration "
+ "options to meet your requirements.")),
+ [default_config_url()]),
+ io_lib:nl(),
+ str:format(
+ tr(Lang, ?T("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 ~s.")),
+ [configuration_guide_url()]),
+ io_lib:nl()].
+
+man_footer(Lang) ->
+ {Year, _, _} = date(),
+ [io_lib:nl(),
+ "AUTHOR",
+ "------",
+ "https://www.process-one.net[ProcessOne].",
+ io_lib:nl(),
+ "VERSION",
+ "-------",
+ str:format(
+ tr(Lang, ?T("This document describes the configuration file of ejabberd ~ts. "
+ "Configuration options of other ejabberd versions "
+ "may differ significantly.")),
+ [ejabberd_config:version()]),
+ io_lib:nl(),
+ "REPORTING BUGS",
+ "--------------",
+ tr(Lang, ?T("Report bugs to <https://github.com/processone/ejabberd/issues>")),
+ io_lib:nl(),
+ "SEE ALSO",
+ "---------",
+ tr(Lang, ?T("Default configuration file")) ++ ": " ++ default_config_url(),
+ io_lib:nl(),
+ tr(Lang, ?T("Main site")) ++ ": <https://ejabberd.im>",
+ io_lib:nl(),
+ tr(Lang, ?T("Documentation")) ++ ": <https://docs.ejabberd.im>",
+ io_lib:nl(),
+ tr(Lang, ?T("Configuration Guide")) ++ ": " ++ configuration_guide_url(),
+ io_lib:nl(),
+ tr(Lang, ?T("Source code")) ++ ": <https://github.com/processone/ejabberd>",
+ io_lib:nl(),
+ "COPYING",
+ "-------",
+ "Copyright (c) 2002-" ++ integer_to_list(Year) ++
+ " https://www.process-one.net[ProcessOne]."].
+
+tr(Lang, {Format, Args}) ->
+ unicode:characters_to_list(
+ str:format(
+ translate:translate(Lang, iolist_to_binary(Format)),
+ Args));
+tr(Lang, Txt) ->
+ unicode:characters_to_list(translate:translate(Lang, iolist_to_binary(Txt))).
+
+tr_multi(Lang, Txt) when is_binary(Txt) ->
+ tr_multi(Lang, [Txt]);
+tr_multi(Lang, {Format, Args}) ->
+ tr_multi(Lang, [{Format, Args}]);
+tr_multi(Lang, Lines) when is_list(Lines) ->
+ [tr(Lang, Txt) || Txt <- Lines].
+
+write_man(AsciiData) ->
+ case file:get_cwd() of
+ {ok, Cwd} ->
+ AsciiDocFile = filename:join(Cwd, "ejabberd.yml.5.txt"),
+ ManPage = filename:join(Cwd, "ejabberd.yml.5"),
+ case file:write_file(AsciiDocFile, AsciiData) of
+ ok ->
+ Ret = run_a2x(Cwd, AsciiDocFile),
+ %%file:delete(AsciiDocFile),
+ case Ret of
+ ok ->
+ {ok, lists:flatten(
+ io_lib:format(
+ "The manpage saved as ~ts", [ManPage]))};
+ {error, Error} ->
+ {error, lists:flatten(
+ io_lib:format(
+ "Failed to generate manpage: ~ts", [Error]))}
+ end;
+ {error, Reason} ->
+ {error, lists:flatten(
+ io_lib:format(
+ "Failed to write to ~ts: ~s",
+ [AsciiDocFile, file:format_error(Reason)]))}
+ end;
+ {error, Reason} ->
+ {error, lists:flatten(
+ io_lib:format("Failed to get current directory: ~s",
+ [file:format_error(Reason)]))}
+ end.
+
+have_a2x() ->
+ case os:find_executable("a2x") of
+ false -> false;
+ Path -> {true, Path}
+ end.
+
+run_a2x(Cwd, AsciiDocFile) ->
+ case have_a2x() of
+ false ->
+ {error, "a2x was not found: do you have 'asciidoc' installed?"};
+ {true, Path} ->
+ Cmd = lists:flatten(
+ io_lib:format("~ts -f manpage ~ts -D ~ts",
+ [Path, AsciiDocFile, Cwd])),
+ case os:cmd(Cmd) of
+ "" -> ok;
+ Ret -> {error, Ret}
+ end
+ end.
+
+warn_undocumented_modules(Docs) ->
+ lists:foreach(
+ fun({M, _, DocOpts, Backends, _}) ->
+ warn_undocumented_module(M, DocOpts),
+ lists:foreach(
+ fun({SubM, _, SubOpts}) ->
+ warn_undocumented_module(SubM, SubOpts)
+ end, Backends)
+ end, Docs).
+
+warn_undocumented_module(M, DocOpts) ->
+ try M:mod_options(ejabberd_config:get_myname()) of
+ Defaults ->
+ lists:foreach(
+ fun(OptDefault) ->
+ Opt = case OptDefault of
+ O when is_atom(O) -> O;
+ {O, _} -> O
+ end,
+ case lists:keymember(Opt, 1, DocOpts) of
+ false ->
+ warn("~s: option ~s is not documented",
+ [M, Opt]);
+ true ->
+ ok
+ end
+ end, Defaults)
+ catch _:undef ->
+ ok
+ end.
+
+warn_undocumented_options(Docs) ->
+ Opts = lists:flatmap(
+ fun(M) ->
+ try M:options() of
+ Defaults ->
+ lists:map(
+ fun({O, _}) -> O;
+ (O) when is_atom(O) -> O
+ end, Defaults)
+ catch _:undef ->
+ []
+ end
+ end, ejabberd_config:callback_modules(all)),
+ lists:foreach(
+ fun(Opt) ->
+ case lists:keymember(Opt, 1, Docs) of
+ false ->
+ warn("option ~s is not documented", [Opt]);
+ true ->
+ ok
+ end
+ end, Opts).
+
+warn(Format, Args) ->
+ io:format(standard_error, "Warning: " ++ Format ++ "~n", Args).
+
+strip_backend_suffix(M) ->
+ [H|T] = lists:reverse(string:tokens(atom_to_list(M), "_")),
+ {list_to_atom(string:join(lists:reverse(T), "_")), list_to_atom(H)}.
+
+default_config_url() ->
+ "<https://github.com/processone/ejabberd/blob/" ++
+ binary_to_list(binary:part(ejabberd_config:version(), {0,5})) ++
+ "/ejabberd.yml.example>".
+
+configuration_guide_url() ->
+ "<https://docs.ejabberd.im/admin/configuration>".
diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl
index 4c995f4de..5235f2b7a 100644
--- a/src/ejabberd_hooks.erl
+++ b/src/ejabberd_hooks.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl
index 5c49e9c19..fad51f44f 100644
--- a/src/ejabberd_http.erl
+++ b/src/ejabberd_http.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Feb 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -31,7 +31,8 @@
%% External exports
-export([start/3, start_link/3,
accept/1, receive_headers/1, recv_file/2,
- listen_opt_type/1, listen_options/0]).
+ listen_opt_type/1, listen_options/0,
+ apply_custom_headers/2]).
-export([init/3]).
@@ -491,19 +492,19 @@ process_request(#state{request_method = Method,
{Status, Headers, El}
when is_record(El, xmlel) ->
make_xhtml_output(State, Status,
- Headers ++ CustomHeaders, El);
+ apply_custom_headers(Headers, CustomHeaders), El);
Output when is_binary(Output) or is_list(Output) ->
make_text_output(State, 200, CustomHeaders, Output);
{Status, Headers, Output}
when is_binary(Output) or is_list(Output) ->
make_text_output(State, Status,
- Headers ++ CustomHeaders, Output);
+ apply_custom_headers(Headers, CustomHeaders), Output);
{Status, Headers, {file, FileName}} ->
make_file_output(State, Status, Headers, FileName);
{Status, Reason, Headers, Output}
when is_binary(Output) or is_list(Output) ->
make_text_output(State, Status, Reason,
- Headers ++ CustomHeaders, Output);
+ apply_custom_headers(Headers, CustomHeaders), Output);
_ ->
none
end,
@@ -855,6 +856,15 @@ parse_urlencoded(<<>>, Last, Cur, _State) ->
[{Last, Cur}];
parse_urlencoded(undefined, _, _, _) -> [].
+apply_custom_headers(Headers, CustomHeaders) ->
+ {Doctype, Headers2} = case Headers -- [html] of
+ Headers -> {[], Headers};
+ Other -> {[html], Other}
+ end,
+ M = maps:merge(maps:from_list(Headers2),
+ maps:from_list(CustomHeaders)),
+ Doctype ++ maps:to_list(M).
+
% The following code is mostly taken from yaws_ssl.erl
toupper(C) when C >= $a andalso C =< $z -> C - 32;
diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl
index 768a284ce..ef0e7c3f3 100644
--- a/src/ejabberd_http_ws.erl
+++ b/src/ejabberd_http_ws.erl
@@ -5,7 +5,7 @@
%%% Created : 09-10-2010 by Eric Cestari <ecestari@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl
index 6d5055a0d..b4d7438e7 100644
--- a/src/ejabberd_iq.erl
+++ b/src/ejabberd_iq.erl
@@ -5,7 +5,7 @@
%%% Created : 10 Nov 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl
index 9ac3f3859..f371ae7bc 100644
--- a/src/ejabberd_listener.erl
+++ b/src/ejabberd_listener.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl
index 7c8b0489f..f42bb46a1 100644
--- a/src/ejabberd_local.erl
+++ b/src/ejabberd_local.erl
@@ -5,7 +5,7 @@
%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl
index a63aca5cb..f5ea84f29 100644
--- a/src/ejabberd_logger.erl
+++ b/src/ejabberd_logger.erl
@@ -5,7 +5,7 @@
%%% Created : 12 May 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl
index 6bc8ee389..9521abb0a 100644
--- a/src/ejabberd_mnesia.erl
+++ b/src/ejabberd_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 17 Nov 2016 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl
index 232bcf127..9a9e87c16 100644
--- a/src/ejabberd_oauth.erl
+++ b/src/ejabberd_oauth.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Mar 2015 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_oauth_mnesia.erl b/src/ejabberd_oauth_mnesia.erl
index de851f1ea..1137be11f 100644
--- a/src/ejabberd_oauth_mnesia.erl
+++ b/src/ejabberd_oauth_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl
index 6af67996b..52ab6dd74 100644
--- a/src/ejabberd_oauth_rest.erl
+++ b/src/ejabberd_oauth_rest.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl
index fb91a8813..d50dfd35c 100644
--- a/src/ejabberd_oauth_sql.erl
+++ b/src/ejabberd_oauth_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_old_config.erl b/src/ejabberd_old_config.erl
index bc9e9c2a0..c8fef0a12 100644
--- a/src/ejabberd_old_config.erl
+++ b/src/ejabberd_old_config.erl
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% Purpose: Transform old-style Erlang config to YAML config
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl
index a2855b1bf..030001ed8 100644
--- a/src/ejabberd_option.erl
+++ b/src/ejabberd_option.erl
@@ -15,6 +15,7 @@
-export([auth_cache_missed/0]).
-export([auth_cache_size/0]).
-export([auth_method/0, auth_method/1]).
+-export([auth_opts/0, auth_opts/1]).
-export([auth_password_format/0, auth_password_format/1]).
-export([auth_use_cache/0, auth_use_cache/1]).
-export([c2s_cafile/0, c2s_cafile/1]).
@@ -220,6 +221,13 @@ auth_method() ->
auth_method(Host) ->
ejabberd_config:get_option({auth_method, Host}).
+-spec auth_opts() -> [{any(),any()}].
+auth_opts() ->
+ auth_opts(global).
+-spec auth_opts(global | binary()) -> [{any(),any()}].
+auth_opts(Host) ->
+ ejabberd_config:get_option({auth_opts, Host}).
+
-spec auth_password_format() -> 'plain' | 'scram'.
auth_password_format() ->
auth_password_format(global).
diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl
index d1019dd62..e8b8cb890 100644
--- a/src/ejabberd_options.erl
+++ b/src/ejabberd_options.erl
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -19,7 +19,7 @@
-module(ejabberd_options).
-behaviour(ejabberd_config).
--export([opt_type/1, options/0, globals/0]).
+-export([opt_type/1, options/0, globals/0, doc/0]).
-ifdef(NEW_SQL_SCHEMA).
-define(USE_NEW_SQL_SCHEMA_DEFAULT, true).
@@ -62,6 +62,21 @@ opt_type(auth_cache_size) ->
econf:pos_int(infinity);
opt_type(auth_method) ->
econf:list_or_single(econf:db_type(ejabberd_auth));
+opt_type(auth_opts) ->
+ fun(L) when is_list(L) ->
+ lists:map(
+ fun({host, V}) when is_binary(V) ->
+ {host, V};
+ ({connection_pool_size, V}) when is_integer(V) ->
+ {connection_pool_size, V};
+ ({connection_opts, V}) when is_list(V) ->
+ {connection_opts, V};
+ ({basic_auth, V}) when is_binary(V) ->
+ {basic_auth, V};
+ ({path_prefix, V}) when is_binary(V) ->
+ {path_prefix, V}
+ end, L)
+ end;
opt_type(auth_password_format) ->
econf:enum([plain, scram]);
opt_type(auth_use_cache) ->
@@ -443,6 +458,7 @@ opt_type(jwt_auth_only_rule) ->
{disable_sasl_mechanisms, [binary()]} |
{s2s_zlib, boolean()} |
{loglevel, ejabberd_logger:loglevel()} |
+ {auth_opts, [{any(), any()}]} |
{listen, [ejabberd_listener:listener()]} |
{modules, [{module(), gen_mod:opts(), integer()}]} |
{ldap_uids, [{binary(), binary()}]} |
@@ -493,6 +509,7 @@ options() ->
fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
{auth_method,
fun(Host) -> [ejabberd_config:default_db(Host, ejabberd_auth)] end},
+ {auth_opts, []},
{auth_password_format, plain},
{auth_use_cache,
fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
@@ -730,6 +747,9 @@ globals() ->
websocket_ping_interval,
websocket_timeout].
+doc() ->
+ ejabberd_options_doc:doc().
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl
new file mode 100644
index 000000000..28f3addee
--- /dev/null
+++ b/src/ejabberd_options_doc.erl
@@ -0,0 +1,1256 @@
+%%%----------------------------------------------------------------------
+%%% ejabberd, Copyright (C) 2002-2020 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(ejabberd_options_doc).
+
+%% API
+-export([doc/0]).
+
+-include("translate.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+doc() ->
+ [{hosts,
+ #{value => ?T("[Domain1, Domain2, ...]"),
+ desc =>
+ ?T("The option defines a list containing one or more "
+ "domains that 'ejabberd' will serve. This is a "
+ "**mandatory** option.")}},
+ {listen,
+ #{value => "[Options, ...]",
+ desc =>
+ ?T("The option for listeners configuration. See "
+ "<<listeners,Listeners>> section of this document "
+ "for details.")}},
+ {modules,
+ #{value => "{Module: Options}",
+ desc =>
+ ?T("The option for modules configuration. See "
+ "<<modules,Modules>> section of this document "
+ "for details.")}},
+ {loglevel,
+ #{value =>
+ "none | emergency | alert | critical | "
+ "error | warning | notice | info | debug",
+ desc =>
+ ?T("Verbosity of log files generated by ejabberd. "
+ "The default value is 'info'. "
+ "NOTE: previous versions of ejabberd had log levels "
+ "defined in numeric format ('0..5'). The numeric values "
+ "are still accepted for backward compatibility, but "
+ "are not recommended.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("The time of a cached item to keep in cache. "
+ "Once it's expired, the corresponding item is "
+ "erased from cache. The default value is 'one hour'.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether or not to cache missed lookups. When there is "
+ "an attempt to lookup for a value in a database and "
+ "this value is not found and the option is set to 'true', "
+ "this attempt will be cached and no attempts will be "
+ "performed until the cache expires (see 'cache_life_time'). "
+ "Usually you don't want to change it. Default is 'true'.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("A maximum number of items (not memory!) in cache. "
+ "The rule of thumb, for all tables except rosters, "
+ "you should set it to the number of maximum online "
+ "users you expect. For roster multiply this number "
+ "by 20 or so. If the cache size reaches this threshold, "
+ "it's fully cleared, i.e. all items are deleted, and "
+ "the corresponding warning is logged. You should avoid "
+ "frequent cache clearance, because this degrades "
+ "performance. The default value is '1000'.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc => ?T("Enable or disable cache. The default is 'true'.")}},
+ {default_db,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Default persistent storage for ejabberd. "
+ "Modules and other components (e.g. authentication) "
+ "may have its own value. The default value is 'mnesia'.")}},
+ {default_ram_db,
+ #{value => "mnesia | sql | redis",
+ desc =>
+ ?T("Default volatile (in-memory) storage for ejabberd. "
+ "Modules and other components (e.g. session management) "
+ "may have its own value. The default value is 'mnesia'.")}},
+ {queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("Default type of queues in ejabberd. "
+ "Modules may have its own value of the option. "
+ "The value of 'ram' means that queues will be kept in memory. "
+ "If value 'file' is set, you may also specify directory "
+ "in 'queue_dir' option where file queues will be placed. "
+ "The default value is 'ram'.")}},
+ {version,
+ #{value => "string()",
+ desc =>
+ ?T("The option can be used to set custom ejabberd version, "
+ "that will be used by different parts of ejabberd, for "
+ "example by 'mod_version' module. The default value is "
+ "obtained at compile time from the underlying version "
+ "control system.")}},
+ {acl,
+ #{value => "{ACLName: {ACLType: ACLValue}}",
+ desc =>
+ ?T("The option defines access control lists: named sets "
+ "of rules which are used to match against different targets "
+ "(such as a JID or an IP address). Every set of rules "
+ "has name 'ACLName': it can be any string except 'all' or 'none' "
+ "(those are predefined names for the rules that match all or nothing "
+ "respectively). The name 'ACLName' can be referenced from other "
+ "parts of the configuration file, for example in 'access_rules' "
+ "option. The rules of 'ACLName' are represented by mapping "
+ "'pass:[{ACLType: ACLValue}]'. These can be one of the following:")},
+ [{user,
+ #{value => ?T("Username"),
+ desc =>
+ ?T("If 'Username' is in the form of \"user@server\", "
+ "the rule matches a JID against this value. "
+ "Otherwise, if 'Username' is in the form of \"user\", "
+ "the rule matches any JID that has 'Username' in the node part "
+ "as long as the server part of this JID is any virtual "
+ "host served by ejabberd.")}},
+ {server,
+ #{value => ?T("Server"),
+ desc =>
+ ?T("The rule matches any JID from server 'Server'. "
+ "The value of 'Server' must be a valid "
+ "hostname or an IP address.")}},
+ {resource,
+ #{value => ?T("Resource"),
+ desc =>
+ ?T("The rule matches any JID with a resource 'Resource'.")}},
+ {ip,
+ #{value => ?T("Network"),
+ desc =>
+ ?T("The rule matches any IP address from the 'Network'.")}},
+ {user_regexp,
+ #{value => ?T("Regexp"),
+ desc =>
+ ?T("If 'Regexp' is in the form of \"regexp@server\", the rule "
+ "matches any JID with node part matching regular expression "
+ "\"regexp\" as long as the server part of this JID is equal "
+ "to \"server\". If 'Regexp' is in the form of \"regexp\", the rule "
+ "matches any JID with node part matching regular expression "
+ "\"regexp\" as long as the server part of this JID is any virtual "
+ "host served by ejabberd.")}},
+ {server_regexp,
+ #{value => ?T("Regexp"),
+ desc =>
+ ?T("The rule matches any JID from the server that "
+ "matches regular expression 'Regexp'.")}},
+ {resource_regexp,
+ #{value => ?T("Regexp"),
+ desc =>
+ ?T("The rule matches any JID with a resource that "
+ "matches regular expression 'Regexp'.")}},
+ {node_regexp,
+ #{value => ?T("user_regexp@server_regexp"),
+ desc =>
+ ?T("The rule matches any JID with node part matching regular "
+ "expression 'user_regexp' and server part matching regular "
+ "expression 'server_regexp'.")}},
+ {user_glob,
+ #{value => ?T("Pattern"),
+ desc =>
+ ?T("Same as 'user_regexp', but matching is performed on a "
+ "specified 'Pattern' according to the rules used by the "
+ "Unix shell.")}},
+ {server_glob,
+ #{value => ?T("Pattern"),
+ desc =>
+ ?T("Same as 'server_regexp', but matching is performed on a "
+ "specified 'Pattern' according to the rules used by the "
+ "Unix shell.")}},
+ {resource_glob,
+ #{value => ?T("Pattern"),
+ desc =>
+ ?T("Same as 'resource_regexp', but matching is performed on a "
+ "specified 'Pattern' according to the rules used by the "
+ "Unix shell.")}},
+ {node_glob,
+ #{value => ?T("Pattern"),
+ desc =>
+ ?T("Same as 'node_regexp', but matching is performed on a "
+ "specified 'Pattern' according to the rules used by the "
+ "Unix shell.")}}]},
+ {access_rules,
+ #{value => "{AccessName: {allow|deny: ACLRules|ACLName}}",
+ desc =>
+ ?T("The option specifies access rules. Each access rule is "
+ "assigned a name that can be referenced from other parts "
+ "of the configuration file (mostly from 'access' options of "
+ "ejabberd modules). Each rule definition may contain "
+ "arbitrary number of 'allow' or 'deny' sections, and each "
+ "section may contain any number of ACL rules (see 'acl' option). "
+ "There are no access rules defined by default."),
+ example =>
+ ["access_rules:",
+ " configure:",
+ " allow: admin",
+ " something:",
+ " deny: someone",
+ " allow: all",
+ " s2s_banned:",
+ " deny: problematic_hosts",
+ " deny: banned_forever",
+ " deny:",
+ " ip: 222.111.222.111/32",
+ " deny:",
+ " ip: 111.222.111.222/32",
+ " allow: all",
+ " xmlrpc_access:",
+ " allow:",
+ " user: peter@example.com",
+ " allow:",
+ " user: ivone@example.com",
+ " allow:",
+ " user: bot@example.com",
+ " ip: 10.0.0.0/24"]}},
+ {acme,
+ #{value => ?T("Options"),
+ desc =>
+ ?T("ACME configuration. ACME is used to automatically "
+ "obtain SSL certificates for the domains served by ejabberd, "
+ "which means that certificate requests and renewals are "
+ "performed to some CA server (aka \"ACME server\") in a fully "
+ "automated mode. The 'Options' are:"),
+ example =>
+ ["acme:",
+ " ca_url: https://acme-v02.api.letsencrypt.org/directory",
+ " contact:",
+ " - mailto:admin@domain.tld",
+ " - mailto:bot@domain.tld",
+ " auto: true",
+ " cert_type: rsa"]},
+ [{ca_url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("The ACME directory URL used as an entry point "
+ "for the ACME server. The default value is "
+ "<https://acme-v02.api.letsencrypt.org/directory> - "
+ "the directory URL of Let's Encrypt authority.")}},
+ {contact,
+ #{value => ?T("[Contact, ...]"),
+ desc =>
+ ?T("A list of contact addresses (typically emails) "
+ "where an ACME server will send notifications "
+ "when problems occur. The value of 'Contact' must "
+ "be in the form of \"scheme:address\" (e.g. "
+ "\"mailto:user@domain.tld\"). The default "
+ "is an empty list which means an ACME server "
+ "will send no notices.")}},
+ {auto,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to automatically request certificates for "
+ "all configured domains (that yet have no a certificate) "
+ "on server start or configuration reload. The default is 'true'.")}},
+ {cert_type,
+ #{value => "rsa | ec",
+ desc =>
+ ?T("A type of a certificate key. Available values are "
+ "'ec' and 'rsa' for EC and RSA certificates respectively. "
+ "It's better to have RSA certificates for the purpose "
+ "of backward compatibility with legacy clients and servers, "
+ "thus the default is 'rsa'.")}}]},
+ {allow_contrib_modules,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to allow installation of third-party modules or not. "
+ "The default value is 'true'.")}},
+ {allow_multiple_connections,
+ #{value => "true | false",
+ desc =>
+ ?T("This option is only used when the anonymous mode is enabled. "
+ "Setting it to 'true' means that the same username can be "
+ "taken multiple times in anonymous login mode if different "
+ "resource are used to connect. This option is only useful "
+ "in very special occasions. The default value is 'false'.")}},
+ {anonymous_protocol,
+ #{value => "login_anon | sasl_anon | both",
+ desc =>
+ ?T("'login_anon' means that the anonymous login method will be used. "
+ "'sasl_anon' means that the SASL Anonymous method will be used. "
+ "'both' means that SASL Anonymous and login anonymous are both "
+ "enabled. The default value is 'sasl_anon'.")}},
+ {append_host_config,
+ #{value => "{Host: Options}",
+ desc =>
+ ?T("To define specific ejabberd modules in a virtual host, "
+ "you can define the global 'modules' option with the common modules, "
+ "and later add specific modules to certain virtual hosts. "
+ "To accomplish that, 'append_host_config' option can be used.")}},
+ {auth_cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as 'cache_life_time', but applied to authentication cache "
+ "only. If not set, the value from 'cache_life_time' will be used.")}},
+ {auth_cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'cache_missed', but applied to authentication cache "
+ "only. If not set, the value from 'cache_missed' will be used.")}},
+ {auth_cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as 'cache_size', but applied to authentication cache "
+ "only. If not set, the value from 'cache_size' will be used.")}},
+ {auth_method,
+ #{value => "[mnesia | sql | anonymous | external | jwt | ldap | pam, ...]",
+ desc =>
+ ?T("A list of authentication methods to use. "
+ "If several methods are defined, authentication is "
+ "considered successful as long as authentication of "
+ "at least one of the methods succeeds. "
+ "The default value is '[mnesia]'.")}},
+ {auth_password_format,
+ #{value => "plain | scram",
+ desc =>
+ ?T("The option defines in what format the users passwords "
+ "are stored. 'plain': The password is stored as plain text "
+ "in the database. This is risky because the passwords "
+ "can be read if your database gets compromised. "
+ "This is the default value. This format allows clients to "
+ "authenticate using: the old Jabber Non-SASL (XEP-0078), "
+ "SASL PLAIN, SASL DIGEST-MD5, and SASL SCRAM-SHA-1. "
+ "'scram': The password is not stored, only some information "
+ "that allows to verify the hash provided by the client. "
+ "It is impossible to obtain the original plain password "
+ "from the stored information; for this reason, when this "
+ "value is configured it cannot be changed to plain anymore. "
+ "This format allows clients to authenticate using: "
+ "SASL PLAIN and SASL SCRAM-SHA-1.")}},
+ {auth_use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'use_cache', but applied to authentication cache "
+ "only. If not set, the value from 'use_cache' will be used.")}},
+ {c2s_cafile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("Full path to a file containing one or more CA certificates "
+ "in PEM format. All client certificates should be signed by "
+ "one of these root CA certificates and should contain the "
+ "corresponding JID(s) in subjectAltName field. "
+ "There is no default value.")}},
+ {c2s_ciphers,
+ #{value => "[Cipher, ...]",
+ desc =>
+ ?T("A list of OpenSSL ciphers to use for c2s connections. "
+ "The default value is shown in the example below:"),
+ example =>
+ ["c2s_ciphers:",
+ " - HIGH",
+ " - \"!aNULL\"",
+ " - \"!eNULL\"",
+ " - \"!3DES\"",
+ " - \"@STRENGTH\""]}},
+ {c2s_dhfile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("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.")}},
+ {c2s_protocol_options,
+ #{value => "[Option, ...]",
+ desc =>
+ ?T("List of general SSL options to use for c2s connections. "
+ "These map to OpenSSL's 'set_options()'. The default value is "
+ "shown in the example below:"),
+ example =>
+ ["c2s_protocol_options:",
+ " - no_sslv3",
+ " - cipher_server_preference",
+ " - no_compression"]}},
+ {c2s_tls_compression,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to enable or disable TLS compression for c2s connections. "
+ "The default value is 'false'.")}},
+ {ca_file,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("Path to a file of CA root certificates. "
+ "The default is to use system defined file if possible.")}},
+ {captcha_cmd,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("Full path to a script that generates CAPTCHA images. "
+ "There is no default value: when this option is not "
+ "set, CAPTCHA functionality is completely disabled.")}},
+ {captcha_limit,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Maximum number of CAPTCHA generated images per minute for "
+ "any given JID. The option is intended to protect the server "
+ "from CAPTCHA DoS. The default value is 'infinity'.")}},
+ {captcha_host,
+ #{desc => ?T("Deprecated. Use 'captcha_url' instead.")}},
+ {captcha_url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("An URL where CAPTCHA requests should be sent. NOTE: you need "
+ "to configure 'request_handlers' for 'ejabberd_http' listener "
+ "as well. There is no default value.")}},
+ {certfiles,
+ #{value => "[Path, ...]",
+ desc =>
+ ?T("The option accepts a list of file paths (optionally with "
+ "wildcards) containing either PEM certificates or PEM private "
+ "keys. At startup or configuration reload, ejabberd reads all "
+ "certificates from these files, sorts them, removes duplicates, "
+ "finds matching private keys and then rebuilds full certificate "
+ "chains for the use in TLS connections. "
+ "Use this option when TLS is enabled in either of "
+ "ejabberd listeners: 'ejabberd_c2s', 'ejabberd_http' and so on. "
+ "NOTE: if you modify the certificate files or change the value "
+ "of the option, run 'ejabberdctl reload-config' in order to "
+ "rebuild and reload the certificate chains."),
+ example =>
+ [{?T("If you use https://letsencrypt.org[Let's Encrypt] certificates "
+ "for your domain \"domain.tld\", the configuration will look "
+ "like this:"),
+ ["certfiles:",
+ " - /etc/letsencrypt/live/domain.tld/fullchain.pem",
+ " - /etc/letsencrypt/live/domain.tld/privkey.pem"]}]}},
+ {cluster_backend,
+ #{value => ?T("Backend"),
+ desc =>
+ ?T("A database backend to use for storing information about "
+ "cluster. The only available value so far is 'mnesia'.")}},
+ {cluster_nodes,
+ #{value => "[Node, ...]",
+ desc =>
+ ?T("A list of Erlang nodes to connect on ejabberd startup. "
+ "This option is mostly intended for ejabberd customization "
+ "and sofisticated setups. The default value is an empty list.")}},
+ {define_macro,
+ #{value => "{MacroName: MacroValue}",
+ desc =>
+ ?T("Defines a macro. The value can be any valid arbitrary "
+ "YAML value. For convenience, it's recommended to define "
+ "a 'MacroName' in capital letters. Duplicated macros are not allowed. "
+ "Macros are processed after additional configuration files have "
+ "been included, so it is possible to use macros that are defined "
+ "in configuration files included before the usage. "
+ "It is possible to use a 'MacroValue' in the definition of another macro."),
+ example =>
+ ["define_macro:",
+ " DEBUG: debug",
+ " LOG_LEVEL: DEBUG",
+ " USERBOB:",
+ " user: bob@localhost",
+ "",
+ "loglevel: LOG_LEVEL",
+ "",
+ "acl:",
+ " admin: USERBOB"]}},
+ {disable_sasl_mechanisms,
+ #{value => "[Mechanism, ...]",
+ desc =>
+ ?T("Specify a list of SASL mechanisms (such as 'DIGEST-MD5' or "
+ "'SCRAM-SHA1') that should not be offered to the client. "
+ "For convenience, the value of 'Mechanism' is case-insensitive. "
+ "The default value is an empty list, i.e. no mechanisms "
+ "are disabled by default.")}},
+ {domain_balancing,
+ #{value => "{Domain: Options}",
+ desc =>
+ ?T("An algorithm to load balance the components that are plugged "
+ "on an ejabberd cluster. It means that you can plug one or several "
+ "instances of the same component on each ejabberd node and that "
+ "the traffic will be automatically distributed. The algorithm "
+ "to deliver messages to the component(s) can be specified by "
+ "this option. For any component connected as 'Domain', available "
+ "'Options' are:"),
+ example =>
+ ["domain_balancing:",
+ " component.domain.tld:",
+ " type: destination",
+ " component_number: 5",
+ " transport.example.org:",
+ " type: bare_source"]},
+ [{type,
+ #{value => "random | source | destination | bare_source | bare_destination",
+ desc =>
+ ?T("How to deliver stanzas to connected components: "
+ "'random' - an instance is chosen at random; "
+ "'destination' - an instance is chosen by the full JID of "
+ "the packet's 'to' attribute; "
+ "'source' - by the full JID of the packet's 'from' attribute; "
+ "'bare_destination' - by the the bare JID (without resource) "
+ "of the packet's 'to' attribute; "
+ "'bare_source' - by the bare JID (without resource) of the "
+ "packet's 'from' attribute is used. The default value is 'random'.")}},
+ {component_number,
+ #{value => "2..1000",
+ desc =>
+ ?T("The number of components to balance.")}}]},
+ {extauth_pool_size,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("The option defines the number of instances of the same "
+ "external program to start for better load balancing. "
+ "The default is the number of available CPU cores.")}},
+ {extauth_program,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("Indicate in this option the full path to the external "
+ "authentication script. The script must be executable by ejabberd.")}},
+ {fqdn,
+ #{value => ?T("Domain"),
+ desc =>
+ ?T("A fully qualified domain name that will be used in "
+ "SASL DIGEST-MD5 authentication. The default is detected "
+ "automatically.")}},
+ {hide_sensitive_log_data,
+ #{value => "true | false",
+ desc =>
+ ?T("A privacy option to not log sensitive data "
+ "(mostly IP addresses). The default value "
+ "is 'false' for backward compatibility.")}},
+ {host_config,
+ #{value => "{Host: Options}",
+ desc =>
+ ?T("The option is used to redefine 'Options' for virtual host "
+ "'Host'. In the example below LDAP authentication method "
+ "will be used on virtual host 'domain.tld' and SQL method "
+ "will be used on virtual host 'example.org'."),
+ example =>
+ ["hosts:",
+ " - domain.tld",
+ " - example.org",
+ "",
+ "auth_method:",
+ " - sql",
+ "",
+ "host_config:",
+ " domain.tld:",
+ " auth_method:",
+ " - ldap"]}},
+ {include_config_file,
+ #{value => "[Filename, ...\\] | {Filename: Options}",
+ desc =>
+ ?T("Read additional configuration from 'Filename'. If the "
+ "value is provided in 'pass:[{Filename: Options}]' format, the "
+ "'Options' must be one of the following:")},
+ [{disallow,
+ #{value => "[OptionName, ...]",
+ desc =>
+ ?T("Disallows the usage of those options in the included "
+ "file 'Filename'. The options that match this criteria "
+ "are not accepted. The default value is an empty list.")}},
+ {allow_only,
+ #{value => "[OptionName, ...]",
+ desc =>
+ ?T("Allows only the usage of those options in the included "
+ "file 'Filename'. The options that do not match this "
+ "criteria are not accepted. The default value is to include "
+ "all options.")}}]},
+ {language,
+ #{value => ?T("Language"),
+ desc =>
+ ?T("The option defines the default language of server strings "
+ "that can be seen by XMPP clients. If an XMPP client does not "
+ "possess 'xml:lang' attribute, the specified language is used.")}},
+ {ldap_servers,
+ #{value => "[Host, ...]",
+ desc =>
+ ?T("A list of IP addresses or DNS names of your LDAP servers. "
+ "The default value is '[localhost]'.")}},
+ {ldap_backups,
+ #{value => "[Host, ...]",
+ desc =>
+ ?T("A list of IP addresses or DNS names of LDAP backup servers. "
+ "When no servers listed in 'ldap_servers' option are reachable, "
+ "ejabberd will try to connect to these backup servers. "
+ "The default is an empty list, i.e. no backup servers specified. "
+ "WARNING: ejabberd doesn't try to reconnect back to the main "
+ "servers when they become operational again, so the only way "
+ "to restore these connections is to restart ejabberd. This "
+ "limitation might be fixed in future releases.")}},
+ {ldap_encrypt,
+ #{value => "tls | none",
+ desc =>
+ ?T("Whether to encrypt LDAP connection using TLS or not. "
+ "The default value is 'none'. NOTE: STARTTLS encryption "
+ "is not supported.")}},
+ {ldap_tls_certfile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("A path to a file containing PEM encoded certificate "
+ "along with PEM encoded private key. This certificate "
+ "will be provided by ejabberd when TLS enabled for "
+ "LDAP connections. There is no default value, which means "
+ "no client certificate will be sent.")}},
+ {ldap_tls_verify,
+ #{value => "false | soft | hard",
+ desc =>
+ ?T("This option specifies whether to verify LDAP server "
+ "certificate or not when TLS is enabled. When 'hard' is set, "
+ "ejabberd doesn't proceed if the certificate is invalid. "
+ "When 'soft' is set, ejabberd proceeds even if the check has failed. "
+ "The default is 'false', which means no checks are performed.")}},
+ {ldap_tls_cacertfile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("A path to a file containing PEM encoded CA certificates. "
+ "This option is required when TLS verification is enabled.")}},
+ {ldap_tls_depth,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("Specifies the maximum verification depth when TLS verification "
+ "is enabled, i.e. how far in a chain of certificates the "
+ "verification process can proceed before the verification "
+ "is considered to be failed. Peer certificate = 0, "
+ "CA certificate = 1, higher level CA certificate = 2, etc. "
+ "The value '2' thus means that a chain can at most contain "
+ "peer cert, CA cert, next CA cert, and an additional CA cert. "
+ "The default value is '1'.")}},
+ {ldap_port,
+ #{value => "1..65535",
+ desc =>
+ ?T("Port to connect to your LDAP server. The default port is "
+ "'389' if encryption is disabled and '636' if encryption is "
+ "enabled.")}},
+ {ldap_rootdn,
+ #{value => "RootDN",
+ desc =>
+ ?T("Bind Distinguished Name. The default value is an empty "
+ "string, which means \"anonymous connection\".")}},
+ {ldap_password,
+ #{value => ?T("Password"),
+ desc =>
+ ?T("Bind password. The default value is an empty string.")}},
+ {ldap_deref_aliases,
+ #{value => "never | always | finding | searching",
+ desc =>
+ ?T("Whether to dereference aliases or not. "
+ "The default value is 'never'.")}},
+ {ldap_base,
+ #{value => "Base",
+ desc =>
+ ?T("LDAP base directory which stores users accounts. "
+ "There is no default value: you must set the option "
+ "in order for LDAP connections to work properly.")}},
+ {ldap_uids,
+ #{value => "[Attr\\] | {Attr: AttrFormat}",
+ desc =>
+ ?T("LDAP attributes which hold a list of attributes to use "
+ "as alternatives for getting the JID, where 'Attr' is "
+ "an LDAP attribute which holds the user's part of the JID and "
+ "'AttrFormat' must contain one and only one pattern variable "
+ "\"%u\" which will be replaced by the user's part of the JID. "
+ "For example, \"%u@example.org\". If the value is in the form "
+ "of '[Attr]' then 'AttrFormat' is assumed to be \"%u\".")}},
+ {ldap_filter,
+ #{value => ?T("Filter"),
+ desc =>
+ ?T("An LDAP filter as defined in "
+ "https://tools.ietf.org/html/rfc4515[RFC4515]. "
+ "There is no default value. Example: "
+ "\"(&(objectClass=shadowAccount)(memberOf=Jabber Users))\". "
+ "NOTE: don't forget to close brackets and don't use superfluous "
+ "whitespaces. Also you must not use \"uid\" attribute in the "
+ "filter because this attribute will be appended to the filter "
+ "automatically.")}},
+ {ldap_dn_filter,
+ #{value => "{Filter: FilterAttrs}",
+ desc =>
+ ?T("This filter is applied on the results returned by the main "
+ "filter. The filter performs an additional LDAP lookup to make "
+ "the complete result. This is useful when you are unable to "
+ "define all filter rules in 'ldap_filter'. You can define "
+ "\"%u\", \"%d\", \"%s\" and \"%D\" pattern variables in 'Filter': "
+ "\"%u\" is replaced by a user's part of the JID, \"%d\" is "
+ "replaced by the corresponding domain (virtual host), all \"%s\" "
+ "variables are consecutively replaced by values from the attributes "
+ "in 'FilterAttrs' and \"%D\" is replaced by Distinguished Name from "
+ "the result set. There is no default value, which means the "
+ "result is not filtered. WARNING: Since this filter makes "
+ "additional LDAP lookups, use it only as the last resort: "
+ "try to define all filter rules in 'ldap_filter' option if possible."),
+ example =>
+ ["ldap_dn_filter:",
+ " \"(&(name=%s)(owner=%D)(user=%u@%d))\": [sn]"]}},
+ {log_rotate_count,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("The number of rotated log files to keep. "
+ "The default value is '1'.")}},
+ {log_rotate_size,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("The size (in bytes) of a log file to trigger rotation. "
+ "The default value is '10485760' (10 Mb).")}},
+ {max_fsm_queue,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("This option specifies the maximum number of elements "
+ "in the queue of the FSM (Finite State Machine). Roughly "
+ "speaking, each message in such queues represents one "
+ "XML stanza queued to be sent into its relevant outgoing "
+ "stream. If queue size reaches the limit (because, for "
+ "example, the receiver of stanzas is too slow), the FSM "
+ "and the corresponding connection (if any) will be terminated "
+ "and error message will be logged. The reasonable value for "
+ "this option depends on your hardware configuration. "
+ "The allowed values are positive integers. "
+ "The default value is '10000'.")}},
+ {negotiation_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("Time to wait for an XMPP stream negotiation to complete. "
+ "When timeout occurs, the corresponding XMPP stream is closed. "
+ "The default value is '30' seconds.")}},
+ {net_ticktime,
+ #{value => "timeout()",
+ desc =>
+ ?T("This option can be used to tune tick time parameter of "
+ "'net_kernel'. It tells Erlang VM how often nodes should check "
+ "if intra-node communication was not interrupted. This option "
+ "must have identical value on all nodes, or it will lead to subtle "
+ "bugs. Usually leaving default value of this is option is best, "
+ "tweak it only if you know what you are doing. "
+ "The default value is '1' minute.")}},
+ {new_sql_schema,
+ #{value => "true | false",
+ desc =>
+ {?T("Whether to use 'new' SQL schema. All schemas are located "
+ "at <https://github.com/processone/ejabberd/tree/~s/sql>. "
+ "There are two schemas available. The default lecacy schema "
+ "allows to store one XMPP domain into one ejabberd database. "
+ "The 'new' schema allows to handle several XMPP domains in a "
+ "single ejabberd database. Using this 'new' schema is best when "
+ "serving several XMPP domains and/or changing domains from "
+ "time to time. This avoid need to manage several databases and "
+ "handle complex configuration changes. The default depends on "
+ "configuration flag '--enable-new-sql-schema' which is set "
+ "at compile time."),
+ [binary:part(ejabberd_config:version(), {0,5})]}}},
+ {oauth_access,
+ #{value => ?T("AccessName"),
+ desc => ?T("By default creating OAuth tokens is not allowed. "
+ "To define which users can create OAuth tokens, "
+ "you can refer to an ejabberd access rule in the "
+ "'oauth_access' option. Use 'all' to allow everyone "
+ "to create tokens.")}},
+ {oauth_cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as 'cache_life_time', but applied to OAuth cache "
+ "only. If not set, the value from 'cache_life_time' will be used.")}},
+ {oauth_cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'cache_missed', but applied to OAuth cache "
+ "only. If not set, the value from 'cache_missed' will be used.")}},
+ {oauth_cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as 'cache_size', but applied to OAuth cache "
+ "only. If not set, the value from 'cache_size' will be used.")}},
+ {oauth_use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'use_cache', but applied to OAuth cache "
+ "only. If not set, the value from 'use_cache' will be used.")}},
+ {oauth_db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Database backend to use for OAuth authentication. "
+ "The default value is picked from 'default_db' option, or "
+ "if it's not set, 'mnesia' will be used.")}},
+ {oauth_expire,
+ #{value => "timeout()",
+ desc =>
+ ?T("Time during which the OAuth token is valid, in seconds. "
+ "After that amount of time, the token expires and the delegated "
+ "credential cannot be used and is removed from the database. "
+ "The default is '4294967' seconds.")}},
+ {oom_killer,
+ #{value => "true | false",
+ desc =>
+ ?T("Enable or disable OOM (out-of-memory) killer. "
+ "When system memory raises above the limit defined in "
+ "'oom_watermark' option, ejabberd triggers OOM killer "
+ "to terminate most memory consuming Erlang processes. "
+ "Note that in order to maintain functionality, ejabberd only "
+ "attempts to kill transient processes, such as those managing "
+ "client sessions, s2s or database connections. "
+ "The default value is 'true'.")}},
+ {oom_queue,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("Trigger OOM killer when some of the running Erlang processes "
+ "have messages queue above this 'Size'. Note that "
+ "such processes won't be killed if 'oom_killer' option is set "
+ "to 'false' or if 'oom_watermark' is not reached yet.")}},
+ {oom_watermark,
+ #{value => ?T("Percent"),
+ desc =>
+ ?T("A percent of total system memory consumed at which "
+ "OOM killer should be activated with some of the processes "
+ "possibly be killed (see 'oom_killer' option). Later, when "
+ "memory drops below this 'Percent', OOM killer is deactivated. "
+ "The default value is '80' percents.")}},
+ {outgoing_s2s_families,
+ #{value => "[ipv4 | ipv6, ...]",
+ desc =>
+ ?T("Specify which address families to try, in what order. "
+ "The default is '[ipv4, ipv6]' which means it first tries "
+ "connecting with IPv4, if that fails it tries using IPv6.")}},
+ {outgoing_s2s_port,
+ #{value => "1..65535",
+ desc =>
+ ?T("A port number to use for outgoing s2s connections when the target "
+ "server doesn't have an SRV record. The default value is '5269'.")}},
+ {outgoing_s2s_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("The timeout in seconds for outgoing S2S connection attempts. "
+ "The default value is '10' seconds.")}},
+ {pam_service,
+ #{value => ?T("Name"),
+ desc =>
+ ?T("This option defines the PAM service name. Refer to the PAM "
+ "documentation of your operation system for more information. "
+ "The default value is 'ejabberd'.")}},
+ {pam_userinfotype,
+ #{value => "username | jid",
+ desc =>
+ ?T("This option defines what type of information about the "
+ "user ejabberd provides to the PAM service: only the username, "
+ "or the user's JID. Default is 'username'.")}},
+ {pgsql_users_number_estimate,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to use PostgreSQL estimation when counting registered "
+ "users. The default value is 'false'.")}},
+ {queue_dir,
+ #{value => ?T("Directory"),
+ desc =>
+ ?T("If 'queue_type' option is set to 'file', use this 'Directory' "
+ "to store file queues. The default is to keep queues inside "
+ "Mnesia directory.")}},
+ {redis_connect_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("A timeout to wait for the connection to be re-established "
+ "to the Redis server. The default is '1 second'.")}},
+ {redis_db,
+ #{value => ?T("Number"),
+ desc => ?T("Redis database number. The default is '0'.")}},
+ {redis_password,
+ #{value => ?T("Password"),
+ desc =>
+ ?T("The password to the Redis server. "
+ "The default is an empty string, i.e. no password.")}},
+ {redis_pool_size,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("The number of simultaneous connections to the Redis server. "
+ "The default value is '10'.")}},
+ {redis_port,
+ #{value => "1..65535",
+ desc =>
+ ?T("The port where the Redis server is accepting connections. "
+ "The default is '6379'.")}},
+ {redis_queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("The type of request queue for the Redis server. "
+ "See description of 'queue_type' option for the explanation. "
+ "The default value is the value defined in 'queue_type' "
+ "or 'ram' if the latter is not set.")}},
+ {redis_server,
+ #{value => ?T("Hostname"),
+ desc =>
+ ?T("A hostname or an IP address of the Redis server. "
+ "The default is 'localhost'.")}},
+ {registration_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("This is a global option for module 'mod_register'. "
+ "It limits the frequency of registrations from a given "
+ "IP or username. So, a user that tries to register a "
+ "new account from the same IP address or JID during "
+ "this time after their previous registration "
+ "will receive an error with the corresponding explanation. "
+ "To disable this limitation, set the value to 'infinity'. "
+ "The default value is '600 seconds'.")}},
+ {resource_conflict,
+ #{value => "setresource | closeold | closenew",
+ desc =>
+ ?T("NOTE: this option is deprecated and may be removed "
+ "anytime in the future versions. The possible values "
+ "match exactly the three possibilities described in "
+ "https://tools.ietf.org/html/rfc6120#section-7.7.2.2"
+ "[XMPP Core: section 7.7.2.2]. "
+ "The default value is 'closeold'. If the client "
+ "uses old Jabber Non-SASL authentication (XEP-0078), "
+ "then this option is not respected, and the action performed "
+ "is 'closeold'.")}},
+ {router_cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as 'cache_life_time', but applied to routing table cache "
+ "only. If not set, the value from 'cache_life_time' will be used.")}},
+ {router_cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'cache_missed', but applied to routing table cache "
+ "only. If not set, the value from 'cache_missed' will be used.")}},
+ {router_cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as 'cache_size', but applied to routing table cache "
+ "only. If not set, the value from 'cache_size' will be used.")}},
+ {router_db_type,
+ #{value => "mnesia | sql | redis",
+ desc =>
+ ?T("Database backend to use for routing information. "
+ "The default value is picked from 'default_ram_db' option, or "
+ "if it's not set, 'mnesia' will be used.")}},
+ {router_use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'use_cache', but applied to routing table cache "
+ "only. If not set, the value from 'use_cache' will be used.")}},
+ {rpc_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("A timeout for remote function calls between nodes "
+ "in an ejabberd cluster. You should probably never change "
+ "this value since those calls are used for internal needs "
+ "only. The default value is '5' seconds.")}},
+ {s2s_access,
+ #{value => ?T("Access"),
+ desc =>
+ ?T("The access rule to restrict server-to-server connections. "
+ "The default value is 'all' which means no restrictions "
+ "are applied.")}},
+ {s2s_cafile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("A path to a file with CA root certificates that will "
+ "be used to authenticate s2s connections. If not set "
+ "the value of 'ca_file' will be used.")}},
+ {s2s_ciphers,
+ #{value => "[Cipher, ...]",
+ desc =>
+ ?T("A list of OpenSSL ciphers to use for s2s connections. "
+ "The default value is shown in the example below:"),
+ example =>
+ ["s2s_ciphers:",
+ " - HIGH",
+ " - \"!aNULL\"",
+ " - \"!eNULL\"",
+ " - \"!3DES\"",
+ " - \"@STRENGTH\""]}},
+ {s2s_dhfile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("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.")}},
+ {s2s_protocol_options,
+ #{value => "[Option, ...]",
+ desc =>
+ ?T("List of general SSL options to use for s2s connections. "
+ "These map to OpenSSL's 'set_options()'. The default value is "
+ "shown in the example below:"),
+ example =>
+ ["s2s_protocol_options:",
+ " - no_sslv3",
+ " - cipher_server_preference",
+ " - no_compression"]}},
+ {s2s_tls_compression,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to enable or disable TLS compression for s2s connections. "
+ "The default value is 'false'.")}},
+ {s2s_dns_retries,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("DNS resolving retries. The default value is '2'.")}},
+ {s2s_dns_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("The timeout for DNS resolving. The default value is '10' seconds.")}},
+ {s2s_max_retry_delay,
+ #{value => "timeout()",
+ desc =>
+ ?T("The maximum allowed delay for s2s connection retry to connect after a "
+ "failed connection attempt. The default value is '300' seconds "
+ "(5 minutes).")}},
+ {s2s_queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("The type of a queue for s2s packets. "
+ "See description of 'queue_type' option for the explanation. "
+ "The default value is the value defined in 'queue_type' "
+ "or 'ram' if the latter is not set.")}},
+ {s2s_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("A time to wait before closing an idle s2s connection. "
+ "The default value is '10' minutes.")}},
+ {s2s_use_starttls,
+ #{value => "true | false | optional | required",
+ desc =>
+ ?T("Whether to use STARTTLS for s2s connections. "
+ "The value of 'false' means STARTTLS is prohibited. "
+ "The value of 'true' or 'optional' means STARTTLS is enabled "
+ "but plain connections are still allowed. And the value of "
+ "'required' means that only STARTTLS connections are allowed. "
+ "The default value is 'false' (for historical reasons).")}},
+ {s2s_zlib,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to use 'zlib' compression (as defined in "
+ "https://xmpp.org/extensions/xep-0138.html[XEP-0138]) or not. "
+ "The default value is 'false'. WARNING: this type "
+ "of compression is nowadays considered insecure.")}},
+ {shaper,
+ #{value => "{ShaperName: Rate}",
+ desc =>
+ ?T("The option defines a set of shapers. Every shaper is assigned "
+ "a name 'ShaperName' that can be used in other parts of the "
+ "configuration file, such as 'shaper_rules' option. The shaper "
+ "itself is defined by its 'Rate', where 'Rate' stands for the "
+ "maximum allowed incoming rate in **bytes** per second. "
+ "When a connection exceeds this limit, ejabberd stops reading "
+ "from the socket until the average rate is again below the "
+ "allowed maximum. In the example below shaper 'normal' limits "
+ "the traffic speed to 1,000 bytes/sec and shaper 'fast' limits "
+ "the traffic speed to 50,000 bytes/sec:"),
+ example =>
+ ["shaper:",
+ " normal: 1000",
+ " fast: 50000"]}},
+ {shaper_rules,
+ #{value => "{ShaperRuleName: {Number|ShaperName: ACLRule|ACLName}}",
+ desc =>
+ ?T("An entry allowing to declaring shaper to use for matching user/hosts. "
+ "Semantics is similar to 'access_rules' option, the only difference is "
+ "that instead using 'allow' or 'deny', a name of a shaper (defined in "
+ "'shaper' option) or a positive number should be used."),
+ example =>
+ ["shaper_rules:",
+ " connections_limit:",
+ " 10:",
+ " user: peter@example.com",
+ " 100: admin",
+ " 5: all",
+ " download_speed:",
+ " fast: admin",
+ " slow: anonymous_users",
+ " normal: all",
+ " log_days: 30"]}},
+ {sm_cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as 'cache_life_time', but applied to client sessions table cache "
+ "only. If not set, the value from 'cache_life_time' will be used.")}},
+ {sm_cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'cache_missed', but applied to client sessions table cache "
+ "only. If not set, the value from 'cache_missed' will be used.")}},
+ {sm_cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as 'cache_size', but applied to client sessions table cache "
+ "only. If not set, the value from 'cache_size' will be used.")}},
+ {sm_db_type,
+ #{value => "mnesia | sql | redis",
+ desc =>
+ ?T("Database backend to use for client sessions information. "
+ "The default value is picked from 'default_ram_db' option, or "
+ "if it's not set, 'mnesia' will be used.")}},
+ {sm_use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as 'use_cache', but applied to client sessions table cache "
+ "only. If not set, the value from 'use_cache' will be used.")}},
+ {sql_type,
+ #{value => "mysql | pgsql | sqlite | mssql | odbc",
+ desc =>
+ ?T("The type of an SQL connection. The default is 'odbc'.")}},
+ {sql_connect_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("A time to wait for connection to an SQL server to be "
+ "established. The default value is '5' seconds.")}},
+ {sql_database,
+ #{value => ?T("Database"),
+ desc =>
+ ?T("An SQL database name. For SQLite this must be a full "
+ "path to a database file. The default value is 'ejabberd'.")}},
+ {sql_keepalive_interval,
+ #{value => "timeout()",
+ desc =>
+ ?T("An interval to make a dummy SQL request to keep alive the "
+ "connections to the database. There is no default value, so no "
+ "keepalive requests are made.")}},
+ {sql_password,
+ #{value => ?T("Password"),
+ desc =>
+ ?T("The password for SQL authentication. The default is empty string.")}},
+ {sql_pool_size,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("A number of connections to the SQL server. By default ejabberd opens "
+ "10 connections to the database for each virtual host. WARNING: "
+ "for SQLite this value is '1' by default and it's not recommended "
+ "to change it due to potential race conditions.")}},
+ {sql_port,
+ #{value => "1..65535",
+ desc =>
+ ?T("The port where the SQL server is accepting connections. "
+ "The default is '3306' for MySQL, '5432' for PostgreSQL and "
+ "'1433' for MSSQL. The option has no effect for SQLite.")}},
+ {sql_query_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("A time to wait for an SQL query response. "
+ "The default value is '60' seconds.")}},
+ {sql_queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("The type of a request queue for the SQL server. "
+ "See description of 'queue_type' option for the explanation. "
+ "The default value is the value defined in 'queue_type' "
+ "or 'ram' if the latter is not set.")}},
+ {sql_server,
+ #{value => ?T("Host"),
+ desc =>
+ ?T("A hostname or an IP address of the SQL server. "
+ "The default value is 'localhost'.")}},
+ {sql_ssl,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to use SSL encrypted connections to the "
+ "SQL server. The option is only available for "
+ "PostgreSQL. The default value is 'false'.")}},
+ {sql_ssl_cafile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("A path to a file with CA root certificates that will "
+ "be used to verify SQL connections. Implies 'sql_ssl' "
+ "and 'sql_ssl_verify' options are set to 'true'. "
+ "There is no default which means "
+ "certificate verification is disabled.")}},
+ {sql_ssl_certfile,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("A path to a certificate file that will be used "
+ "for SSL connections to the SQL server. Implies 'sql_ssl' "
+ "option is set to 'true'. There is no default which means "
+ "ejabberd won't provide a client certificate to the SQL "
+ "server.")}},
+ {sql_ssl_verify,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to verify SSL connection to the SQL server against "
+ "CA root certificates defined in 'sql_ssl_cafile' option. "
+ "Implies 'sql_ssl' option is set to 'true'. "
+ "The default value is 'false'.")}},
+ {sql_start_interval,
+ #{value => "timeout()",
+ desc =>
+ ?T("A time to wait before retrying to restore failed SQL connection. "
+ "The default value is '30' seconds.")}},
+ {sql_username,
+ #{value => ?T("Username"),
+ desc =>
+ ?T("A user name for SQL authentication. "
+ "The default value is 'ejabberd'.")}},
+ {trusted_proxies,
+ #{value => "all | [Network1, Network2, ...]",
+ desc =>
+ ?T("Specify what proxies are trusted when an HTTP request "
+ "contains the header 'X-Forwarded-For'. You can specify "
+ "'all' to allow all proxies, or specify a list of IPs, "
+ "possibly with masks. The default value is an empty list. "
+ "This allows, if enabled, to be able to know the real IP "
+ "of the request, for admin purpose, or security configuration "
+ "(for example using 'mod_fail2ban'). IMPORTANT: The proxy MUST "
+ "be configured to set the 'X-Forwarded-For' header if you "
+ "enable this option as, otherwise, the client can set it "
+ "itself and as a result the IP value cannot be trusted for "
+ "security rules in ejabberd.")}},
+ {validate_stream,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to validate any incoming XML packet according "
+ "to the schemas of "
+ "https://github.com/processone/xmpp#supported-xmpp-elements"
+ "[supported XMPP extensions]. WARNING: the validation is only "
+ "intended for the use by client developers - don't enable "
+ "it in production environment. The default value is 'false'.")}},
+ {websocket_origin,
+ #{value => "ignore | URL",
+ desc =>
+ ?T("This option enables validation for 'Origin' 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 'ignore'. An example value of the 'URL' is "
+ "\"https://test.example.org:8081\".")}},
+ {websocket_ping_interval,
+ #{value => "timeout()",
+ desc =>
+ ?T("Defines time between pings sent by the server to a client "
+ "(Websocket level protocol pings are used for this) to keep "
+ "a connection active. If the client doesn't respond to two "
+ "consecutive pings, the connection will be assumed as closed. "
+ "The value of '0' can be used to disable the feature. This option "
+ "makes the server sending pings only for connections using the RFC "
+ "compliant protocol. For older style connections the server "
+ "expects that whitespace pings would be used for this purpose. "
+ "The default value is '60' seconds.")}},
+ {websocket_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("Amount of time without any communication after which the "
+ "connection would be closed. The default value is '300' seconds.")}}].
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl
index 0722e137e..737ed502f 100644
--- a/src/ejabberd_piefxis.erl
+++ b/src/ejabberd_piefxis.erl
@@ -5,7 +5,7 @@
%%% Created : 17 Jul 2008 by Pablo Polvorin <pablo.polvorin@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -193,9 +193,9 @@ parse_scram_password(PassData) ->
Split = binary:split(PassData, <<",">>, [global]),
[StoredKeyB64, ServerKeyB64, SaltB64, IterationCountBin] = Split,
#scram{
- storedkey = StoredKeyB64,
- serverkey = ServerKeyB64,
- salt = SaltB64,
+ storedkey = base64:decode(StoredKeyB64),
+ serverkey = base64:decode(ServerKeyB64),
+ salt = base64:decode(SaltB64),
iterationcount = (binary_to_integer(IterationCountBin))
}.
@@ -296,17 +296,26 @@ process(#state{xml_stream_state = XMLStreamState, fd = Fd} = State) ->
end.
process_els(State) ->
+ Els = gather_els(State, []),
+ process_els(State, lists:reverse(Els)).
+
+gather_els(State, List) ->
receive
{'$gen_event', El} ->
- case process_el(El, State) of
- {ok, NewState} ->
- process_els(NewState);
- Err ->
- Err
- end
+ gather_els(State, [El | List])
after 0 ->
- {ok, State}
- end.
+ List
+end.
+
+process_els(State, [El | Tail]) ->
+ case process_el(El, State) of
+ {ok, NewState} ->
+ process_els(NewState, Tail);
+ Err ->
+ Err
+ end;
+process_els(State, []) ->
+ {ok, State}.
process_el({xmlstreamstart, <<"server-data">>, Attrs}, State) ->
case fxml:get_attr_s(<<"xmlns">>, Attrs) of
diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl
index f6ecf6a63..563828769 100644
--- a/src/ejabberd_pkix.erl
+++ b/src/ejabberd_pkix.erl
@@ -3,7 +3,7 @@
%%% Created : 4 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl
index f999cc96e..9c8f686ca 100644
--- a/src/ejabberd_redis.erl
+++ b/src/ejabberd_redis.erl
@@ -4,7 +4,7 @@
%%% Created : 8 May 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_redis_sup.erl b/src/ejabberd_redis_sup.erl
index 7bba0882a..a78ba8164 100644
--- a/src/ejabberd_redis_sup.erl
+++ b/src/ejabberd_redis_sup.erl
@@ -3,7 +3,7 @@
%%% Created : 6 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_regexp.erl b/src/ejabberd_regexp.erl
index 9e1a979a4..6c0fc462a 100644
--- a/src/ejabberd_regexp.erl
+++ b/src/ejabberd_regexp.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Dec 2011 by Badlop
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl
index 2377f9067..7fb4cae0b 100644
--- a/src/ejabberd_router.erl
+++ b/src/ejabberd_router.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_mnesia.erl b/src/ejabberd_router_mnesia.erl
index 98d03cdd9..87bbb0107 100644
--- a/src/ejabberd_router_mnesia.erl
+++ b/src/ejabberd_router_mnesia.erl
@@ -2,7 +2,7 @@
%%% Created : 11 Jan 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl
index 5a0988448..79e03a9a2 100644
--- a/src/ejabberd_router_multicast.erl
+++ b/src/ejabberd_router_multicast.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Aug 2007 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_redis.erl b/src/ejabberd_router_redis.erl
index b0f79d2a3..d2639f4c0 100644
--- a/src/ejabberd_router_redis.erl
+++ b/src/ejabberd_router_redis.erl
@@ -3,7 +3,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl
index e18857a05..d00f50522 100644
--- a/src/ejabberd_router_sql.erl
+++ b/src/ejabberd_router_sql.erl
@@ -3,7 +3,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl
index f8dafc970..afe941f9a 100644
--- a/src/ejabberd_s2s.erl
+++ b/src/ejabberd_s2s.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index f351626b1..2c838f76e 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -2,7 +2,7 @@
%%% Created : 12 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index 29833bed1..7bbaf870c 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -2,7 +2,7 @@
%%% Created : 16 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index 65a044f1d..92350956d 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -2,7 +2,7 @@
%%% Created : 11 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_shaper.erl b/src/ejabberd_shaper.erl
index 741f3977d..1ec87d3bb 100644
--- a/src/ejabberd_shaper.erl
+++ b/src/ejabberd_shaper.erl
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sip.erl b/src/ejabberd_sip.erl
index 7e62ee453..e98693ea5 100644
--- a/src/ejabberd_sip.erl
+++ b/src/ejabberd_sip.erl
@@ -5,7 +5,7 @@
%%% Created : 30 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index efcbf3657..6202614db 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm_mnesia.erl b/src/ejabberd_sm_mnesia.erl
index 9626ba3d6..cd749e339 100644
--- a/src/ejabberd_sm_mnesia.erl
+++ b/src/ejabberd_sm_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 9 Mar 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm_redis.erl b/src/ejabberd_sm_redis.erl
index d26e2d249..ed700f15f 100644
--- a/src/ejabberd_sm_redis.erl
+++ b/src/ejabberd_sm_redis.erl
@@ -4,7 +4,7 @@
%%% Created : 11 Mar 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl
index 2a048f006..321c4f37e 100644
--- a/src/ejabberd_sm_sql.erl
+++ b/src/ejabberd_sm_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 9 Mar 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl
index 5bf371654..ebbd9a5cb 100644
--- a/src/ejabberd_sql.erl
+++ b/src/ejabberd_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -227,6 +227,8 @@ escape_like_arg(S) when is_binary(S) ->
escape_like_arg($%) -> <<"\\%">>;
escape_like_arg($_) -> <<"\\_">>;
escape_like_arg($\\) -> <<"\\\\">>;
+escape_like_arg($[) -> <<"\\[">>; % For MSSQL
+escape_like_arg($]) -> <<"\\]">>;
escape_like_arg(C) when is_integer(C), C >= 0, C =< 255 -> <<C>>.
escape_like_arg_circumflex(S) when is_binary(S) ->
@@ -718,7 +720,8 @@ generic_escape() ->
boolean = fun(true) -> <<"1">>;
(false) -> <<"0">>
end,
- in_array_string = fun(X) -> <<"'", (escape(X))/binary, "'">> end
+ in_array_string = fun(X) -> <<"'", (escape(X))/binary, "'">> end,
+ like_escape = fun() -> <<"">> end
}.
pgsql_sql_query(SQLQuery) ->
@@ -736,7 +739,8 @@ pgsql_escape() ->
boolean = fun(true) -> <<"1">>;
(false) -> <<"0">>
end,
- in_array_string = fun(X) -> <<"E'", (escape(X))/binary, "'">> end
+ in_array_string = fun(X) -> <<"E'", (escape(X))/binary, "'">> end,
+ like_escape = fun() -> <<"">> end
}.
sqlite_sql_query(SQLQuery) ->
@@ -754,7 +758,8 @@ sqlite_escape() ->
boolean = fun(true) -> <<"1">>;
(false) -> <<"0">>
end,
- in_array_string = fun(X) -> <<"'", (standard_escape(X))/binary, "'">> end
+ in_array_string = fun(X) -> <<"'", (standard_escape(X))/binary, "'">> end,
+ like_escape = fun() -> <<"ESCAPE '\\'">> end
}.
standard_escape(S) ->
@@ -767,9 +772,18 @@ mssql_sql_query(SQLQuery) ->
sqlite_sql_query(SQLQuery).
pgsql_prepare(SQLQuery, State) ->
- Escape = #sql_escape{_ = fun(X) -> X end},
- N = length((SQLQuery#sql_query.args)(Escape)),
- Args = [<<$$, (integer_to_binary(I))/binary>> || I <- lists:seq(1, N)],
+ Escape = #sql_escape{_ = fun(_) -> arg end,
+ like_escape = fun() -> escape end},
+ {RArgs, _} =
+ lists:foldl(
+ fun(arg, {Acc, I}) ->
+ {[<<$$, (integer_to_binary(I))/binary>> | Acc], I + 1};
+ (escape, {Acc, I}) ->
+ {[<<"">> | Acc], I}
+ end, {[], 1}, (SQLQuery#sql_query.args)(Escape)),
+ Args = lists:reverse(RArgs),
+ %N = length((SQLQuery#sql_query.args)(Escape)),
+ %Args = [<<$$, (integer_to_binary(I))/binary>> || I <- lists:seq(1, N)],
Query = (SQLQuery#sql_query.format_query)(Args),
pgsql:prepare(State#state.db_ref, SQLQuery#sql_query.hash, Query).
@@ -779,13 +793,15 @@ pgsql_execute_escape() ->
boolean = fun(true) -> "1";
(false) -> "0"
end,
- in_array_string = fun(X) -> <<"\"", (escape(X))/binary, "\"">> end
+ in_array_string = fun(X) -> <<"\"", (escape(X))/binary, "\"">> end,
+ like_escape = fun() -> ignore end
}.
pgsql_execute_sql_query(SQLQuery, State) ->
Args = (SQLQuery#sql_query.args)(pgsql_execute_escape()),
+ Args2 = lists:filter(fun(ignore) -> false; (_) -> true end, Args),
ExecuteRes =
- pgsql:execute(State#state.db_ref, SQLQuery#sql_query.hash, Args),
+ pgsql:execute(State#state.db_ref, SQLQuery#sql_query.hash, Args2),
% {T, ExecuteRes} =
% timer:tc(pgsql, execute, [State#state.db_ref, SQLQuery#sql_query.hash, Args]),
% io:format("T ~ts ~p~n", [SQLQuery#sql_query.hash, T]),
@@ -985,12 +1001,18 @@ pgsql_execute_to_odbc(_) -> {updated, undefined}.
%% part of init/1
%% Open a database connection to MySQL
-mysql_connect(Server, Port, DB, Username, Password, ConnectTimeout, _, _) ->
+mysql_connect(Server, Port, DB, Username, Password, ConnectTimeout, Transport, _) ->
+ SSLOpts = case Transport of
+ ssl ->
+ [ssl_required];
+ _ ->
+ []
+ end,
case p1_mysql_conn:start(binary_to_list(Server), Port,
binary_to_list(Username),
binary_to_list(Password),
binary_to_list(DB),
- ConnectTimeout, fun log/3)
+ ConnectTimeout, fun log/3, SSLOpts)
of
{ok, Ref} ->
p1_mysql_conn:fetch(
@@ -1096,6 +1118,8 @@ warn_if_ssl_unsupported(tcp, _) ->
ok;
warn_if_ssl_unsupported(ssl, pgsql) ->
ok;
+warn_if_ssl_unsupported(ssl, mysql) ->
+ ok;
warn_if_ssl_unsupported(ssl, Type) ->
?WARNING_MSG("SSL connection is not supported for ~ts", [Type]).
diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl
index 0896b4b1a..5ac027f9b 100644
--- a/src/ejabberd_sql_pt.erl
+++ b/src/ejabberd_sql_pt.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Jan 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -354,6 +354,22 @@ parse1([$%, $( | S], Acc, State) ->
used_vars = [Name | State2#state.used_vars]}
end,
parse1(S1, [], State4);
+parse1("%ESCAPE" ++ S, Acc, State) ->
+ State1 = append_string(lists:reverse(Acc), State),
+ Convert =
+ erl_syntax:application(
+ erl_syntax:record_access(
+ erl_syntax:variable(?ESCAPE_VAR),
+ erl_syntax:atom(?ESCAPE_RECORD),
+ erl_syntax:atom(like_escape)),
+ []),
+ Var = State1#state.param_pos,
+ State2 =
+ State1#state{'query' = [{var, Var} | State1#state.'query'],
+ args = [Convert | State1#state.args],
+ params = [Var | State1#state.params],
+ param_pos = State1#state.param_pos + 1},
+ parse1(S, [], State2);
parse1([C | S], Acc, State) ->
parse1(S, [C | Acc], State).
diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl
index 074080f5f..026c3fab6 100644
--- a/src/ejabberd_sql_sup.erl
+++ b/src/ejabberd_sql_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl
index f5df533bd..347598e6d 100644
--- a/src/ejabberd_stun.erl
+++ b/src/ejabberd_stun.erl
@@ -5,7 +5,7 @@
%%% Created : 8 May 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl
index 05cbef017..a0c3e2305 100644
--- a/src/ejabberd_sup.erl
+++ b/src/ejabberd_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl
index 7e7e52b8e..35ef48d49 100644
--- a/src/ejabberd_system_monitor.erl
+++ b/src/ejabberd_system_monitor.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Mar 2007 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_tmp_sup.erl b/src/ejabberd_tmp_sup.erl
index be03a9b6d..e6a1361d1 100644
--- a/src/ejabberd_tmp_sup.erl
+++ b/src/ejabberd_tmp_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 18 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl
index fad9ce5b8..ea45dff89 100644
--- a/src/ejabberd_update.erl
+++ b/src/ejabberd_update.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_web.erl b/src/ejabberd_web.erl
index 3025dc146..6f8f463e1 100644
--- a/src/ejabberd_web.erl
+++ b/src/ejabberd_web.erl
@@ -6,7 +6,7 @@
%%% Created : 28 Feb 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl
index 4ae97df02..597dd06b0 100644
--- a/src/ejabberd_web_admin.erl
+++ b/src/ejabberd_web_admin.erl
@@ -5,7 +5,7 @@
%%% Created : 9 Apr 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -326,7 +326,7 @@ make_xhtml(Els, Host, Node, Lang, JID, Level) ->
[?XAE(<<"div">>, [{<<"id">>, <<"copyright">>}],
[?XE(<<"p">>,
[?AC(<<"https://www.ejabberd.im/">>, <<"ejabberd">>),
- ?C(<<" (c) 2002-2019 ">>),
+ ?C(<<" (c) 2002-2020 ">>),
?AC(<<"https://www.process-one.net/">>, <<"ProcessOne, leader in messaging and push solutions">>)]
)])])])]}}.
diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl
index b77e39820..0a3000274 100644
--- a/src/ejabberd_websocket.erl
+++ b/src/ejabberd_websocket.erl
@@ -33,7 +33,7 @@
%%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%%% POSSIBILITY OF SUCH DAMAGE.
%%% ==========================================================================================================
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%----------------------------------------------------------------------
-module(ejabberd_websocket).
diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl
index 9b4b119b6..31718904a 100644
--- a/src/ejabberd_xmlrpc.erl
+++ b/src/ejabberd_xmlrpc.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Aug 2007 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -375,7 +375,7 @@ format_result(Code, {Name, rescode}) ->
format_result({Code, Text}, {Name, restuple}) ->
{struct,
[{Name, make_status(Code)},
- {text, io_lib:format("~ts", [Text])}]};
+ {text, io_lib:format("~s", [Text])}]};
format_result(Elements, {Name, {list, ElementsDef}}) ->
FormattedList = lists:map(fun (Element) ->
format_result(Element, ElementsDef)
diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl
index d22774e7b..4973648bf 100644
--- a/src/ejd2sql.erl
+++ b/src/ejd2sql.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/eldap_filter.erl b/src/eldap_filter.erl
index 7ab634d95..454690012 100644
--- a/src/eldap_filter.erl
+++ b/src/eldap_filter.erl
@@ -6,7 +6,7 @@
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/eldap_pool.erl b/src/eldap_pool.erl
index b6421c230..004b28db3 100644
--- a/src/eldap_pool.erl
+++ b/src/eldap_pool.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Nov 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/eldap_utils.erl b/src/eldap_utils.erl
index 40771d4ad..be6915575 100644
--- a/src/eldap_utils.erl
+++ b/src/eldap_utils.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/elixir_logger_backend.erl b/src/elixir_logger_backend.erl
index 794af3171..3cc4fd424 100644
--- a/src/elixir_logger_backend.erl
+++ b/src/elixir_logger_backend.erl
@@ -5,7 +5,7 @@
%%% Created : 9 March 2016 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ext_mod.erl b/src/ext_mod.erl
index 19a489fb0..1b8c0eb61 100644
--- a/src/ext_mod.erl
+++ b/src/ext_mod.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Feb 2015 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2006-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2006-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/extauth.erl b/src/extauth.erl
index bdf678100..c725c3995 100644
--- a/src/extauth.erl
+++ b/src/extauth.erl
@@ -2,7 +2,7 @@
%%% Created : 7 May 2018 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/extauth_sup.erl b/src/extauth_sup.erl
index 58ab867c3..309374cb6 100644
--- a/src/extauth_sup.erl
+++ b/src/extauth_sup.erl
@@ -2,7 +2,7 @@
%%% Created : 7 May 2018 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl
index c18ae1918..6571acc50 100644
--- a/src/gen_iq_handler.erl
+++ b/src/gen_iq_handler.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index 58ec9950f..d424f4b5c 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -53,12 +53,18 @@
-type opts() :: #{atom() => term()}.
-type db_type() :: atom().
+-type opt_desc() :: #{desc => binary() | [binary()],
+ value => string() | binary()}.
+-type opt_doc() :: {atom(), opt_desc()} | {atom(), opt_desc(), [opt_doc()]}.
-callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}.
-callback stop(binary()) -> any().
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()} | {error, term()}.
-callback mod_opt_type(atom()) -> econf:validator().
-callback mod_options(binary()) -> [{atom(), term()} | atom()].
+-callback mod_doc() -> #{desc => binary() | [binary()],
+ opts => [opt_doc()],
+ example => [string()] | [{binary(), [string()]}]}.
-callback depends(binary(), opts()) -> [{module(), hard | soft}].
-optional_callbacks([mod_opt_type/1, reload/3]).
diff --git a/src/gen_pubsub_node.erl b/src/gen_pubsub_node.erl
index a483bd6ee..db8cf1d2e 100644
--- a/src/gen_pubsub_node.erl
+++ b/src/gen_pubsub_node.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/gen_pubsub_nodetree.erl b/src/gen_pubsub_nodetree.erl
index e834f8e0b..46b9cdc71 100644
--- a/src/gen_pubsub_nodetree.erl
+++ b/src/gen_pubsub_nodetree.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl
index 9f20d7d82..2915fe9c0 100644
--- a/src/jd2ejd.erl
+++ b/src/jd2ejd.erl
@@ -5,7 +5,7 @@
%%% Created : 2 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/misc.erl b/src/misc.erl
index e29f73fc1..8476e7cbf 100644
--- a/src/misc.erl
+++ b/src/misc.erl
@@ -8,7 +8,7 @@
%%% Created : 30 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl
index edba1cebe..68af99630 100644
--- a/src/mod_adhoc.erl
+++ b/src/mod_adhoc.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,7 +36,7 @@
get_local_identity/5, get_local_features/5,
get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
ping_item/4, ping_command/4, mod_opt_type/1, depends/2,
- mod_options/1]).
+ mod_options/1, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -278,3 +278,15 @@ mod_opt_type(report_commands_node) ->
mod_options(_Host) ->
[{report_commands_node, false}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements https://xmpp.org/extensions/xep-0050.html"
+ "[XEP-0050: Ad-Hoc Commands]. It's an auxiliary module and is "
+ "only needed by some of the other modules."),
+ opts =>
+ [{report_commands_node,
+ #{value => "true | false",
+ desc =>
+ ?T("Provide the Commands item in the Service Disvocery. "
+ "Default value: 'false'.")}}]}.
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 3387a831e..f33784a70 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -5,7 +5,7 @@
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -29,9 +29,10 @@
-behaviour(gen_mod).
-include("logger.hrl").
+-include("translate.hrl").
-export([start/2, stop/1, reload/3, mod_options/1,
- get_commands_spec/0, depends/2]).
+ get_commands_spec/0, depends/2, mod_doc/0]).
% Commands API
-export([
@@ -1589,3 +1590,59 @@ num_prio(_) ->
-1.
mod_options(_) -> [].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module provides additional administrative commands."), "",
+ ?T("Details for some commands:"), "",
+ ?T("- 'ban-acount':"),
+ ?T("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't 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. For example, if this command is called:
+ 'ejabberdctl vhost example.org ban-account boby \"Spammed
+ rooms\"', then the sessions of the local account which JID
+ is boby@example.org will be kicked, and its password will
+ be set to something like this:
+ 'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"),
+ ?T("- 'pushroster' (and 'pushroster-all'):"),
+ ?T("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."),
+ ?T("- 'srg-create':"),
+ ?T("If you want to put a group Name with blankspaces, use
+ the characters \"\' and \\'\" to define when the Name
+ starts and ends. For example: 'ejabberdctl srg-create g1
+ example.org \"\'Group number 1\\'\" this_is_g1 g1'")],
+ opts =>
+ [{module_resource,
+ #{value => ?T("Resource"),
+ desc =>
+ ?T("Indicate the resource that the XMPP stanzas must
+ use in the FROM or TO JIDs. This is only useful in
+ the 'get_vcard*' and 'set_vcard*' commands. The
+ default value is 'mod_admin_extra'.")}}],
+ example =>
+ [{?T("With this configuration, vCards can only be modified
+ with mod_admin_extra commands:"),
+ ["acl:",
+ " adminextraresource:",
+ " - resource: \"modadminextraf8x,31ad\"",
+ "access_rules:",
+ " vcard_set:",
+ " - allow: adminextraresource",
+ "modules:",
+ " mod_admin_extra:",
+ " module_resource: \"modadminextraf8x,31ad\"",
+ " mod_vcard:",
+ " access_set: vcard_set"]},
+ {?T("Content of roster file for 'pushroster' command:"),
+ ["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},",
+ "{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},",
+ "{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]}]}.
diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl
index 74e78cc44..51ec957ac 100644
--- a/src/mod_admin_update_sql.erl
+++ b/src/mod_admin_update_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 9 Aug 2017 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -29,7 +29,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, mod_options/1,
- get_commands_spec/0, depends/2]).
+ get_commands_spec/0, depends/2, mod_doc/0]).
% Commands API
-export([update_sql/0]).
@@ -39,6 +39,7 @@
-include("ejabberd_commands.hrl").
-include("xmpp.hrl").
-include("ejabberd_sql_pt.hrl").
+-include("translate.hrl").
%%%
%%% gen_mod
@@ -358,3 +359,9 @@ sql_query(Host, Query) ->
end.
mod_options(_) -> [].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module can be used to update existing SQL database "
+ "from 'old' to 'new' schema. When the module is loaded "
+ "use 'update_sql' ejabberdctl command.")}.
diff --git a/src/mod_announce.erl b/src/mod_announce.erl
index 013594af8..f74d43888 100644
--- a/src/mod_announce.erl
+++ b/src/mod_announce.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -35,7 +35,7 @@
-export([start/2, stop/1, reload/3, export/1, import_info/0,
import_start/2, import/5, announce/1, send_motd/1, disco_identity/5,
disco_features/5, disco_items/5, depends/2,
- send_announcement_to_all/3, announce_commands/4,
+ send_announcement_to_all/3, announce_commands/4, mod_doc/0,
announce_items/4, mod_opt_type/1, mod_options/1, clean_cache/1]).
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
@@ -917,3 +917,64 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module enables configured users to broadcast "
+ "announcements and to set the message of the day (MOTD). "
+ "Configured users can perform these actions with an XMPP "
+ "client either using Ad-hoc Commands or sending messages "
+ "to specific JIDs."), "",
+ ?T("The Ad-hoc Commands are listed in the Server Discovery. "
+ "For this feature to work, 'mod_adhoc' must be enabled."), "",
+ ?T("The specific JIDs where messages can be sent are listed below. "
+ "The first JID in each entry will apply only to the specified "
+ "virtual host example.org, while the JID between brackets "
+ "will apply to all virtual hosts in ejabberd:"), "",
+ "example.org/announce/all (example.org/announce/all-hosts/all)::",
+ ?T("The message is sent to all registered users. If the user is "
+ "online and connected to several resources, only the resource "
+ "with the highest priority will receive the message. "
+ "If the registered user is not connected, the message will be "
+ "stored offline in assumption that offline storage (see 'mod_offline') "
+ "is enabled."),
+ "example.org/announce/online (example.org/announce/all-hosts/online)::",
+ ?T("The message is sent to all connected users. If the user is "
+ "online and connected to several resources, all resources will "
+ "receive the message."),
+ "example.org/announce/motd (example.org/announce/all-hosts/motd)::",
+ ?T("The message is set as the message of the day (MOTD) and is sent "
+ "to users when they login. In addition the message is sent to all "
+ "connected users (similar to announce/online)."),
+ "example.org/announce/motd/update (example.org/announce/all-hosts/motd/update)::",
+ ?T("The message is set as message of the day (MOTD) and is sent to users "
+ "when they login. The message is not sent to any currently connected user."),
+ "example.org/announce/motd/delete (example.org/announce/all-hosts/motd/delete)::",
+ ?T("Any message sent to this JID removes the existing message of the day (MOTD).")],
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option specifies who is allowed to send announcements "
+ "and to set the message of the day. The default value is 'none' "
+ "(i.e. nobody is able to send such messages).")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
diff --git a/src/mod_announce_mnesia.erl b/src/mod_announce_mnesia.erl
index c5a2a2d9a..58d69525d 100644
--- a/src/mod_announce_mnesia.erl
+++ b/src/mod_announce_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl
index f47953e85..f4c0cb890 100644
--- a/src/mod_announce_sql.erl
+++ b/src/mod_announce_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl
index fd38cc2d2..10c7e619c 100644
--- a/src/mod_avatar.erl
+++ b/src/mod_avatar.erl
@@ -3,7 +3,7 @@
%%% Created : 13 Sep 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -26,6 +26,7 @@
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
+-export([mod_doc/0]).
%% Hooks
-export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1,
get_sm_features/5]).
@@ -33,6 +34,7 @@
-include("xmpp.hrl").
-include("logger.hrl").
-include("pubsub.hrl").
+-include("translate.hrl").
-type avatar_id_meta() :: #{avatar_meta => {binary(), avatar_meta()}}.
-opaque convert_rule() :: {default | eimp:img_type(), eimp:img_type()}.
@@ -458,3 +460,36 @@ mod_opt_type(rate_limit) ->
mod_options(_) ->
[{rate_limit, 10},
{convert, []}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("The purpose of the module is to cope with legacy and modern "
+ "XMPP clients posting avatars. The process is described in "
+ "https://xmpp.org/extensions/xep-0398.html"
+ "[XEP-0398: User Avatar to vCard-Based Avatars Conversion]."), "",
+ ?T("Also, the module supports conversion between avatar "
+ "image formats on the fly."), "",
+ ?T("The module depends on 'mod_vcard', 'mod_vcard_xupdate' and "
+ "'mod_pubsub'.")],
+ opts =>
+ [{convert,
+ #{value => "{From: To}",
+ desc =>
+ ?T("Defines image convertion rules: the format in 'From' "
+ "will be converted to format in 'To'. The value of 'From' "
+ "can also be 'default', which is match-all rule. NOTE: "
+ "the list of supported formats is detected at compile time "
+ "depending on the image libraries installed in the system."),
+ example =>
+ [{?T("In this example avatars in WebP format are "
+ "converted to JPEG, all other formats are "
+ "converted to PNG:"),
+ ["convert:",
+ " webp: jpg",
+ " default: png"]}]}},
+ {rate_limit,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("Limit any given JID by the number of avatars it is able "
+ "to convert per minute. This is to protect the server from "
+ "image convertion DoS. The default value is '10'.")}}]}.
diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl
index c1e93b94a..8097cfcf7 100644
--- a/src/mod_block_strangers.erl
+++ b/src/mod_block_strangers.erl
@@ -5,7 +5,7 @@
%%% Created : 25 Dec 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -29,7 +29,7 @@
-behaviour(gen_mod).
%% API
--export([start/2, stop/1, reload/3,
+-export([start/2, stop/1, reload/3, mod_doc/0,
depends/2, mod_opt_type/1, mod_options/1]).
-export([filter_packet/1, filter_offline_msg/1, filter_subscription/2]).
@@ -260,3 +260,54 @@ mod_options(_) ->
{captcha, false},
{allow_local_users, true},
{allow_transports, true}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module allows to block/log messages coming from an "
+ "unknown entity. If a writing entity is not in your roster, "
+ "you can let this module drop and/or log the message. "
+ "By default you'll just not receive message from that entity. "
+ "Enable this module if you want to drop SPAM messages."),
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("The option is supposed to be used when 'allow_local_users' "
+ "and 'allow_transports' are not enough. It's an ACL where "
+ "'deny' means the message will be rejected (or a CAPTCHA "
+ "would be generated for a presence, if configured), and "
+ "'allow' means the sender is whitelisted and the stanza "
+ "will pass through. The default value is 'none', which "
+ "means nothing is whitelisted.")}},
+ {drop,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies if strangers messages should "
+ "be dropped or not. The default value is 'true'.")}},
+ {log,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies if strangers' messages should "
+ "be logged (as info message) in ejabberd.log. "
+ "The default value is 'false'.")}},
+ {allow_local_users,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies if strangers from the same "
+ "local host should be accepted or not. "
+ "The default value is 'true'.")}},
+ {allow_transports,
+ #{value => "true | false",
+ desc =>
+ ?T("If set to 'true' and some server's JID is in user's "
+ "roster, then messages from any user of this server "
+ "are accepted even if no subscription present. "
+ "The default value is 'true'.")}},
+ {captcha,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to generate CAPTCHA or not in response to "
+ "messages from strangers. See also section "
+ "https://docs.ejabberd.im/admin/configuration/#captcha"
+ "[CAPTCHA] of the Configuration Guide. "
+ "The default value is 'false'.")}}]}.
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl
index 3ddb10a6d..15da4c9a9 100644
--- a/src/mod_blocking.erl
+++ b/src/mod_blocking.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -30,7 +30,7 @@
-protocol({xep, 191, '1.2'}).
-export([start/2, stop/1, reload/3, process_iq/1, depends/2,
- disco_features/5, mod_options/1]).
+ disco_features/5, mod_options/1, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -265,3 +265,11 @@ err_db_failure(#iq{lang = Lang} = IQ) ->
mod_options(_Host) ->
[].
+
+mod_doc() ->
+ #{desc =>
+ [?T("The module implements "
+ "https://xmpp.org/extensions/xep-0191.html"
+ "[XEP-0191: Blocking Command]."), "",
+ ?T("This module depends on 'mod_privacy' where "
+ "all the configuration is performed.")]}.
diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl
index 9803e941c..28750f893 100644
--- a/src/mod_bosh.erl
+++ b/src/mod_bosh.erl
@@ -7,7 +7,7 @@
%%% Created : 20 Jul 2011 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,13 +36,14 @@
-export([start/2, stop/1, reload/3, process/2, open_session/2,
close_session/1, find_session/1, clean_cache/1]).
--export([depends/2, mod_opt_type/1, mod_options/1]).
+-export([depends/2, mod_opt_type/1, mod_options/1, mod_doc/0]).
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-include("xmpp.hrl").
-include("ejabberd_http.hrl").
-include("bosh.hrl").
+-include("translate.hrl").
-callback init() -> any().
-callback open_session(binary(), pid()) -> ok | {error, any()}.
@@ -197,6 +198,51 @@ mod_options(Host) ->
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements XMPP over BOSH as defined in "
+ "https://xmpp.org/extensions/xep-0124.html[XEP-0124] and "
+ "https://xmpp.org/extensions/xep-0206.html[XEP-0206]. BOSH "
+ "stands for Bidirectional-streams Over Synchronous HTTP. "
+ "It makes it possible to simulate long lived connections "
+ "required by XMPP over the HTTP protocol. In practice, "
+ "this module makes it possible to use XMPP in a browser without "
+ "Websocket support and more generally to have a way to use "
+ "XMPP while having to get through an HTTP proxy."),
+ opts =>
+ [{json,
+ #{value => "true | false",
+ desc => ?T("This option has no effect.")}},
+ {max_inactivity,
+ #{value => "timeout()",
+ desc =>
+ ?T("The option defines the maximum inactivity period. "
+ "The default value is '30' seconds.")}},
+ {queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("Same as top-level 'queue_type' option, but applied to this module only.")}},
+ {ram_db_type,
+ #{value => "mnesia | sql | redis",
+ desc =>
+ ?T("Same as 'default_ram_db' but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
+
%%%----------------------------------------------------------------------
%%% Cache stuff
%%%----------------------------------------------------------------------
diff --git a/src/mod_bosh_mnesia.erl b/src/mod_bosh_mnesia.erl
index c84b01704..d909b767b 100644
--- a/src/mod_bosh_mnesia.erl
+++ b/src/mod_bosh_mnesia.erl
@@ -2,7 +2,7 @@
%%% Created : 12 Jan 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_bosh_redis.erl b/src/mod_bosh_redis.erl
index 11cf430ac..fa8f91737 100644
--- a/src/mod_bosh_redis.erl
+++ b/src/mod_bosh_redis.erl
@@ -5,7 +5,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl
index 102372a69..f5f34dadc 100644
--- a/src/mod_bosh_sql.erl
+++ b/src/mod_bosh_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_caps.erl b/src/mod_caps.erl
index 7a41d9ff2..5e8cc2eda 100644
--- a/src/mod_caps.erl
+++ b/src/mod_caps.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Oct 2006 by Magnus Henoch <henoch@dtek.chalmers.se>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -49,12 +49,13 @@
handle_cast/2, terminate/2, code_change/3]).
-export([user_send_packet/1, user_receive_packet/1,
- c2s_presence_in/2, mod_opt_type/1, mod_options/1]).
+ c2s_presence_in/2, mod_opt_type/1, mod_options/1, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
-include("mod_caps.hrl").
+-include("translate.hrl").
-define(BAD_HASH_LIFETIME, 600).
@@ -563,3 +564,32 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module implements "
+ "https://xmpp.org/extensions/xep-0115.html"
+ "[XEP-0115: Entity Capabilities]."),
+ ?T("The main purpose of the module is to provide "
+ "PEP functionality (see 'mod_pubsub').")],
+ opts =>
+ [{db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
diff --git a/src/mod_caps_mnesia.erl b/src/mod_caps_mnesia.erl
index 9855b1fc2..acb72c796 100644
--- a/src/mod_caps_mnesia.erl
+++ b/src/mod_caps_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl
index f32759564..d74798947 100644
--- a/src/mod_caps_sql.erl
+++ b/src/mod_caps_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl
index bda77f816..d62a13336 100644
--- a/src/mod_carboncopy.erl
+++ b/src/mod_carboncopy.erl
@@ -7,7 +7,7 @@
%%% {mod_carboncopy, []}
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,8 +36,7 @@
-export([user_send_packet/1, user_receive_packet/1,
iq_handler/1, disco_features/5,
- is_carbon_copy/1, depends/2,
- mod_options/1]).
+ depends/2, mod_options/1, mod_doc/0]).
-export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1]).
%% For debugging purposes
-export([list/2]).
@@ -49,12 +48,6 @@
-type direction() :: sent | received.
-type c2s_state() :: ejabberd_c2s:state().
--spec is_carbon_copy(stanza()) -> boolean().
-is_carbon_copy(#message{meta = #{carbon_copy := true}}) ->
- true;
-is_carbon_copy(_) ->
- false.
-
start(Host, _Opts) ->
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
%% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90)
@@ -81,12 +74,10 @@ reload(_Host, _NewOpts, _OldOpts) ->
-spec disco_features({error, stanza_error()} | {result, [binary()]} | empty,
jid(), jid(), binary(), binary()) ->
{error, stanza_error()} | {result, [binary()]}.
-disco_features({error, Err}, _From, _To, _Node, _Lang) ->
- {error, Err};
-disco_features(empty, _From, _To, <<"">>, _Lang) ->
- {result, [?NS_CARBONS_2]};
+disco_features(empty, From, To, <<"">>, Lang) ->
+ disco_features({result, []}, From, To, <<"">>, Lang);
disco_features({result, Feats}, _From, _To, <<"">>, _Lang) ->
- {result, [?NS_CARBONS_2|Feats]};
+ {result, [?NS_CARBONS_2,?NS_CARBONS_RULES_0|Feats]};
disco_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
@@ -115,22 +106,25 @@ iq_handler(#iq{type = get, lang = Lang} = IQ)->
-spec user_send_packet({stanza(), ejabberd_c2s:state()})
-> {stanza(), ejabberd_c2s:state()} | {stop, {stanza(), ejabberd_c2s:state()}}.
-user_send_packet({Packet, C2SState}) ->
- From = xmpp:get_from(Packet),
- To = xmpp:get_to(Packet),
- case check_and_forward(From, To, Packet, sent) of
- {stop, Pkt} -> {stop, {Pkt, C2SState}};
- Pkt -> {Pkt, C2SState}
- end.
+user_send_packet({#message{meta = #{carbon_copy := true}}, _C2SState} = Acc) ->
+ %% Stop the hook chain, we don't want logging modules to duplicate this
+ %% message.
+ {stop, Acc};
+user_send_packet({#message{from = From, to = To} = Msg, C2SState}) ->
+ {check_and_forward(From, To, Msg, sent), C2SState};
+user_send_packet(Acc) ->
+ Acc.
-spec user_receive_packet({stanza(), ejabberd_c2s:state()})
-> {stanza(), ejabberd_c2s:state()} | {stop, {stanza(), ejabberd_c2s:state()}}.
-user_receive_packet({Packet, #{jid := JID} = C2SState}) ->
- To = xmpp:get_to(Packet),
- case check_and_forward(JID, To, Packet, received) of
- {stop, Pkt} -> {stop, {Pkt, C2SState}};
- Pkt -> {Pkt, C2SState}
- end.
+user_receive_packet({#message{meta = #{carbon_copy := true}}, _C2SState} = Acc) ->
+ %% Stop the hook chain, we don't want logging modules to duplicate this
+ %% message.
+ {stop, Acc};
+user_receive_packet({#message{to = To} = Msg, #{jid := JID} = C2SState}) ->
+ {check_and_forward(JID, To, Msg, received), C2SState};
+user_receive_packet(Acc) ->
+ Acc.
-spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state().
c2s_copy_session(State, #{user := U, server := S, resource := R}) ->
@@ -159,31 +153,24 @@ c2s_session_opened(State) ->
% - registered to the user_send_packet hook, to be called only once even for multicast
% - do not support "private" message mode, and do not modify the original packet in any way
% - we also replicate "read" notifications
--spec check_and_forward(jid(), jid(), stanza(), direction()) ->
- stanza() | {stop, stanza()}.
-check_and_forward(JID, To, Packet, Direction)->
- case is_chat_message(Packet) andalso
- not is_received_muc_pm(To, Packet, Direction) andalso
- not xmpp:has_subtag(Packet, #carbons_private{}) andalso
- not xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) of
+-spec check_and_forward(jid(), jid(), message(), direction()) -> message().
+check_and_forward(JID, To, Msg, Direction)->
+ case (is_chat_message(Msg) orelse
+ is_received_muc_invite(Msg, Direction)) andalso
+ not is_received_muc_pm(To, Msg, Direction) andalso
+ not xmpp:has_subtag(Msg, #carbons_private{}) andalso
+ not xmpp:has_subtag(Msg, #hint{type = 'no-copy'}) of
true ->
- case is_carbon_copy(Packet) of
- false ->
- send_copies(JID, To, Packet, Direction),
- Packet;
- true ->
- %% stop the hook chain, we don't want logging modules to duplicates
- %% this message
- {stop, Packet}
- end;
- _ ->
- Packet
- end.
+ send_copies(JID, To, Msg, Direction);
+ false ->
+ ok
+ end,
+ Msg.
%%% Internal
%% Direction = received | sent <received xmlns='urn:xmpp:carbons:1'/>
-spec send_copies(jid(), jid(), message(), direction()) -> ok.
-send_copies(JID, To, Packet, Direction)->
+send_copies(JID, To, Msg, Direction)->
{U, S, R} = jid:tolower(JID),
PrioRes = ejabberd_sm:get_user_present_resources(U, S),
{_, AvailRs} = lists:unzip(PrioRes),
@@ -200,7 +187,7 @@ send_copies(JID, To, Packet, Direction)->
end,
%% list of JIDs that should receive a carbon copy of this message (excluding the
%% receiver(s) of the original message
- TargetJIDs = case {IsBareTo, Packet} of
+ TargetJIDs = case {IsBareTo, Msg} of
{true, #message{meta = #{sm_copy := true}}} ->
%% The message was sent to our bare JID, and we currently have
%% multiple resources with the same highest priority, so the session
@@ -225,13 +212,13 @@ send_copies(JID, To, Packet, Direction)->
{_, _, Resource} = jid:tolower(Dest),
?DEBUG("Sending: ~p =/= ~p", [R, Resource]),
Sender = jid:make({U, S, <<>>}),
- New = build_forward_packet(JID, Packet, Sender, Dest, Direction),
+ New = build_forward_packet(Msg, Sender, Dest, Direction),
ejabberd_router:route(xmpp:set_from_to(New, Sender, Dest))
end, TargetJIDs).
--spec build_forward_packet(jid(), message(), jid(), jid(), direction()) -> message().
-build_forward_packet(JID, #message{type = T} = Msg, Sender, Dest, Direction) ->
- Forwarded = #forwarded{sub_els = [complete_packet(JID, Msg, Direction)]},
+-spec build_forward_packet(message(), jid(), jid(), direction()) -> message().
+build_forward_packet(#message{type = T} = Msg, Sender, Dest, Direction) ->
+ Forwarded = #forwarded{sub_els = [Msg]},
Carbon = case Direction of
sent -> #carbons_sent{forwarded = Forwarded};
received -> #carbons_received{forwarded = Forwarded}
@@ -262,29 +249,39 @@ disable(Host, U, R)->
Err
end.
--spec complete_packet(jid(), message(), direction()) -> message().
-complete_packet(From, #message{from = undefined} = Msg, sent) ->
- %% if this is a packet sent by user on this host, then Packet doesn't
- %% include the 'from' attribute. We must add it.
- Msg#message{from = From};
-complete_packet(_From, Msg, _Direction) ->
- Msg.
-
--spec is_chat_message(stanza()) -> boolean().
+-spec is_chat_message(message()) -> boolean().
is_chat_message(#message{type = chat}) ->
true;
is_chat_message(#message{type = normal, body = [_|_]}) ->
true;
+is_chat_message(#message{type = Type} = Msg) when Type == chat;
+ Type == normal ->
+ has_chatstate(Msg) orelse xmpp:has_subtag(Msg, #receipt_response{});
is_chat_message(_) ->
false.
+-spec is_received_muc_invite(message(), direction()) -> boolean().
+is_received_muc_invite(_Msg, sent) ->
+ false;
+is_received_muc_invite(Msg, received) ->
+ case xmpp:get_subtag(Msg, #muc_user{}) of
+ #muc_user{invites = [_|_]} ->
+ true;
+ _ ->
+ xmpp:has_subtag(Msg, #x_conference{})
+ end.
+
-spec is_received_muc_pm(jid(), message(), direction()) -> boolean().
-is_received_muc_pm(#jid{lresource = <<>>}, _Packet, _Direction) ->
+is_received_muc_pm(#jid{lresource = <<>>}, _Msg, _Direction) ->
false;
-is_received_muc_pm(_To, _Packet, sent) ->
+is_received_muc_pm(_To, _Msg, sent) ->
false;
-is_received_muc_pm(_To, Packet, received) ->
- xmpp:has_subtag(Packet, #muc_user{}).
+is_received_muc_pm(_To, Msg, received) ->
+ xmpp:has_subtag(Msg, #muc_user{}).
+
+-spec has_chatstate(message()) -> boolean().
+has_chatstate(#message{sub_els = Els}) ->
+ lists:any(fun(El) -> xmpp:get_ns(El) == ?NS_CHATSTATES end, Els).
-spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}].
list(User, Server) ->
@@ -301,3 +298,10 @@ depends(_Host, _Opts) ->
mod_options(_) ->
[].
+
+mod_doc() ->
+ #{desc =>
+ ?T("The module implements https://xmpp.org/extensions/xep-0280.html"
+ "[XEP-0280: Message Carbons]. "
+ "The module broadcasts messages on all connected "
+ "user resources (devices).")}.
diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl
index 35eb6b733..90031e9a1 100644
--- a/src/mod_client_state.erl
+++ b/src/mod_client_state.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Sep 2014 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,6 +32,7 @@
%% gen_mod callbacks.
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
+-export([mod_doc/0]).
%% ejabberd_hooks callbacks.
-export([filter_presence/1, filter_chat_states/1,
@@ -42,6 +43,7 @@
-include("logger.hrl").
-include("xmpp.hrl").
+-include("translate.hrl").
-define(CSI_QUEUE_MAX, 100).
@@ -151,6 +153,38 @@ mod_options(_) ->
{queue_chat_states, true},
{queue_pep, true}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module allows for queueing certain types of stanzas "
+ "when a client indicates that the user is not actively using "
+ "the client right now (see https://xmpp.org/extensions/xep-0352.html"
+ "[XEP-0352: Client State Indication]). This can save bandwidth and "
+ "resources."), "",
+ ?T("A stanza is dropped from the queue if it's effectively obsoleted "
+ "by a new one (e.g., a new presence stanza would replace an old "
+ "one from the same client). The queue is flushed if a stanza arrives "
+ "that won't be queued, or if the queue size reaches a certain limit "
+ "(currently 100 stanzas), or if the client becomes active again.")],
+ opts =>
+ [{queue_presence,
+ #{value => "true | false",
+ desc =>
+ ?T("While a client is inactive, queue presence stanzas "
+ "that indicate (un)availability. The default value is 'true'.")}},
+ {queue_chat_states,
+ #{value => "true | false",
+ desc =>
+ ?T("Queue \"standalone\" chat state notifications (as defined in "
+ "https://xmpp.org/extensions/xep-0085.html"
+ "[XEP-0085: Chat State Notifications]) while a client "
+ "indicates inactivity. The default value is 'true'.")}},
+ {queue_pep,
+ #{value => "true | false",
+ desc =>
+ ?T("Queue PEP notifications while a client is inactive. "
+ "When the queue is flushed, only the most recent notification "
+ "of a given PEP node is delivered. The default value is 'true'.")}}]}.
+
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[].
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index 1a327064e..255c7f66d 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,7 +36,7 @@
adhoc_local_items/4, adhoc_local_commands/4,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
adhoc_sm_items/4, adhoc_sm_commands/4, mod_options/1,
- depends/2]).
+ depends/2, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -1558,3 +1558,10 @@ tr(Lang, Text) ->
translate:translate(Lang, Text).
mod_options(_) -> [].
+
+mod_doc() ->
+ #{desc =>
+ ?T("The module provides server configuration functionality via "
+ "https://xmpp.org/extensions/xep-0050.html"
+ "[XEP-0050: Ad-Hoc Commands]. This module requires "
+ "'mod_adhoc' to be loaded.")}.
diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl
index 508cb84b6..ea0061089 100644
--- a/src/mod_delegation.erl
+++ b/src/mod_delegation.erl
@@ -4,7 +4,7 @@
%%% Purpose : XEP-0355: Namespace Delegation
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,6 +32,7 @@
%% API
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
+-export([mod_doc/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -82,6 +83,51 @@ mod_opt_type(namespaces) ->
mod_options(_Host) ->
[{namespaces, []}].
+mod_doc() ->
+ #{desc =>
+ ?T("This module is an implementation of "
+ "https://xmpp.org/extensions/xep-0355.html"
+ "[XEP-0355: Namespace Delegation]. "
+ "Only admin mode has been implemented by now. "
+ "Namespace delegation allows external services to "
+ "handle IQ using specific namespace. This may be applied "
+ "for external PEP service."),
+ opts =>
+ [{namespaces,
+ #{value => "{Namespace: Options}",
+ desc =>
+ ?T("If you want to delegate namespaces to a component, "
+ "specify them in this option, and associate them "
+ "to an access rule. The 'Options' are:")},
+ [{filtering,
+ #{value => ?T("Attributes"),
+ desc =>
+ ?T("The list of attributes. Currently not used.")}},
+ {access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("The option defines which components are allowed "
+ "for namespace delegation. The default value is 'none'.")}}]}],
+ example =>
+ ["access_rules:",
+ " external_pubsub:",
+ " allow: external_component",
+ " external_mam:",
+ " allow: external_component",
+ "",
+ "acl:",
+ " external_component:",
+ " server: sat-pubsub.example.org",
+ "",
+ "modules:",
+ " ...",
+ " mod_delegation:",
+ " namespaces:",
+ " urn:xmpp:mam:1:",
+ " access: external_mam",
+ " http://jabber.org/protocol/pubsub:",
+ " access: external_pubsub"]}.
+
depends(_, _) ->
[].
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 4ec77e847..2d72f64ce 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -37,7 +37,8 @@
get_local_features/5, get_local_services/5,
process_sm_iq_items/1, process_sm_iq_info/1,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
- get_info/5, mod_opt_type/1, mod_options/1, depends/2]).
+ get_info/5, mod_opt_type/1, mod_options/1, depends/2,
+ mod_doc/0]).
-include("logger.hrl").
-include("translate.hrl").
@@ -436,3 +437,70 @@ mod_options(_Host) ->
[{extra_domains, []},
{server_info, []},
{name, ?T("ejabberd")}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module adds support for "
+ "https://xmpp.org/extensions/xep-0030.html"
+ "[XEP-0030: Service Discovery]. With this module enabled, "
+ "services on your server can be discovered by XMPP clients."),
+ opts =>
+ [{extra_domains,
+ #{value => "[Domain, ...]",
+ desc =>
+ ?T("With this option, you can specify a list of extra "
+ "domains that are added to the Service Discovery item list. "
+ "The default value is an empty list.")}},
+ {name,
+ #{value => ?T("Name"),
+ desc =>
+ ?T("A name of the server in the Service Discovery. "
+ "This will only be displayed by special XMPP clients. "
+ "The default value is 'ejabberd'.")}},
+ {server_info,
+ #{value => "[Info, ...]",
+ example =>
+ ["server_info:",
+ " -",
+ " modules: all",
+ " name: abuse-addresses",
+ " urls: [mailto:abuse@shakespeare.lit]",
+ " -",
+ " modules: [mod_muc]",
+ " name: \"Web chatroom logs\"",
+ " urls: [http://www.example.org/muc-logs]",
+ " -",
+ " modules: [mod_disco]",
+ " name: feedback-addresses",
+ " urls:",
+ " - http://shakespeare.lit/feedback.php",
+ " - mailto:feedback@shakespeare.lit",
+ " - xmpp:feedback@shakespeare.lit",
+ " -",
+ " modules:",
+ " - mod_disco",
+ " - mod_vcard",
+ " name: admin-addresses",
+ " urls:",
+ " - mailto:xmpp@shakespeare.lit",
+ " - xmpp:admins@shakespeare.lit"],
+ desc =>
+ ?T("Specify additional information about the server, "
+ "as described in https://xmpp.org/extensions/xep-0157.html"
+ "[XEP-0157: Contact Addresses for XMPP Services]. Every 'Info' "
+ "element in the list is constructed from the following options:")},
+ [{modules,
+ #{value => "all | [Module, ...]",
+ desc =>
+ ?T("The value can be the keyword 'all', in which case the "
+ "information is reported in all the services, "
+ "or a list of ejabberd modules, in which case the "
+ "information is only specified for the services provided "
+ "by those modules.")}},
+ {name,
+ #{value => ?T("Name"),
+ desc => ?T("Any arbitrary name of the contact.")}},
+ {urls,
+ #{value => "[URI, ...]",
+ desc => ?T("A list of contact URIs, such as "
+ "HTTP URLs, XMPP URIs and so on.")}}]}]}.
diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl
index 0d2473c15..b9fed0db0 100644
--- a/src/mod_fail2ban.erl
+++ b/src/mod_fail2ban.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Aug 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -34,7 +34,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
- mod_opt_type/1, mod_options/1, depends/2]).
+ mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
%% ejabberd command.
-export([get_commands_spec/0, unban/1]).
@@ -217,8 +217,8 @@ log_and_disconnect(#{ip := {Addr, _}, lang := Lang} = State, Attempts, UnbanTS)
UnbanDate = format_date(
calendar:now_to_universal_time(msec_to_now(UnbanTS))),
Format = ?T("Too many (~p) failed authentications "
- "from this IP address (~ts). The address "
- "will be unblocked at ~ts UTC"),
+ "from this IP address (~s). The address "
+ "will be unblocked at ~s UTC"),
Args = [Attempts, IP, UnbanDate],
?WARNING_MSG("Connection attempt from blacklisted IP ~ts: ~ts",
[IP, io_lib:fwrite(Format, Args)]),
@@ -254,3 +254,33 @@ mod_options(_Host) ->
[{access, none},
{c2s_auth_ban_lifetime, timer:hours(1)},
{c2s_max_auth_failures, 20}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("The module bans IPs that show the malicious signs. "
+ "Currently only C2S authentication failures are detected."), "",
+ ?T("Unlike the standalone program, 'mod_fail2ban' clears the "
+ "record of authentication failures after some time since the "
+ "first failure or on a successful authentication. "
+ "It also does not simply block network traffic, but "
+ "provides the client with a descriptive error message.")],
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Specify an access rule for whitelisting IP "
+ "addresses or networks. If the rule returns 'allow' "
+ "for a given IP address, that address will never be "
+ "banned. The 'AccessName' should be of type 'ip'. "
+ "The default value is 'none'.")}},
+ {c2s_auth_ban_lifetime,
+ #{value => "timeout()",
+ desc =>
+ ?T("The lifetime of the IP ban caused by too many "
+ "C2S authentication failures. The default value is "
+ "'1' hour.")}},
+ {c2s_max_auth_failures,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("The number of C2S authentication failures to "
+ "trigger the IP ban. The default value is '20'.")}}]}.
diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl
index 34e118cc9..912299e45 100644
--- a/src/mod_http_api.erl
+++ b/src/mod_http_api.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Sep 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -30,12 +30,13 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process/2, depends/2,
- mod_options/1]).
+ mod_options/1, mod_doc/0]).
-include("xmpp.hrl").
-include("logger.hrl").
-include("ejabberd_http.hrl").
-include("ejabberd_stacktrace.hrl").
+-include("translate.hrl").
-define(DEFAULT_API_VERSION, 0).
@@ -520,3 +521,8 @@ hide_sensitive_args(NonListArgs) ->
mod_options(_) ->
[].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module provides a ReST API to call "
+ "ejabberd commands using JSON data.")}.
diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl
index f7d1b15ca..287fce624 100644
--- a/src/mod_http_fileserver.erl
+++ b/src/mod_http_fileserver.erl
@@ -5,7 +5,7 @@
%%% Created :
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -43,11 +43,12 @@
%% utility for other http modules
-export([content_type/3]).
--export([reopen_log/0, mod_opt_type/1, mod_options/1, depends/2]).
+-export([reopen_log/0, mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-include("ejabberd_http.hrl").
-include_lib("kernel/include/file.hrl").
+-include("translate.hrl").
-record(state,
{host, docroot, accesslog, accesslogfd,
@@ -344,9 +345,10 @@ serve_index(FileName, [Index | T], CH, DefaultContentType, ContentTypes) ->
serve_not_modified(FileInfo, FileName, CustomHeaders) ->
?DEBUG("Delivering not modified: ~ts", [FileName]),
{0, 304,
- [{<<"Server">>, <<"ejabberd">>},
- {<<"Last-Modified">>, last_modified(FileInfo)}
- | CustomHeaders], <<>>}.
+ ejabberd_http:apply_custom_headers(
+ [{<<"Server">>, <<"ejabberd">>},
+ {<<"Last-Modified">>, last_modified(FileInfo)}],
+ CustomHeaders), <<>>}.
%% Assume the file exists if we got this far and attempt to read it in
%% and serve it up.
@@ -355,10 +357,11 @@ serve_file(FileInfo, FileName, CustomHeaders, DefaultContentType, ContentTypes)
ContentType = content_type(FileName, DefaultContentType,
ContentTypes),
{FileInfo#file_info.size, 200,
- [{<<"Server">>, <<"ejabberd">>},
- {<<"Last-Modified">>, last_modified(FileInfo)},
- {<<"Content-Type">>, ContentType}
- | CustomHeaders],
+ ejabberd_http:apply_custom_headers(
+ [{<<"Server">>, <<"ejabberd">>},
+ {<<"Last-Modified">>, last_modified(FileInfo)},
+ {<<"Content-Type">>, ContentType}],
+ CustomHeaders),
{file, FileName}}.
%%----------------------------------------------------------------------
@@ -498,3 +501,87 @@ mod_options(_) ->
{must_authenticate_with, []},
%% Required option
docroot].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This simple module serves files from the local disk over HTTP."),
+ opts =>
+ [{accesslog,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("File to log accesses using an Apache-like format. "
+ "No log will be recorded if this option is not specified.")}},
+ {docroot,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("Directory to serve the files from. "
+ "This is a mandatory option.")}},
+ {content_types,
+ #{value => "{Extension: Type}",
+ desc =>
+ ?T("Specify mappings of extension to content type. "
+ "There are several content types already defined. "
+ "With this option you can add new definitions "
+ "or modify existing ones."),
+ example =>
+ [{?T("The default value is shown in the example below:"),
+ ["content_types:"|
+ [" " ++ binary_to_list(E) ++ ": " ++ binary_to_list(T)
+ || {E, T} <- ?DEFAULT_CONTENT_TYPES]]}]}},
+ {default_content_type,
+ #{value => ?T("Type"),
+ desc =>
+ ?T("Specify the content type to use for unknown extensions. "
+ "The default value is 'application/octet-stream'.")}},
+ {custom_headers,
+ #{value => "{Name: Value}",
+ desc =>
+ ?T("Indicate custom HTTP headers to be included in all responses. "
+ "There are no custom headers by default.")}},
+ {directory_indices,
+ #{value => "[Index, ...]",
+ desc =>
+ ?T("Indicate one or more directory index files, "
+ "similarly to Apache's 'DirectoryIndex' variable. "
+ "When an HTTP request hits a directory instead of a "
+ "regular file, those directory indices are looked in order, "
+ "and the first one found is returned. "
+ "The default value is an empty list.")}},
+ {must_authenticate_with,
+ #{value => ?T("[{Username, Hostname}, ...]"),
+ desc =>
+ ?T("List of accounts that are allowed to use this service. "
+ "Default value: '[]'.")}}],
+ example =>
+ [{?T("This example configuration will serve the files from the "
+ "local directory '/var/www' in the address "
+ "'http://example.org:5280/pub/archive/'. In this example a new "
+ "content type 'ogg' is defined, 'png' is redefined, and 'jpg' "
+ "definition is deleted:"),
+ ["listen:",
+ " ...",
+ " -",
+ " port: 5280",
+ " module: ejabberd_http",
+ " request_handlers:",
+ " ...",
+ " /pub/archive: mod_http_fileserver",
+ " ...",
+ " ...",
+ "",
+ "modules:",
+ " ...",
+ " mod_http_fileserver:",
+ " docroot: /var/www",
+ " accesslog: /var/log/ejabberd/access.log",
+ " directory_indices:",
+ " - index.html",
+ " - main.htm",
+ " custom_headers:",
+ " X-Powered-By: Erlang/OTP",
+ " X-Fry: \"It's a widely-believed fact!\"",
+ " content_types:",
+ " .ogg: audio/ogg",
+ " .png: image/png",
+ " default_content_type: text/html",
+ " ..."]}]}.
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index 035aa3982..51d7761ab 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Aug 2015 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2015-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2015-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -63,6 +63,7 @@
stop/1,
reload/3,
depends/2,
+ mod_doc/0,
mod_opt_type/1,
mod_options/1]).
@@ -223,6 +224,182 @@ mod_options(Host) ->
{rm_on_unregister, true},
{thumbnail, false}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module allows for requesting permissions to "
+ "upload a file via HTTP as described in "
+ "https://xmpp.org/extensions/xep-0363.html"
+ "[XEP-0363: HTTP File Upload]. If the request is accepted, "
+ "the client receives a URL for uploading the file and "
+ "another URL from which that file can later be downloaded."), "",
+ ?T("In order to use this module, it must be configured as "
+ "a 'request_handler' for 'ejabberd_http' listener.")],
+ opts =>
+ [{host,
+ #{desc => ?T("Deprecated. Use 'hosts' instead.")}},
+ {hosts,
+ #{value => ?T("[Host, ...]"),
+ desc =>
+ ?T("This option defines the Jabber IDs of the service. "
+ "If the 'hosts' option is not specified, the only Jabber ID will "
+ "be the hostname of the virtual host with the prefix \"upload.\". "
+ "The keyword '@HOST@' is replaced with the real virtual host name.")}},
+ {name,
+ #{value => ?T("Name"),
+ desc =>
+ ?T("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\".")}},
+ {access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option defines the access rule to limit who is "
+ "permitted to use the HTTP upload service. "
+ "The default value is 'local'. If no access rule of "
+ "that name exists, no user will be allowed to use the service.")}},
+ {max_size,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("This option limits the acceptable file size. "
+ "Either a number of bytes (larger than zero) or "
+ "'infinity' must be specified. "
+ "The default value is '104857600'.")}},
+ {secret_length,
+ #{value => ?T("Length"),
+ desc =>
+ ?T("This option defines the length of the random "
+ "string included in the GET and PUT URLs generated "
+ "by 'mod_http_upload'. The minimum length is 8 characters, "
+ "but it is recommended to choose a larger value. "
+ "The default value is '40'.")}},
+ {jid_in_url,
+ #{value => "node | sha1",
+ desc =>
+ ?T("When this option is set to 'node', the node identifier "
+ "of the user's JID (i.e., the user name) is included in "
+ "the GET and PUT URLs generated by 'mod_http_upload'. "
+ "Otherwise, a SHA-1 hash of the user's bare JID is "
+ "included instead. The default value is 'sha1'.")}},
+ {thumbnail,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies whether ejabberd should create "
+ "thumbnails of uploaded images. If a thumbnail is created, "
+ "a <thumbnail/> element that contains the download <uri/> "
+ "and some metadata is returned with the PUT response. "
+ "The default value is 'false'.")}},
+ {file_mode,
+ #{value => ?T("Permission"),
+ desc =>
+ ?T("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.")}},
+ {dir_mode,
+ #{value => ?T("Permission"),
+ desc =>
+ ?T("This option defines the permission bits of the 'docroot' "
+ "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.")}},
+ {docroot,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("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\".")}},
+ {put_url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("This option specifies the initial part of the PUT URLs "
+ "used for file uploads. The keyword @HOST@ is replaced "
+ "with the virtual host name. NOTE: different virtual "
+ "hosts cannot use the same PUT URL. "
+ "The default value is \"https://@HOST@:5443\".")}},
+ {get_url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("This option specifies the initial part of the GET URLs "
+ "used for downloading the files. By default, it is set "
+ "to the same value as 'put_url'. The keyword @HOST@ is "
+ "replaced with the virtual host name. NOTE: if GET requests "
+ "are handled by 'mod_http_upload', the 'get_url' must match the "
+ "'put_url'. Setting it to a different value only makes "
+ "sense if an external web server or 'mod_http_fileserver' "
+ "is used to serve the uploaded files.")}},
+ {service_url,
+ #{desc => ?T("Deprecated.")}},
+ {custom_headers,
+ #{value => "{Name: Value}",
+ desc =>
+ ?T("This option specifies additional header fields to be "
+ "included in all HTTP responses. By default no custom "
+ "headers are included.")}},
+ {external_secret,
+ #{value => ?T("Text"),
+ desc =>
+ ?T("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 "
+ "https://modules.prosody.im/mod_http_upload_external.html"
+ "[Prosody's mod_http_upload_external] in the "
+ "'Implementation' section. There is no default value.")}},
+ {rm_on_unregister,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies whether files uploaded by a user "
+ "should be removed when that user is unregistered. "
+ "The default value is 'true'.")}},
+ {vcard,
+ #{value => ?T("vCard"),
+ desc =>
+ ?T("A custom vCard of the service that will be displayed "
+ "by some XMPP clients in Service Discovery. The value of "
+ "'vCard' is a YAML map constructed from an XML representation "
+ "of vCard. Since the representation has no attributes, "
+ "the mapping is straightforward."),
+ example =>
+ [{?T("For example, the following XML representation of vCard:"),
+ ["<vCard xmlns='vcard-temp'>",
+ " <FN>Conferences</FN>",
+ " <ADR>",
+ " <WORK/>",
+ " <STREET>Elm Street</STREET>",
+ " </ADR>",
+ "</vCard>"]},
+ {?T("will be translated to:"),
+ ["vcard:",
+ " fn: Conferences",
+ " adr:",
+ " -",
+ " work: true",
+ " street: Elm Street"]}]}}],
+ example =>
+ ["listen:",
+ " ...",
+ " -",
+ " port: 5443",
+ " module: ejabberd_http",
+ " tls: true",
+ " request_handlers:",
+ " ...",
+ " /upload: mod_http_upload",
+ " ...",
+ " ...",
+ "",
+ "modules:",
+ " ...",
+ " mod_http_upload:",
+ " docroot: /ejabberd/upload",
+ " put_url: \"https://@HOST@:5443/upload\"",
+ " ..."]}.
+
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[].
@@ -368,7 +545,7 @@ process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP,
ok ->
http_response(201, CustomHeaders);
{ok, Headers, OutData} ->
- http_response(201, Headers ++ CustomHeaders, OutData);
+ http_response(201, ejabberd_http:apply_custom_headers(Headers, CustomHeaders), OutData);
{error, closed} ->
?DEBUG("Cannot store file ~ts from ~ts for ~ts: connection closed",
[Path, encode_addr(IP), Host]),
@@ -418,7 +595,7 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request)
$", FileName/binary, $">>}]
end,
Headers2 = [{<<"Content-Type">>, ContentType} | Headers1],
- Headers3 = Headers2 ++ CustomHeaders,
+ Headers3 = ejabberd_http:apply_custom_headers(Headers2, CustomHeaders),
http_response(200, Headers3, {file, Path});
{error, eacces} ->
?WARNING_MSG("Cannot serve ~ts to ~ts: Permission denied",
@@ -456,7 +633,7 @@ process(_LocalPath, #request{method = 'OPTIONS', host = Host,
try gen_server:call(Proc, get_conf, ?CALL_TIMEOUT) of
{ok, _DocRoot, CustomHeaders} ->
AllowHeader = {<<"Allow">>, <<"OPTIONS, HEAD, GET, PUT">>},
- http_response(200, [AllowHeader | CustomHeaders])
+ http_response(200, ejabberd_http:apply_custom_headers([AllowHeader], CustomHeaders))
catch
exit:{noproc, _} ->
?WARNING_MSG("Cannot handle OPTIONS request from ~ts for ~ts: "
diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl
index b4c2203c3..ed1bbe331 100644
--- a/src/mod_http_upload_quota.erl
+++ b/src/mod_http_upload_quota.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Oct 2015 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2015-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2015-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -37,6 +37,7 @@
-export([start/2,
stop/1,
depends/2,
+ mod_doc/0,
mod_opt_type/1,
mod_options/1]).
@@ -53,6 +54,7 @@
-include("jid.hrl").
-include("logger.hrl").
+-include("translate.hrl").
-include_lib("kernel/include/file.hrl").
-record(state,
@@ -92,6 +94,57 @@ mod_options(_) ->
{access_hard_quota, hard_upload_quota},
{max_days, infinity}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module adds quota support for mod_http_upload."), "",
+ ?T("This module depends on 'mod_http_upload'.")],
+ opts =>
+ [{max_days,
+ #{value => ?T("Days"),
+ desc =>
+ ?T("If a number larger than zero is specified, "
+ "any files (and directories) older than this "
+ "number of days are removed from the subdirectories "
+ "of the 'docroot' directory, once per day. "
+ "The default value is 'infinity'.")}},
+ {access_soft_quota,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option defines which access rule is used "
+ "to specify the \"soft quota\" for the matching JIDs. "
+ "That rule must yield a positive number of megabytes "
+ "for any JID that is supposed to have a quota limit. "
+ "See the description of the 'access_hard_quota' option "
+ "for details. The default value is 'soft_upload_quota'.")}},
+ {access_hard_quota,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("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 'access_soft_quota'). "
+ "The default value is 'hard_upload_quota'.")}}],
+ example =>
+ ["shaper_rules:",
+ " ...",
+ " soft_upload_quota:",
+ " 1000: all # MiB",
+ " hard_upload_quota:",
+ " 1100: all # MiB",
+ " ...",
+ "",
+ "modules:",
+ " ...",
+ " mod_http_upload: {}",
+ " mod_http_upload_quota:",
+ " max_days: 100",
+ " ..."]}.
+
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
[{mod_http_upload, hard}].
diff --git a/src/mod_jidprep.erl b/src/mod_jidprep.erl
index 4aeb68ec6..c38443c29 100644
--- a/src/mod_jidprep.erl
+++ b/src/mod_jidprep.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Sep 2019 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2019 ProcessOne
+%%% ejabberd, Copyright (C) 2019-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -31,6 +31,7 @@
%% gen_mod callbacks.
-export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2]).
+-export([mod_doc/0]).
%% ejabberd_hooks callbacks.
-export([disco_local_features/5]).
@@ -71,6 +72,22 @@ mod_opt_type(access) ->
mod_options(_Host) ->
[{access, local}].
+mod_doc() ->
+ #{desc =>
+ ?T("This module allows XMPP clients to ask the "
+ "server to normalize a JID as per the rules specified "
+ "in https://tools.ietf.org/html/rfc6122"
+ "[RFC 6122: XMPP Address Format]. This might be useful "
+ "for clients in certain constrained environments, "
+ "or for testing purposes."),
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option defines which access rule will "
+ "be used to control who is allowed to use this "
+ "service. The default value is 'local'.")}}]}.
+
%%--------------------------------------------------------------------
%% Register/unregister hooks.
%%--------------------------------------------------------------------
diff --git a/src/mod_last.erl b/src/mod_last.erl
index 28b66be08..6a37a9e1c 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -34,7 +34,7 @@
-export([start/2, stop/1, reload/3, process_local_iq/1, export/1,
process_sm_iq/1, on_presence_update/4, import_info/0,
import/5, import_start/2, store_last_info/4, get_last_info/2,
- remove_user/2, mod_opt_type/1, mod_options/1,
+ remove_user/2, mod_opt_type/1, mod_options/1, mod_doc/0,
register_user/2, depends/2, privacy_check_packet/4]).
-include("logger.hrl").
@@ -331,3 +331,33 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module adds support for "
+ "https://xmpp.org/extensions/xep-0012.html"
+ "[XEP-0012: Last Activity]. It can be used "
+ "to discover when a disconnected user last accessed "
+ "the server, to know when a connected user was last "
+ "active on the server, or to query the uptime of the ejabberd server."),
+ opts =>
+ [{db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
diff --git a/src/mod_last_mnesia.erl b/src/mod_last_mnesia.erl
index 7e4411443..0d914ce07 100644
--- a/src/mod_last_mnesia.erl
+++ b/src/mod_last_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl
index 46079717c..0e8c39f8b 100644
--- a/src/mod_last_sql.erl
+++ b/src/mod_last_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl
index a48ef8de5..1f2d49001 100644
--- a/src/mod_legacy_auth.erl
+++ b/src/mod_legacy_auth.erl
@@ -2,7 +2,7 @@
%%% Created : 11 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -25,7 +25,7 @@
-protocol({xep, 78, '2.5'}).
%% gen_mod API
--export([start/2, stop/1, reload/3, depends/2, mod_options/1]).
+-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_doc/0]).
%% hooks
-export([c2s_unauthenticated_packet/2, c2s_stream_features/2]).
@@ -58,6 +58,15 @@ depends(_Host, _Opts) ->
mod_options(_) ->
[].
+mod_doc() ->
+ #{desc =>
+ [?T("The module implements "
+ "https://xmpp.org/extensions/xep-0078.html"
+ "[XEP-0078: Non-SASL Authentication]."), "",
+ ?T("NOTE: This type of authentication was obsoleted in "
+ "2008 and you unlikely need this module unless "
+ "you have something like outdated Jabber bots.")]}.
+
-spec c2s_unauthenticated_packet(c2s_state(), iq()) ->
c2s_state() | {stop, c2s_state()}.
c2s_unauthenticated_packet(State, #iq{type = T, sub_els = [_]} = IQ)
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index 7bc508db4..3927c9ee1 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -5,7 +5,7 @@
%%% Created : 4 Jul 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
%% API
--export([start/2, stop/1, reload/3, depends/2]).
+-export([start/2, stop/1, reload/3, depends/2, mod_doc/0]).
-export([sm_receive_packet/1, user_receive_packet/1, user_send_packet/1,
user_send_packet_strip_tag/1, process_iq_v0_2/1, process_iq_v0_3/1,
@@ -1426,3 +1426,77 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements "
+ "https://xmpp.org/extensions/xep-0313.html"
+ "[XEP-0313: Message Archive Management]. "
+ "Compatible XMPP clients can use it to store their "
+ "chat history on the server."),
+ opts =>
+ [{assume_mam_usage,
+ #{value => "true | false",
+ desc =>
+ ?T("This option determines how ejabberd's "
+ "stream management code (see 'mod_stream_mgmt') "
+ "handles unacknowledged messages when the "
+ "connection is lost. Usually, such messages are "
+ "either bounced or resent. However, neither is "
+ "done for messages that were stored in the user's "
+ "MAM archive if this option is set to 'true'. In "
+ "this case, ejabberd assumes those messages will "
+ "be retrieved from the archive. "
+ "The default value is 'false'.")}},
+ {default,
+ #{value => "always | never | roster",
+ desc =>
+ ?T("The option defines default policy for chat history. "
+ "When 'always' is set every chat message is stored. "
+ "With 'roster' only chat history with contacts from "
+ "user's roster is stored. And 'never' fully disables "
+ "chat history. Note that a client can change its "
+ "policy via protocol commands. "
+ "The default value is 'never'.")}},
+ {request_activates_archiving,
+ #{value => "true | false",
+ desc =>
+ ?T("If the value is 'true', no messages are stored "
+ "for a user until their client issue a MAM request, "
+ "regardless of the value of the 'default' option. "
+ "Once the server received a request, that user's "
+ "messages are archived as usual. "
+ "The default value is 'false'.")}},
+ {compress_xml,
+ #{value => "true | false",
+ desc =>
+ ?T("When enabled, new messages added to archives are "
+ "compressed using a custom compression algorithm. "
+ "This feature works only with SQL backends. "
+ "The default value is 'false'.")}},
+ {clear_archive_on_room_destroy,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to destroy message archive of a room "
+ "(see 'mod_muc') when it gets destroyed. "
+ "The default value is 'true'.")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl
index 1c8e742c8..b98af749a 100644
--- a/src/mod_mam_mnesia.erl
+++ b/src/mod_mam_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl
index 654e1f42f..eec682df9 100644
--- a/src/mod_mam_sql.erl
+++ b/src/mod_mam_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -105,6 +105,7 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) ->
jid:tolower(Peer)),
Body = fxml:get_subtag_cdata(Pkt, <<"body">>),
SType = misc:atom_to_binary(Type),
+ SqlType = ejabberd_option:sql_type(LHost),
XML = case mod_mam_opt:compress_xml(LServer) of
true ->
J1 = case Type of
@@ -115,24 +116,44 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) ->
_ ->
fxml:element_to_binary(Pkt)
end,
- case ejabberd_sql:sql_query(
- LServer,
- ?SQL_INSERT(
- "archive",
- ["username=%(SUser)s",
- "server_host=%(LServer)s",
- "timestamp=%(TS)d",
- "peer=%(LPeer)s",
- "bare_peer=%(BarePeer)s",
- "xml=%(XML)s",
- "txt=%(Body)s",
- "kind=%(SType)s",
- "nick=%(Nick)s"])) of
- {updated, _} ->
- ok;
- Err ->
- Err
- end.
+ case SqlType of
+ mssql -> case ejabberd_sql:sql_query(
+ LServer,
+ ?SQL_INSERT(
+ "archive",
+ ["username=%(SUser)s",
+ "server_host=%(LServer)s",
+ "timestamp=%(TS)d",
+ "peer=%(LPeer)s",
+ "bare_peer=%(BarePeer)s",
+ "xml=N%(XML)s",
+ "txt=N%(Body)s",
+ "kind=%(SType)s",
+ "nick=%(Nick)s"])) of
+ {updated, _} ->
+ ok;
+ Err ->
+ Err
+ end;
+ _ -> case ejabberd_sql:sql_query(
+ LServer,
+ ?SQL_INSERT(
+ "archive",
+ ["username=%(SUser)s",
+ "server_host=%(LServer)s",
+ "timestamp=%(TS)d",
+ "peer=%(LPeer)s",
+ "bare_peer=%(BarePeer)s",
+ "xml=%(XML)s",
+ "txt=%(Body)s",
+ "kind=%(SType)s",
+ "nick=%(Nick)s"])) of
+ {updated, _} ->
+ ok;
+ Err ->
+ Err
+ end
+ end.
write_prefs(LUser, _LServer, #archive_prefs{default = Default,
never = Never,
@@ -304,17 +325,31 @@ export(_Server) ->
XML = fxml:element_to_binary(Pkt),
Body = fxml:get_subtag_cdata(Pkt, <<"body">>),
SType = misc:atom_to_binary(Type),
- [?SQL_INSERT(
- "archive",
- ["username=%(SUser)s",
- "server_host=%(LServer)s",
- "timestamp=%(TStmp)d",
- "peer=%(LPeer)s",
- "bare_peer=%(BarePeer)s",
- "xml=%(XML)s",
- "txt=%(Body)s",
- "kind=%(SType)s",
- "nick=%(Nick)s"])];
+ SqlType = ejabberd_option:sql_type(Host),
+ case SqlType of
+ mssql -> [?SQL_INSERT(
+ "archive",
+ ["username=%(SUser)s",
+ "server_host=%(LServer)s",
+ "timestamp=%(TStmp)d",
+ "peer=%(LPeer)s",
+ "bare_peer=%(BarePeer)s",
+ "xml=N%(XML)s",
+ "txt=N%(Body)s",
+ "kind=%(SType)s",
+ "nick=%(Nick)s"])];
+ _ -> [?SQL_INSERT(
+ "archive",
+ ["username=%(SUser)s",
+ "server_host=%(LServer)s",
+ "timestamp=%(TStmp)d",
+ "peer=%(LPeer)s",
+ "bare_peer=%(BarePeer)s",
+ "xml=%(XML)s",
+ "txt=%(Body)s",
+ "kind=%(SType)s",
+ "nick=%(Nick)s"])]
+ end;
(_Host, _R) ->
[]
end}].
diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl
index 297f3eeb5..88bb93cdc 100644
--- a/src/mod_metrics.erl
+++ b/src/mod_metrics.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Oct 2015 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -30,9 +30,10 @@
-include("logger.hrl").
-include("xmpp.hrl").
+-include("translate.hrl").
-export([start/2, stop/1, mod_opt_type/1, mod_options/1, depends/2, reload/3]).
--export([push/2]).
+-export([push/2, mod_doc/0]).
-export([offline_message_hook/1,
sm_register_connection_hook/3, sm_remove_connection_hook/3,
user_send_packet/1, user_receive_packet/1,
@@ -188,3 +189,32 @@ mod_opt_type(port) ->
mod_options(_) ->
[{ip, {127,0,0,1}}, {port, 11111}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module sends events to external backend "
+ "(by now only https://github.com/processone/grapherl"
+ "[grapherl] is supported). Supported events are:"), "",
+ "- sm_register_connection", "",
+ "- sm_remove_connection", "",
+ "- user_send_packet", "",
+ "- user_receive_packet", "",
+ "- s2s_send_packet", "",
+ "- s2s_receive_packet", "",
+ "- register_user", "",
+ "- remove_user", "",
+ "- offline_message", "",
+ ?T("When enabled, every call to these hooks triggers "
+ "a counter event to be sent to the external backend.")],
+ opts =>
+ [{ip,
+ #{value => ?T("IPv4Address"),
+ desc =>
+ ?T("IPv4 address where the backend is located. "
+ "The default value is '127.0.0.1'.")}},
+ {port,
+ #{value => ?T("Port"),
+ desc =>
+ ?T("An internet port number at which the backend "
+ "is listening for incoming connections/packets. "
+ "The default value is '11111'.")}}]}.
diff --git a/src/mod_mix.erl b/src/mod_mix.erl
index 8a55f1cbe..bc69c1aa1 100644
--- a/src/mod_mix.erl
+++ b/src/mod_mix.erl
@@ -30,6 +30,7 @@
-export([route/1]).
%% gen_mod callbacks
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
+-export([mod_doc/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, format_status/2]).
@@ -96,6 +97,43 @@ mod_options(Host) ->
{name, ?T("Channels")},
{db_type, ejabberd_config:default_db(Host, ?MODULE)}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module is an experimental implementation of "
+ "https://xmpp.org/extensions/xep-0369.html"
+ "[XEP-0369: Mediated Information eXchange (MIX)]. "
+ "MIX support was added in ejabberd 16.03 as an "
+ "experimental feature, updated in 19.02, and is not "
+ "yet ready to use in production. It's asserted that "
+ "the MIX protocol is going to replace the MUC protocol "
+ "in the future (see 'mod_muc')."), "",
+ ?T("The module depends on 'mod_mam'.")],
+ opts =>
+ [{access_create,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("An access rule to control MIX channels creations. "
+ "The default value is 'all'.")}},
+ {host,
+ #{desc => ?T("Deprecated. Use 'hosts' instead.")}},
+ {hosts,
+ #{value => ?T("[Host, ...]"),
+ desc =>
+ ?T("This option defines the Jabber IDs of the service. "
+ "If the 'hosts' option is not specified, the only Jabber ID will "
+ "be the hostname of the virtual host with the prefix \"mix.\". "
+ "The keyword '@HOST@' is replaced with the real virtual host name.")}},
+ {name,
+ #{value => ?T("Name"),
+ desc =>
+ ?T("A name of the service in the Service Discovery. "
+ "This will only be displayed by special XMPP clients. "
+ "The default value is 'Channels'.")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}}]}.
+
-spec route(stanza()) -> ok.
route(#iq{} = IQ) ->
ejabberd_router:process_iq(IQ);
diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl
index 7b01965c7..1e5b32ba4 100644
--- a/src/mod_mix_pam.erl
+++ b/src/mod_mix_pam.erl
@@ -26,6 +26,7 @@
%% gen_mod callbacks
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
+-export([mod_doc/0]).
%% Hooks and handlers
-export([bounce_sm_packet/1,
disco_sm_features/5,
@@ -103,6 +104,40 @@ mod_options(Host) ->
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module implements "
+ "https://xmpp.org/extensions/xep-0405.html"
+ "[XEP-0405: Mediated Information eXchange (MIX): "
+ "Participant Server Requirements]. "
+ "The module is needed if MIX compatible clients "
+ "on your server are going to join MIX channels "
+ "(either on your server or on any remote servers)."), "",
+ ?T("NOTE: 'mod_mix' is not required for this module "
+ "to work, however, without 'mod_mix_pam' the MIX "
+ "functionality of your local XMPP clients will be impaired.")],
+ opts =>
+ [{db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
+
-spec bounce_sm_packet({term(), stanza()}) -> {term(), stanza()}.
bounce_sm_packet({_, #message{to = #jid{lresource = <<>>} = To,
from = From,
diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl
index 60da1b796..b2f4ded68 100644
--- a/src/mod_mqtt.erl
+++ b/src/mod_mqtt.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2019 ProcessOne, SARL. All Rights Reserved.
+%%% @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]).
+-export([mod_doc/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -39,6 +40,7 @@
-include("logger.hrl").
-include("mqtt.hrl").
+-include("translate.hrl").
-define(MQTT_TOPIC_CACHE, mqtt_topic_cache).
-define(MQTT_PAYLOAD_CACHE, mqtt_payload_cache).
@@ -260,6 +262,91 @@ listen_options() ->
{tls_verify, false}].
%%%===================================================================
+%%% Doc
+%%%===================================================================
+mod_doc() ->
+ #{desc =>
+ ?T("This module adds support for the MQTT protocol "
+ "version '3.1.1' and '5.0'."),
+ opts =>
+ [{access_subscribe,
+ #{value => "{TopicFilter: AccessName}",
+ desc =>
+ ?T("Access rules to restrict access to topics "
+ "for subscribers. By default there are no restrictions.")}},
+ {access_publish,
+ #{value => "{TopicFilter: AccessName}",
+ desc =>
+ ?T("Access rules to restrict access to topics "
+ "for publishers. By default there are no restrictions.")}},
+ {max_queue,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("Maximum queue size for outgoing packets. "
+ "The default value is '5000'.")}},
+ {session_expiry,
+ #{value => "timeout()",
+ desc =>
+ ?T("The option specifies how long to wait for "
+ "an MQTT session resumption. When '0' is set, "
+ "the session gets destroyed when the underlying "
+ "client connection is closed. The default value is "
+ "'5' minutes.")}},
+ {max_topic_depth,
+ #{value => ?T("Depth"),
+ desc =>
+ ?T("The maximum topic depth, i.e. the number of "
+ "slashes ('/') in the topic. The default "
+ "value is '8'.")}},
+ {max_topic_aliases,
+ #{value => "0..65535",
+ desc =>
+ ?T("The maximum number of aliases a client "
+ "is able to associate with the topics. "
+ "The default value is '100'.")}},
+ {match_retained_limit,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("The option limits the number of retained messages "
+ "returned to a client when it subscribes to some "
+ "topic filter. The default value is '1000'.")}},
+ {queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("Same as top-level 'queue_type' option, "
+ "but applied to this module only.")}},
+ {ram_db_type,
+ #{value => "mnesia",
+ desc =>
+ ?T("Same as top-level 'default_ram_db' option, "
+ "but applied to this module only.")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, "
+ "but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, "
+ "but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, "
+ "but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, "
+ "but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, "
+ "but applied to this module only.")}}]}.
+
+%%%===================================================================
%%% Internal functions
%%%===================================================================
route(Mod, LServer, Pkt, ExpiryTime) ->
diff --git a/src/mod_mqtt_mnesia.erl b/src/mod_mqtt_mnesia.erl
index f5d2dec8e..d5fa44b59 100644
--- a/src/mod_mqtt_mnesia.erl
+++ b/src/mod_mqtt_mnesia.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2019 ProcessOne, SARL. All Rights Reserved.
+%%% @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
diff --git a/src/mod_mqtt_session.erl b/src/mod_mqtt_session.erl
index 6508a706c..b8a95e7a5 100644
--- a/src/mod_mqtt_session.erl
+++ b/src/mod_mqtt_session.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2019 ProcessOne, SARL. All Rights Reserved.
+%%% @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl
index 88fb86965..9449f2bea 100644
--- a/src/mod_mqtt_sql.erl
+++ b/src/mod_mqtt_sql.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2019 ProcessOne, SARL. All Rights Reserved.
+%%% @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
diff --git a/src/mod_mqtt_ws.erl b/src/mod_mqtt_ws.erl
index 421fdde17..3dc26d558 100644
--- a/src/mod_mqtt_ws.erl
+++ b/src/mod_mqtt_ws.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2019 ProcessOne, SARL. All Rights Reserved.
+%%% @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index afbefa28a..65f70d1d6 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,6 +36,7 @@
stop/1,
start_link/2,
reload/3,
+ mod_doc/0,
room_destroyed/4,
store_room/4,
store_room/5,
@@ -1276,3 +1277,380 @@ mod_options(Host) ->
{allow_private_messages_from_visitors,anyone},
{max_users,200},
{presence_broadcast,[moderator,participant,visitor]}]}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module provides support for https://xmpp.org/extensions/xep-0045.html"
+ "[XEP-0045: Multi-User Chat]. Users can discover existing rooms, "
+ "join or create them. Occupants of a room can chat in public or have private chats."),
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("You can specify who is allowed to use the Multi-User Chat service. "
+ "By default everyone is allowed to use it.")}},
+ {access_admin,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option specifies who is allowed to administrate "
+ "the Multi-User Chat service. The default value is 'none', "
+ "which means that only the room creator can administer "
+ "their room. The administrators can send a normal message "
+ "to the service JID, and it will be shown in all active "
+ "rooms as a service message. The administrators can send a "
+ "groupchat message to the JID of an active room, and the "
+ "message will be shown in the room as a service message.")}},
+ {access_create,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("To configure who is allowed to create new rooms at the "
+ "Multi-User Chat service, this option can be used. "
+ "By default any account in the local ejabberd server is "
+ "allowed to create rooms.")}},
+ {access_persistent,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("To configure who is allowed to modify the 'persistent' room option. "
+ "By default any account in the local ejabberd server is allowed to "
+ "modify that option.")}},
+ {access_mam,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("To configure who is allowed to modify the 'mam' room option. "
+ "By default any account in the local ejabberd server is allowed to "
+ "modify that option.")}},
+ {access_register,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option specifies who is allowed to register nickname "
+ "within the Multi-User Chat service. The default is 'all' for "
+ "backward compatibility, which means that any user is allowed "
+ "to register any free nick.")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Define the type of persistent storage where the module will "
+ "store room information. The default is the storage defined "
+ "by the global option 'default_db', or 'mnesia' if omitted.")}},
+ {ram_db_type,
+ #{value => "mnesia",
+ desc =>
+ ?T("Define the type of volatile (in-memory) storage where the module "
+ "will store room information. The only available value for this "
+ "module is 'mnesia'.")}},
+ {hibernation_timeout,
+ #{value => "infinity | Seconds",
+ desc =>
+ ?T("Timeout before hibernating the room process, expressed "
+ "in seconds. The default value is 'infinity'.")}},
+ {history_size,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("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're only using modern clients "
+ "and have 'mod_mam' module loaded.")}},
+ {host, #{desc => ?T("Deprecated. Use 'hosts' instead.")}},
+ {hosts,
+ #{value => ?T("[Host, ...]"),
+ desc =>
+ ?T("This option defines the Jabber IDs of the service. "
+ "If the 'hosts' option is not specified, the only Jabber ID will "
+ "be the hostname of the virtual host with the prefix \"conference.\". "
+ "The keyword '@HOST@' is replaced with the real virtual host name.")}},
+ {name,
+ #{value => "string()",
+ desc =>
+ ?T("The value of the service name. This name is only visible in some "
+ "clients that support https://xmpp.org/extensions/xep-0030.html"
+ "[XEP-0030: Service Discovery]. The default is 'Chatrooms'.")}},
+ {max_room_desc,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("This option defines the maximum number of characters that "
+ "Room Description can have when configuring the room. "
+ "The default value is 'infinity'.")}},
+ {max_room_id,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("This option defines the maximum number of characters that "
+ "Room ID can have when creating a new room. "
+ "The default value is 'infinity'.")}},
+ {max_room_name,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("This option defines the maximum number of characters "
+ "that Room Name can have when configuring the room. "
+ "The default value is 'infinity'.")}},
+ {max_rooms_discoitems,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("When there are more rooms than this 'Number', "
+ "only the non-empty ones are returned in a Service Discovery query. "
+ "The default value is '100'.")}},
+ {max_user_conferences,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("This option defines the maximum number of rooms that any "
+ "given user can join. The default value is '100'. This option "
+ "is used to prevent possible abuses. Note that this is a soft "
+ "limit: some users can sometimes join more conferences in "
+ "cluster configurations.")}},
+ {max_users,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("This option defines at the service level, the maximum "
+ "number of users allowed per room. It can be lowered in "
+ "each room configuration but cannot be increased in "
+ "individual room configuration. The default value is '200'.")}},
+ {max_users_admin_threshold,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("This option defines the number of service admins or room "
+ "owners allowed to enter the room when the maximum number "
+ "of allowed occupants was reached. The default limit is '5'.")}},
+ {max_users_presence,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("This option defines after how many users in the room, "
+ "it is considered overcrowded. When a MUC room is considered "
+ "overcrowed, presence broadcasts are limited to reduce load, "
+ "traffic and excessive presence \"storm\" received by participants.")}},
+ {min_message_interval,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("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.")}},
+ {min_presence_interval,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("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.")}},
+ {queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("Same as top-level 'queue_type' option, but applied to this module only.")}},
+ {regexp_room_id,
+ #{value => "string()",
+ desc =>
+ ?T("This option defines the regular expression that a Room ID "
+ "must satisfy to allow the room creation. The default value "
+ "is the empty string.")}},
+ {preload_rooms,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to load all persistent rooms in memory on startup. "
+ "If disabled, the room is only loaded on first participant join. "
+ "The default is 'true'. It makes sense to disable room preloading "
+ "when the number of rooms is high: this will improve server startup "
+ "time and memory consumption.")}},
+ {room_shaper,
+ #{value => "none | ShaperName",
+ desc =>
+ ?T("This option defines shaper for the MUC rooms. "
+ "The default value is 'none'.")}},
+ {user_message_shaper,
+ #{value => "none | ShaperName",
+ desc =>
+ ?T("This option defines shaper for the users messages. "
+ "The default value is 'none'.")}},
+ {user_presence_shaper,
+ #{value => "none | ShaperName",
+ desc =>
+ ?T("This option defines shaper for the users presences. "
+ "The default value is 'none'.")}},
+ {vcard,
+ #{value => ?T("vCard"),
+ desc =>
+ ?T("A custom vCard of the service that will be displayed "
+ "by some XMPP clients in Service Discovery. The value of "
+ "'vCard' is a YAML map constructed from an XML representation "
+ "of vCard. Since the representation has no attributes, "
+ "the mapping is straightforward."),
+ example =>
+ [{?T("For example, the following XML representation of vCard:"),
+ ["<vCard xmlns='vcard-temp'>",
+ " <FN>Conferences</FN>",
+ " <ADR>",
+ " <WORK/>",
+ " <STREET>Elm Street</STREET>",
+ " </ADR>",
+ "</vCard>"]},
+ {?T("will be translated to:"),
+ ["vcard:",
+ " fn: Conferences",
+ " adr:",
+ " -",
+ " work: true",
+ " street: Elm Street"]}]}},
+ {default_room_options,
+ #{value => ?T("Options"),
+ desc =>
+ ?T("This option allows to define the desired "
+ "default room options. Note that the creator of a room "
+ "can modify the options of his room at any time using an "
+ "XMPP client with MUC capability. The 'Options' are:")},
+ [{allow_change_subj,
+ #{value => "true | false",
+ desc =>
+ ?T("Allow occupants to change the subject. "
+ "The default value is 'true'.")}},
+ {allow_private_messages,
+ #{value => "true | false",
+ desc =>
+ ?T("Occupants can send private messages to other occupants. "
+ "The default value is 'true'.")}},
+ {allow_query_users,
+ #{value => "true | false",
+ desc =>
+ ?T("Occupants can send IQ queries to other occupants. "
+ "The default value is 'true'.")}},
+ {allow_user_invites,
+ #{value => "true | false",
+ desc =>
+ ?T("Allow occupants to send invitations. "
+ "The default value is 'false'.")}},
+ {allow_visitor_nickchange,
+ #{value => "true | false",
+ desc => ?T("Allow visitors to change nickname. "
+ "The default value is 'true'.")}},
+ {allow_visitor_status,
+ #{value => "true | false",
+ desc =>
+ ?T("Allow visitors to send status text in presence updates. "
+ "If disallowed, the status text is stripped before broadcasting "
+ "the presence update to all the room occupants. "
+ "The default value is 'true'.")}},
+ {anonymous,
+ #{value => "true | false",
+ desc =>
+ ?T("The room is anonymous: occupants don't see the real "
+ "JIDs of other occupants. Note that the room moderators "
+ "can always see the real JIDs of the occupants. "
+ "The default value is 'true'.")}},
+ {captcha_protected,
+ #{value => "true | false",
+ desc =>
+ ?T("When a user tries to join a room where they have no "
+ "affiliation (not owner, admin or member), the room "
+ "requires them to fill a CAPTCHA challenge (see section "
+ "https://docs.ejabberd.im/admin/configuration/#captcha[CAPTCHA] "
+ "in order to accept their join in the room. "
+ "The default value is 'false'.")}},
+ {lang,
+ #{value => ?T("Language"),
+ desc =>
+ ?T("Preferred language for the discussions in the room. "
+ "The language format should conform to RFC 5646. "
+ "There is no value by default.")}},
+ {logging,
+ #{value => "true | false",
+ desc =>
+ ?T("The public messages are logged using 'mod_muc_log'. "
+ "The default value is 'false'.")}},
+ {members_by_default,
+ #{value => "true | false",
+ desc =>
+ ?T("The occupants that enter the room are participants "
+ "by default, so they have \"voice\". "
+ "The default value is 'true'.")}},
+ {members_only,
+ #{value => "true | false",
+ desc =>
+ ?T("Only members of the room can enter. "
+ "The default value is 'false'.")}},
+ {moderated,
+ #{value => "true | false",
+ desc =>
+ ?T("Only occupants with \"voice\" can send public messages. "
+ "The default value is 'true'.")}},
+ {password_protected,
+ #{value => "true | false",
+ desc =>
+ ?T("The password is required to enter the room. "
+ "The default value is 'false'.")}},
+ {password,
+ #{value => ?T("Password"),
+ desc =>
+ ?T("Password of the room. Implies option 'password_protected' "
+ "set to 'true'. There is no default value.")}},
+ {persistent,
+ #{value => "true | false",
+ desc =>
+ ?T("The room persists even if the last participant leaves. "
+ "The default value is 'false'.")}},
+ {public,
+ #{value => "true | false",
+ desc =>
+ ?T("The room is public in the list of the MUC service, "
+ "so it can be discovered. MUC admins and room participants "
+ "will see private rooms in Service Discovery if their XMPP "
+ "client supports this feature. "
+ "The default value is 'true'.")}},
+ {public_list,
+ #{value => "true | false",
+ desc =>
+ ?T("The list of participants is public, without requiring "
+ "to enter the room. The default value is 'true'.")}},
+ {mam,
+ #{value => "true | false",
+ desc =>
+ ?T("Enable message archiving. Implies mod_mam is enabled. "
+ "The default value is 'false'.")}},
+ {allow_subscription,
+ #{value => "true | false",
+ desc =>
+ ?T("Allow users to subscribe to room events as described in "
+ "https://docs.ejabberd.im/developer/xmpp-clients-bots/extensions/muc-sub/"
+ "[Multi-User Chat Subscriptions]. "
+ "The default value is 'false'.")}},
+ {title,
+ #{value => ?T("Room Title"),
+ desc =>
+ ?T("A human-readable title of the room. "
+ "There is no default value")}},
+ {allow_private_messages_from_visitors,
+ #{value => "anyone | moderators | nobody",
+ desc =>
+ ?T("Visitors can send private messages to other occupants. "
+ "The default value is 'anyone' which means visitors "
+ "can send private messages to any occupant.")}},
+ {max_users,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("Maximum number of occupants in the room. "
+ "The default value is '200'.")}},
+ {presence_broadcast,
+ #{value => "[moderator | participant | visitor, ...]",
+ desc =>
+ ?T("List of roles for which presence is broadcasted. "
+ "The list can contain one or several of: 'moderator', "
+ "'participant', 'visitor'. The default value is shown "
+ "in the example below:"),
+ example =>
+ ["presence_broadcast:",
+ " - moderator",
+ " - participant",
+ " - visitor"]}}]}]}.
diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl
index 2163b5e3e..abc9dd129 100644
--- a/src/mod_muc_admin.erl
+++ b/src/mod_muc_admin.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Sep 2007 by Badlop <badlop@ono.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -28,7 +28,7 @@
-behaviour(gen_mod).
--export([start/2, stop/1, reload/3, depends/2,
+-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,
create_room_with_opts/4, create_room/3, destroy_room/2,
@@ -625,10 +625,7 @@ justcreated_to_binary(J) when is_atom(J) ->
%% ok | error
%% @doc Create a room immediately with the default options.
create_room(Name1, Host1, ServerHost) ->
- case create_room_with_opts(Name1, Host1, ServerHost, []) of
- ok -> change_room_option(Name1, Host1, <<"persistent">>, <<"true">>);
- Error -> Error
- end.
+ create_room_with_opts(Name1, Host1, ServerHost, []).
create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
true = (error /= (Name = jid:nodeprep(Name1))),
@@ -643,7 +640,12 @@ create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
lists:keysort(1, DefRoomOpts)),
%% Store the room on the server, it is not started yet though at this point
- mod_muc:store_room(ServerHost, Host, Name, RoomOpts),
+ case lists:keyfind(persistent, 1, RoomOpts) of
+ {persistent, true} ->
+ mod_muc:store_room(ServerHost, Host, Name, RoomOpts);
+ _ ->
+ ok
+ end,
%% Get all remaining mod_muc parameters that might be utilized
Access = mod_muc_opt:access(ServerHost),
@@ -886,7 +888,7 @@ act_on_rooms(Method, Action, Rooms) ->
act_on_room(Method, destroy, {N, H, SH, Pid}) ->
Message = iolist_to_binary(io_lib:format(
- <<"Room destroyed by rooms_~ts_destroy.">>, [Method])),
+ <<"Room destroyed by rooms_~s_destroy.">>, [Method])),
mod_muc_room:destroy(Pid, Message),
mod_muc:room_destroyed(H, N, Pid, SH),
mod_muc:forget_room(SH, H, N);
@@ -1315,3 +1317,10 @@ find_hosts(ServerHost) ->
end.
mod_options(_) -> [].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module provides commands to administer local MUC "
+ "services and their MUC rooms. It also provides simple "
+ "WebAdmin pages to view the existing rooms."), "",
+ ?T("This module depends on 'mod_muc'.")]}.
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index b37f7cc7d..c622d5591 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Mar 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -39,7 +39,7 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3,
- mod_opt_type/1, mod_options/1, depends/2]).
+ mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -988,3 +988,124 @@ mod_options(_) ->
{timezone, local},
{url, undefined},
{top_link, {<<"/">>, <<"Home">>}}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module enables optional logging "
+ "of Multi-User Chat (MUC) public "
+ "conversations to HTML. Once you enable "
+ "this module, users can join a room using a "
+ "MUC capable XMPP client, and if they have "
+ "enough privileges, they can request the "
+ "configuration form in which they can set "
+ "the option to enable room logging."), "",
+ ?T("Features:"), "",
+ ?T("- Room details are added on top of each page: "
+ "room title, JID, author, subject and configuration."), "",
+ ?T("- The room JID in the generated HTML is a link "
+ "to join the room (using XMPP URI)."), "",
+ ?T("- Subject and room configuration changes are tracked "
+ "and displayed."), "",
+ ?T("- Joins, leaves, nick changes, kicks, bans and '/me' "
+ "are tracked and displayed, including the reason if available."), "",
+ ?T("- Generated HTML files are XHTML 1.0 Transitional and "
+ "CSS compliant."), "",
+ ?T("- Timestamps are self-referencing links."), "",
+ ?T("- Links on top for quicker navigation: "
+ "Previous day, Next day, Up."), "",
+ ?T("- CSS is used for style definition, and a custom "
+ "CSS file can be used."), "",
+ ?T("- URLs on messages and subjects are converted to hyperlinks."), "",
+ ?T("- Timezone used on timestamps is shown on the log files."), "",
+ ?T("- A custom link can be added on top of each page."), "",
+ ?T("The module depends on 'mod_muc'.")],
+ opts =>
+ [{access_log,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option restricts which occupants are "
+ "allowed to enable or disable room logging. "
+ "The default value is 'muc_admin'. NOTE: "
+ "for this default setting you need to have an "
+ "access rule for 'muc_admin' in order to take effect.")}},
+ {cssfile,
+ #{value => ?T("Path | URL"),
+ desc =>
+ ?T("With this option you can set whether the HTML "
+ "files should have a custom CSS file or if they "
+ "need to use the embedded CSS. Allowed values "
+ "are either 'Path' to local file or an 'URL' to "
+ "a remote file. By default a predefined CSS will "
+ "be embedded into the HTML page.")}},
+ {dirname,
+ #{value => "room_jid | room_name",
+ desc =>
+ ?T("Allows to configure the name of the room directory. "
+ "If set to 'room_jid', the room directory name will "
+ "be the full room JID. Otherwise, the room directory "
+ "name will be only the room name, not including the "
+ "MUC service name. The default value is 'room_jid'.")}},
+ {dirtype,
+ #{value => "subdirs | plain",
+ desc =>
+ ?T("The type of the created directories can be specified "
+ "with this option. If set to 'subdirs', subdirectories "
+ "are created for each year and month. Otherwise, the "
+ "names of the log files contain the full date, and "
+ "there are no subdirectories. The default value is 'subdirs'.")}},
+ {file_format,
+ #{value => "html | plaintext",
+ desc =>
+ ?T("Define the format of the log files: 'html' stores "
+ "in HTML format, 'plaintext' stores in plain text. "
+ "The default value is 'html'.")}},
+ {file_permissions,
+ #{value => "{mode: Mode, group: Group}",
+ desc =>
+ ?T("Define the permissions that must be used when "
+ "creating the log files: the number of the mode, "
+ "and the numeric id of the group that will own the "
+ "files. The default value is shown in the example below:"),
+ example =>
+ ["file_permissions:",
+ " mode: 644",
+ " group: 33"]}},
+ {outdir,
+ #{value => ?T("Path"),
+ desc =>
+ ?T("This option sets the full path to the directory "
+ "in which the HTML files should be stored. "
+ "Make sure the ejabberd daemon user has write "
+ "access on that directory. The default value is 'www/muc'.")}},
+ {spam_prevention,
+ #{value => "true | false",
+ desc =>
+ ?T("If set to 'true', a special attribute is added to links "
+ "that prevent their indexation by search engines. "
+ "The default value is 'true', which mean that 'nofollow' "
+ "attributes will be added to user submitted links.")}},
+ {timezone,
+ #{value => "local | universal",
+ desc =>
+ ?T("The time zone for the logs is configurable with "
+ "this option. If set to 'local', the local time, as "
+ "reported to Erlang emulator by the operating system, "
+ "will be used. Otherwise, UTC time will be used. "
+ "The default value is 'local'.")}},
+ {url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("A top level 'URL' where a client can access "
+ "logs of a particular conference. The conference name "
+ "is appended to the URL if 'dirname' option is set to "
+ "'room_name' or a conference JID is appended to the 'URL' "
+ "otherwise. There is no default value.")}},
+ {top_link,
+ #{value => "{URL: Text}",
+ desc =>
+ ?T("With this option you can customize the link on "
+ "the top right corner of each log file. "
+ "The default value is shown in the example below:"),
+ example =>
+ ["top_link:",
+ " /: Home"]}}]}.
diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl
index e5b98fb78..cf0987a78 100644
--- a/src/mod_muc_mnesia.erl
+++ b/src/mod_muc_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index ea2b069d7..4d8816e97 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -371,8 +371,8 @@ normal_state({route, <<"">>,
case is_user_online(From, StateData) of
true ->
ErrorText = ?T("It is not allowed to send error messages to the"
- " room. The participant (~ts) has sent an error "
- "message (~ts) and got kicked from the room"),
+ " room. The participant (~s) has sent an error "
+ "message (~s) and got kicked from the room"),
NewState = expulse_participant(Packet, From, StateData,
translate:translate(Lang,
ErrorText)),
@@ -517,8 +517,8 @@ normal_state({route, ToNick,
{expulse_sender, Reason} ->
?DEBUG(Reason, []),
ErrorText = ?T("It is not allowed to send error messages to the"
- " room. The participant (~ts) has sent an error "
- "message (~ts) and got kicked from the room"),
+ " room. The participant (~s) has sent an error "
+ "message (~s) and got kicked from the room"),
NewState = expulse_participant(Packet, From, StateData,
translate:translate(Lang, ErrorText)),
{next_state, normal_state, NewState};
@@ -1291,8 +1291,8 @@ do_process_presence(Nick, #presence{from = From, type = unavailable} = Packet,
do_process_presence(_Nick, #presence{from = From, type = error, lang = Lang} = Packet,
StateData) ->
ErrorText = ?T("It is not allowed to send error messages to the"
- " room. The participant (~ts) has sent an error "
- "message (~ts) and got kicked from the room"),
+ " room. The participant (~s) has sent an error "
+ "message (~s) and got kicked from the room"),
expulse_participant(Packet, From, StateData,
translate:translate(Lang, ErrorText)).
@@ -1383,7 +1383,7 @@ decide_fate_message(#message{type = error} = Msg,
%% If this is an error stanza and its condition matches a criteria
true ->
Reason = str:format("This participant is considered a ghost "
- "and is expulsed: ~ts",
+ "and is expulsed: ~s",
[jid:encode(From)]),
{expulse_sender, Reason};
false -> continue_delivery
@@ -2991,7 +2991,7 @@ find_changed_items(UJID, UAffiliation, URole,
Nick /= <<"">> ->
case find_jids_by_nick(Nick, StateData) of
[] ->
- ErrText = {?T("Nickname ~ts does not exist in the room"),
+ ErrText = {?T("Nickname ~s does not exist in the room"),
[Nick]},
throw({error, xmpp:err_not_acceptable(ErrText, Lang)});
JIDList ->
@@ -3486,7 +3486,7 @@ get_config(Lang, StateData, From) ->
Config = StateData#state.config,
MaxUsersRoom = get_max_users(StateData),
Title = str:format(
- translate:translate(Lang, ?T("Configuration of room ~ts")),
+ translate:translate(Lang, ?T("Configuration of room ~s")),
[jid:encode(StateData#state.jid)]),
Fs = [{roomname, Config#config.title},
{roomdesc, Config#config.description},
@@ -3629,7 +3629,7 @@ set_config(Opts, Config, ServerHost, Lang) ->
{0, undefined} ->
?ERROR_MSG("set_room_option hook failed for "
"option '~ts' with value ~p", [O, V]),
- Txt = {?T("Failed to process option '~ts'"), [O]},
+ Txt = {?T("Failed to process option '~s'"), [O]},
{error, xmpp:err_internal_server_error(Txt, Lang)};
{Pos, Val} ->
setelement(Pos, C, Val)
@@ -4463,7 +4463,7 @@ route_invitation(From, Pkt, Invitation, Lang, StateData) ->
[io_lib:format(
translate:translate(
Lang,
- ?T("~ts invites you to the room ~ts")),
+ ?T("~s invites you to the room ~s")),
[jid:encode(From),
jid:encode({StateData#state.room, StateData#state.host, <<"">>})]),
case (StateData#state.config)#config.password_protected of
diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl
index 4d7e5e8dd..07c1acb2f 100644
--- a/src/mod_muc_sql.erl
+++ b/src/mod_muc_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_muc_sup.erl b/src/mod_muc_sup.erl
index 99c2620ce..cf448f6ec 100644
--- a/src/mod_muc_sup.erl
+++ b/src/mod_muc_sup.erl
@@ -2,7 +2,7 @@
%%% Created : 4 Jul 2019 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl
index e76e9a63d..d8205e8a9 100644
--- a/src/mod_multicast.erl
+++ b/src/mod_multicast.erl
@@ -5,7 +5,7 @@
%%% Created : 29 May 2007 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -41,7 +41,7 @@
-export([init/1, handle_info/2, handle_call/3,
handle_cast/2, terminate/2, code_change/3]).
--export([purge_loop/1, mod_opt_type/1, mod_options/1, depends/2]).
+-export([purge_loop/1, mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-include("translate.hrl").
@@ -495,7 +495,7 @@ report_not_jid(From, Packet, Dests) ->
|| Dest <- Dests],
[route_error(
xmpp:set_from_to(Packet, From, From), jid_malformed,
- str:format(?T("This service can not process the address: ~ts"), [D]))
+ str:format(?T("This service can not process the address: ~s"), [D]))
|| D <- Dests2].
%%%-------------------------
@@ -1160,3 +1160,82 @@ mod_options(Host) ->
{limits, [{local, []}, {remote, []}]},
{vcard, undefined},
{name, ?T("Multicast")}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module implements a service for "
+ "https://xmpp.org/extensions/xep-0054.html"
+ "[XEP-0033: Extended Stanza Addressing].")],
+ opts =>
+ [{access,
+ #{value => "Access",
+ desc =>
+ ?T("The access rule to restrict who can send packets to "
+ "the multicast service. Default value: 'all'.")}},
+ {host,
+ #{desc => ?T("Deprecated. Use 'hosts' instead.")}},
+ {hosts,
+ #{value => ?T("[Host, ...]"),
+ desc =>
+ [?T("This option defines the Jabber IDs of the service. "
+ "If the 'hosts' option is not specified, the only "
+ "Jabber ID will be the hostname of the virtual host "
+ "with the prefix \"multicast.\". The keyword '@HOST@' "
+ "is replaced with the real virtual host name."),
+ ?T("The default value is 'multicast.@HOST@'.")]}},
+ {limits,
+ #{value => "Sender: Stanza: Number",
+ desc =>
+ [?T("Specify a list of custom limits which override the "
+ "default ones defined in XEP-0033. Limits are defined "
+ "per sender type and stanza type, where:"),
+ ?T("- The sender type can be: 'local' or 'remote'."), "",
+ ?T("- The stanza type can be: 'message' or 'presence'."), "",
+ ?T("- The number can be a positive integer or the key word 'infinite'.")],
+ example =>
+ [{?T("Default values:"),
+ ["local:",
+ " message: 100",
+ " presence: 100",
+ "remote:",
+ " message: 20",
+ " presence: 20"]}
+ ]}},
+ {name,
+ #{desc => ?T("Service name to provide in the Info query to the "
+ "Service Discovery. Default is '\"Multicast\"'.")}},
+ {vcard,
+ #{desc => ?T("vCard element to return when queried. "
+ "Default value is 'undefined'.")}}],
+ example =>
+ ["# Only admins can send packets to multicast service",
+ "access_rules:",
+ " multicast:",
+ " - allow: admin",
+ "",
+ "# If you want to allow all your users:",
+ "access_rules:",
+ " multicast:",
+ " - allow",
+ "",
+ "# This allows both admins and remote users to send packets,",
+ "# but does not allow local users",
+ "acl:",
+ " allservers:",
+ " server_glob: \"*\"",
+ "access_rules:",
+ " multicast:",
+ " - allow: admin",
+ " - deny: local",
+ " - allow: allservers",
+ "",
+ "modules:",
+ " mod_multicast:",
+ " host: multicast.example.org",
+ " access: multicast",
+ " limits:",
+ " local:",
+ " message: 40",
+ " presence: infinite",
+ " remote:",
+ " message: 150"]}.
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index c3fca8868..9021a08df 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -63,7 +63,7 @@
webadmin_user/4,
webadmin_user_parse_query/5]).
--export([mod_opt_type/1, mod_options/1, depends/2]).
+-export([mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]).
-deprecated({get_queue_length,2}).
@@ -493,26 +493,20 @@ store_packet({_Action, #message{from = From, to = To} = Packet} = Acc) ->
case check_event(Packet) of
true ->
#jid{luser = LUser, lserver = LServer} = To,
- case ejabberd_hooks:run_fold(store_offline_message, LServer,
- Packet, []) of
- drop ->
- Acc;
- NewPacket ->
- TimeStamp = erlang:timestamp(),
- Expire = find_x_expire(TimeStamp, NewPacket),
- OffMsg = #offline_msg{us = {LUser, LServer},
- timestamp = TimeStamp,
- expire = Expire,
- from = From,
- to = To,
- packet = NewPacket},
- case store_offline_msg(OffMsg) of
- ok ->
- {offlined, NewPacket};
- {error, Reason} ->
- discard_warn_sender(Packet, Reason),
- stop
- end
+ TimeStamp = erlang:timestamp(),
+ Expire = find_x_expire(TimeStamp, Packet),
+ OffMsg = #offline_msg{us = {LUser, LServer},
+ timestamp = TimeStamp,
+ expire = Expire,
+ from = From,
+ to = To,
+ packet = Packet},
+ case store_offline_msg(OffMsg) of
+ ok ->
+ {offlined, Packet};
+ {error, Reason} ->
+ discard_warn_sender(Packet, Reason),
+ stop
end;
_ ->
maybe_update_cache(To, Packet),
@@ -1241,3 +1235,119 @@ mod_options(Host) ->
{use_cache, ejabberd_option:use_cache(Host)},
{cache_size, ejabberd_option:cache_size(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module implements "
+ "https://xmpp.org/extensions/xep-0160.html"
+ "[XEP-0160: Best Practices for Handling Offline Messages] "
+ "and https://xmpp.org/extensions/xep-0013.html"
+ "[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."), "",
+ ?T("NOTE: 'ejabberdctl' has a command to "
+ "delete expired messages (see chapter"
+ "https://docs.ejabberd.im/admin/guide/managing"
+ "[Managing an ejabberd server] in online documentation.")],
+ opts =>
+ [{access_max_user_messages,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("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 "
+ "'max_user_offline_messages'.")}},
+ {store_empty_body,
+ #{value => "true | false | unless_chat_state",
+ desc =>
+ ?T("Whether or not to store messages that lack a <body/> "
+ "element. The default value is 'unless_chat_state', "
+ "which tells ejabberd to store messages even if they "
+ "lack the <body/> element, unless they only contain a "
+ "chat state notification (as defined in "
+ "https://xmpp.org/extensions/xep-0085.html"
+ "[XEP-0085: Chat State Notifications].")}},
+ {store_groupchat,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether or not to store groupchat messages. "
+ "The default value is 'false'.")}},
+ {use_mam_for_storage,
+ #{value => "true | false",
+ desc =>
+ ?T("This is an experimetal option. Enabling this option "
+ "will make 'mod_offline' not use the former spool "
+ "table for storing MucSub offline messages, but will "
+ "use the archive table instead. This use of the archive "
+ "table is cleaner and it makes it possible for clients "
+ "to slowly drop the former offline use case and rely on "
+ "message archive instead. It also further reduces the "
+ "storage required when you enabled MucSub. Enabling this "
+ "option has a known drawback for the moment: most of "
+ "flexible message retrieval queries don't work (those that "
+ "allow retrieval/deletion of messages by id), but this "
+ "specification is not widely used. The default value "
+ "is 'false' to keep former behaviour as default and "
+ "ensure this option is disabled.")}},
+ {bounce_groupchat,
+ #{value => "true | false",
+ desc =>
+ ?T("This option is use the disable an optimisation that "
+ "avoids bouncing error messages when groupchat messages "
+ "could not be stored as offline. It will reduce chat "
+ "room load, without any drawback in standard use cases. "
+ "You may change default value only if you have a custom "
+ "module which uses offline hook after 'mod_offline'. This "
+ "option can be useful for both standard MUC and MucSub, "
+ "but the bounce is much more likely to happen in the context "
+ "of MucSub, so it is even more important to have it on "
+ "large MucSub services. The default value is 'false', meaning "
+ "the optimisation is enabled.")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}],
+ example =>
+ [{?T("This example allows power users to have as much as 5000 "
+ "offline messages, administrators up to 2000, and all the "
+ "other users up to 100:"),
+ ["acl:",
+ " admin:",
+ " user:",
+ " - admin1@localhost",
+ " - admin2@example.org",
+ " poweruser:",
+ " user:",
+ " - bob@example.org",
+ " - jane@example.org",
+ "",
+ "shaper_rules:",
+ " max_user_offline_messages:",
+ " - 5000: poweruser",
+ " - 2000: admin",
+ " - 100",
+ "",
+ "modules:",
+ " ...",
+ " mod_offline:",
+ " access_max_user_messages: max_user_offline_messages",
+ " ..."
+ ]}]}.
diff --git a/src/mod_offline_mnesia.erl b/src/mod_offline_mnesia.erl
index 5d0bf2921..95a79b3e3 100644
--- a/src/mod_offline_mnesia.erl
+++ b/src/mod_offline_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl
index 5cc5b4a71..b9cf9c96e 100644
--- a/src/mod_offline_sql.erl
+++ b/src/mod_offline_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_ping.erl b/src/mod_ping.erl
index 8e6827247..c83f471d2 100644
--- a/src/mod_ping.erl
+++ b/src/mod_ping.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Jul 2009 by Brian Cully <bjc@kublai.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -49,7 +49,7 @@
-export([init/1, terminate/2, handle_call/3,
handle_cast/2, handle_info/2, code_change/3]).
--export([iq_ping/1, user_online/3, user_offline/3,
+-export([iq_ping/1, user_online/3, user_offline/3, mod_doc/0,
user_send/1, mod_opt_type/1, mod_options/1, depends/2]).
-record(state,
@@ -271,3 +271,56 @@ mod_options(_Host) ->
{ping_ack_timeout, undefined},
{send_pings, false},
{timeout_action, none}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements support for "
+ "https://xmpp.org/extensions/xep-0199.html"
+ "[XEP-0199: XMPP Ping] and periodic keepalives. "
+ "When this module is enabled ejabberd responds "
+ "correctly to ping requests, as defined by the protocol."),
+ opts =>
+ [{ping_interval,
+ #{value => "timeout()",
+ desc =>
+ ?T("How often to send pings to connected clients, "
+ "if option 'send_pings' is set to 'true'. If a client "
+ "connection does not send or receive any stanza "
+ "within this interval, a ping request is sent to "
+ "the client. The default value is '1' minute.")}},
+ {ping_ack_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("How long to wait before deeming that a client "
+ "has not answered a given server ping request. "
+ "The default value is '32' seconds.")}},
+ {send_pings,
+ #{value => "true | false",
+ desc =>
+ ?T("If this option is set to 'true', the server "
+ "sends pings to connected clients that are not "
+ "active in a given interval defined in 'ping_interval' "
+ "option. This is useful to keep client connections "
+ "alive or checking availability. "
+ "The default value is 'false'.")}},
+ {timeout_action,
+ #{value => "none | kill",
+ desc =>
+ ?T("What to do when a client does not answer to a "
+ "server ping request in less than period defined "
+ "in 'ping_ack_timeout' option: "
+ "'kill' means destroying the underlying connection, "
+ "'none' means to do nothing. NOTE: when 'mod_stream_mgmt' "
+ "module is loaded and stream management is enabled by "
+ "a client, killing the client connection doesn't mean "
+ "killing the client session - the session will be kept "
+ "alive in order to give the client a chance to resume it. "
+ "The default value is 'none'.")}}],
+ example =>
+ ["modules:",
+ " ...",
+ " mod_ping:",
+ " send_pings: true",
+ " ping_interval: 4 min",
+ " timeout_action: kill",
+ " ..."]}.
diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl
index f151e6310..c747ee9d0 100644
--- a/src/mod_pres_counter.erl
+++ b/src/mod_pres_counter.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Sep 2010 by Ahmed Omar
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -28,10 +28,10 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, check_packet/4,
- mod_opt_type/1, mod_options/1, depends/2]).
+ mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-
+-include("translate.hrl").
-include("xmpp.hrl").
-record(pres_counter,
@@ -129,3 +129,33 @@ mod_opt_type(interval) ->
mod_options(_) ->
[{count, 5}, {interval, timer:seconds(60)}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module detects flood/spam in presence "
+ "subscriptions traffic. If a user sends or receives "
+ "more of those stanzas in a given time interval, "
+ "the exceeding stanzas are silently dropped, and a "
+ "warning is logged."),
+ opts =>
+ [{count,
+ #{value => ?T("Number"),
+ desc =>
+ ?T("The number of subscription presence stanzas "
+ "(subscribe, unsubscribe, subscribed, unsubscribed) "
+ "allowed for any direction (input or output) per time "
+ "defined in 'interval' option. Please note that two "
+ "users subscribing to each other usually generate 4 "
+ "stanzas, so the recommended value is '4' or more. "
+ "The default value is '5'.")}},
+ {interval,
+ #{value => "timeout()",
+ desc =>
+ ?T("The time interval. The default value is '1' minute.")}}],
+ example =>
+ ["modules:",
+ " ...",
+ " mod_pres_counter:",
+ " count: 5",
+ " interval: 30 secs",
+ " ..."]}.
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index 850fd6aa5..33615da29 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,7 +36,7 @@
check_packet/4, remove_user/2, encode_list_item/1,
get_user_lists/2, get_user_list/3,
set_list/1, set_list/4, set_default_list/3,
- user_send_packet/1,
+ user_send_packet/1, mod_doc/0,
import_start/2, import_stop/2, import/5, import_info/0,
mod_opt_type/1, mod_options/1, depends/2]).
@@ -868,3 +868,35 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module implements "
+ "https://xmpp.org/extensions/xep-0016.html"
+ "[XEP-0016: Privacy Lists]."), "",
+ ?T("NOTE: Nowadays modern XMPP clients rely on "
+ "https://xmpp.org/extensions/xep-0191.html"
+ "[XEP-0191: Blocking Command] which is implemented by "
+ "'mod_blocking' module. However, you still need "
+ "'mod_privacy' loaded in order for 'mod_blocking' to work.")],
+ opts =>
+ [{db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
diff --git a/src/mod_privacy_mnesia.erl b/src/mod_privacy_mnesia.erl
index be50894e0..4ad9c3c96 100644
--- a/src/mod_privacy_mnesia.erl
+++ b/src/mod_privacy_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl
index 673787328..cd7c5e885 100644
--- a/src/mod_privacy_sql.erl
+++ b/src/mod_privacy_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_private.erl b/src/mod_private.erl
index 19f547642..08268e723 100644
--- a/src/mod_private.erl
+++ b/src/mod_private.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -33,7 +33,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process_sm_iq/1, import_info/0,
- remove_user/2, get_data/2, get_data/3, export/1,
+ remove_user/2, get_data/2, get_data/3, export/1, mod_doc/0,
import/5, import_start/2, mod_opt_type/1, set_data/2,
mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6]).
@@ -111,6 +111,41 @@ mod_options(Host) ->
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module adds support for "
+ "https://xmpp.org/extensions/xep-0049.html"
+ "[XEP-0049: Private XML Storage]."), "",
+ ?T("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's conferences "
+ "(https://xmpp.org/extensions/xep-0048.html"
+ "[XEP-0048: Bookmarks]).")],
+ opts =>
+ [{db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
+
-spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]},
jid(), jid(), binary(), binary()) ->
{error, stanza_error()} | empty | {result, [binary()]}.
diff --git a/src/mod_private_mnesia.erl b/src/mod_private_mnesia.erl
index bf0ce26e8..4d97f6df1 100644
--- a/src/mod_private_mnesia.erl
+++ b/src/mod_private_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl
index 5e438a56a..3fe740c12 100644
--- a/src/mod_private_sql.erl
+++ b/src/mod_private_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl
index 9c05c60b0..c334348b4 100644
--- a/src/mod_privilege.erl
+++ b/src/mod_privilege.erl
@@ -4,7 +4,7 @@
%%% Purpose : XEP-0356: Privileged Entity
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,6 +32,7 @@
%% API
-export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2]).
+-export([mod_doc/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -82,6 +83,90 @@ mod_options(_) ->
{presence, [{managed_entity, none}, {roster, none}]},
{message, [{outgoing,none}]}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module is an implementation of "
+ "https://xmpp.org/extensions/xep-0356.html"
+ "[XEP-0356: Privileged Entity]. This extension "
+ "allows components to have privileged access to "
+ "other entity data (send messages on behalf of the "
+ "server or on behalf of a user, get/set user roster, "
+ "access presence information, etc.). This may be used "
+ "to write powerful external components, for example "
+ "implementing an external "
+ "https://xmpp.org/extensions/xep-0163.html[PEP] or "
+ "https://xmpp.org/extensions/xep-0313.html[MAM] service."), "",
+ ?T("By default a component does not have any privileged access. "
+ "It is worth noting that the permissions grant access to "
+ "the component to a specific data type for all users of "
+ "the virtual host on which 'mod_privilege' is loaded."), "",
+ ?T("NOTE: This module is complementary to 'mod_delegation', "
+ "but can also be used separately.")],
+ opts =>
+ [{roster,
+ #{value => ?T("Options"),
+ desc =>
+ ?T("This option defines roster permissions. "
+ "By default no permissions are given. "
+ "The 'Options' are:")},
+ [{both,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Sets read/write access to a user's roster. "
+ "The default value is 'none'.")}},
+ {get,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Sets read access to a user's roster. "
+ "The default value is 'none'.")}},
+ {set,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Sets write access to a user's roster. "
+ "The default value is 'none'.")}}]},
+ {message,
+ #{value => ?T("Options"),
+ desc =>
+ ?T("This option defines permissions for messages. "
+ "By default no permissions are given. "
+ "The 'Options' are:")},
+ [{outgoing,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("The option defines an access rule for sending "
+ "outgoing messages by the component. "
+ "The default value is 'none'.")}}]},
+ {presence,
+ #{value => ?T("Options"),
+ desc =>
+ ?T("This option defines permissions for presences. "
+ "By default no permissions are given. "
+ "The 'Options' are:")},
+ [{managed_entity,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("An access rule that gives permissions to "
+ "the component to receive server presences. "
+ "The default value is 'none'.")}},
+ {roster,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("An access rule that gives permissions to "
+ "the component to receive the presence of both "
+ "the users and the contacts in their roster. "
+ "The default value is 'none'.")}}]}],
+ example =>
+ ["modules:",
+ " ...",
+ " mod_privilege:",
+ " roster:",
+ " get: all",
+ " presence:",
+ " managed_entity: all",
+ " message:",
+ " outgoing: all",
+ " ..."]}.
+
depends(_, _) ->
[].
diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl
index 718f49cb4..c5ac1fc73 100644
--- a/src/mod_proxy65.erl
+++ b/src/mod_proxy65.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -39,7 +39,7 @@
%% supervisor callbacks.
-export([init/1]).
--export([start_link/1, mod_opt_type/1, mod_options/1, depends/2]).
+-export([start_link/1, mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
-define(PROCNAME, ejabberd_mod_proxy65).
@@ -139,3 +139,150 @@ mod_options(Host) ->
{recbuf, 65536},
{sndbuf, 65536},
{shaper, none}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements "
+ "https://xmpp.org/extensions/xep-0065.html"
+ "[XEP-0065: SOCKS5 Bytestreams]. It allows ejabberd "
+ "to act as a file transfer proxy between two XMPP clients."),
+ opts =>
+ [{host,
+ #{desc => ?T("Deprecated. Use 'hosts' instead.")}},
+ {hosts,
+ #{value => ?T("[Host, ...]"),
+ desc =>
+ ?T("This option defines the Jabber IDs of the service. "
+ "If the 'hosts' option is not specified, the only Jabber ID will "
+ "be the hostname of the virtual host with the prefix \"proxy.\". "
+ "The keyword '@HOST@' is replaced with the real virtual host name.")}},
+ {name,
+ #{value => ?T("Name"),
+ desc =>
+ ?T("The value of the service name. This name is only visible in some "
+ "clients that support https://xmpp.org/extensions/xep-0030.html"
+ "[XEP-0030: Service Discovery]. The default is \"SOCKS5 Bytestreams\".")}},
+ {access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Defines an access rule for file transfer initiators. "
+ "The default value is 'all'. You may want to restrict "
+ "access to the users of your server only, in order to "
+ "avoid abusing your proxy by the users of remote "
+ "servers.")}},
+ {ram_db_type,
+ #{value => "mnesia | redis | sql",
+ desc =>
+ ?T("Define the type of volatile (in-memory) storage where the module "
+ "will store room information.")}},
+ {ip,
+ #{value => ?T("IPAddress"),
+ desc =>
+ ?T("This option specifies which network interface to listen "
+ "for. The default value is an IP address of the service's "
+ "DNS name, or, if fails, '127.0.0.1'.")}},
+ {hostname,
+ #{value => ?T("Host"),
+ desc =>
+ ?T("Defines a hostname offered by the proxy when "
+ "establishing a session with clients. This is useful "
+ "when you run the proxy behind a NAT. The keyword "
+ "'@HOST@' is replaced with the virtual host name. "
+ "The default is to use the value of 'ip' option. "
+ "Examples: 'proxy.mydomain.org', '200.150.100.50'.")}},
+ {port,
+ #{value => "1..65535",
+ desc =>
+ ?T("A port number to listen for incoming connections. "
+ "The default value is '7777'.")}},
+ {auth_type,
+ #{value => "anonymous | plain",
+ desc =>
+ ?T("SOCKS5 authentication type. "
+ "The default value is 'anonymous'. "
+ "If set to 'plain', ejabberd will use "
+ "authentication backend as it would "
+ "for SASL PLAIN.")}},
+ {max_connections,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Maximum number of active connections per file transfer "
+ "initiator. The default value is 'infinity'.")}},
+ {shaper,
+ #{value => ?T("Shaper"),
+ desc =>
+ ?T("This option defines a shaper for the file transfer peers. "
+ "A shaper with the maximum bandwidth will be selected. "
+ "The default is 'none', i.e. no shaper.")}},
+ {recbuf,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("A size of the buffer for incoming packets. "
+ "If you define a shaper, set the value of this "
+ "option to the size of the shaper in order "
+ "to avoid traffic spikes in file transfers. "
+ "The default value is '65536' bytes.")}},
+ {sndbuf,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("A size of the buffer for outgoing packets. "
+ "If you define a shaper, set the value of this "
+ "option to the size of the shaper in order "
+ "to avoid traffic spikes in file transfers. "
+ "The default value is '65536' bytes.")}},
+ {vcard,
+ #{value => ?T("vCard"),
+ desc =>
+ ?T("A custom vCard of the service that will be displayed "
+ "by some XMPP clients in Service Discovery. The value of "
+ "'vCard' is a YAML map constructed from an XML representation "
+ "of vCard. Since the representation has no attributes, "
+ "the mapping is straightforward."),
+ example =>
+ [{?T("For example, the following XML representation of vCard:"),
+ ["<vCard xmlns='vcard-temp'>",
+ " <FN>Conferences</FN>",
+ " <ADR>",
+ " <WORK/>",
+ " <STREET>Elm Street</STREET>",
+ " </ADR>",
+ "</vCard>"]},
+ {?T("will be translated to:"),
+ ["vcard:",
+ " fn: Conferences",
+ " adr:",
+ " -",
+ " work: true",
+ " street: Elm Street"]}]}}],
+ example =>
+ ["acl:",
+ " admin:",
+ " user: admin@example.org",
+ " proxy_users:",
+ " server: example.org",
+ "",
+ "access_rules:",
+ " proxy65_access:",
+ " allow: proxy_users",
+ "",
+ "shaper_rules:",
+ " proxy65_shaper:",
+ " none: admin",
+ " proxyrate: proxy_users",
+ "",
+ "shaper:",
+ " proxyrate: 10240",
+ "",
+ "modules:",
+ " ...",
+ " mod_proxy65:",
+ " host: proxy1.example.org",
+ " name: \"File Transfer Proxy\"",
+ " ip: 200.150.100.1",
+ " port: 7778",
+ " max_connections: 5",
+ " access: proxy65_access",
+ " shaper: proxy65_shaper",
+ " recbuf: 10240",
+ " sndbuf: 10240",
+ " ..."]}.
diff --git a/src/mod_proxy65_lib.erl b/src/mod_proxy65_lib.erl
index 70a762413..e4940d2fe 100644
--- a/src/mod_proxy65_lib.erl
+++ b/src/mod_proxy65_lib.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_mnesia.erl b/src/mod_proxy65_mnesia.erl
index 3f8c934ab..105dde0f2 100644
--- a/src/mod_proxy65_mnesia.erl
+++ b/src/mod_proxy65_mnesia.erl
@@ -2,7 +2,7 @@
%%% Created : 16 Jan 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_redis.erl b/src/mod_proxy65_redis.erl
index 131f83560..b622fd994 100644
--- a/src/mod_proxy65_redis.erl
+++ b/src/mod_proxy65_redis.erl
@@ -3,7 +3,7 @@
%%% Created : 31 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl
index 8778a03d8..fe07ca72a 100644
--- a/src/mod_proxy65_service.erl
+++ b/src/mod_proxy65_service.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl
index 31c2dcc36..cc8345dd9 100644
--- a/src/mod_proxy65_sql.erl
+++ b/src/mod_proxy65_sql.erl
@@ -3,7 +3,7 @@
%%% Created : 30 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_stream.erl b/src/mod_proxy65_stream.erl
index eb100872b..fcd1ceaac 100644
--- a/src/mod_proxy65_stream.erl
+++ b/src/mod_proxy65_stream.erl
@@ -4,7 +4,7 @@
%%% Purpose : Bytestream process.
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index 48a19b99f..7653d41e2 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_pubsub_mnesia.erl b/src/mod_pubsub_mnesia.erl
index 8f780a623..359164f87 100644
--- a/src/mod_pubsub_mnesia.erl
+++ b/src/mod_pubsub_mnesia.erl
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_pubsub_sql.erl b/src/mod_pubsub_sql.erl
index 655d43ea9..cdf41c8c9 100644
--- a/src/mod_pubsub_sql.erl
+++ b/src/mod_pubsub_sql.erl
@@ -1,5 +1,5 @@
%%%----------------------------------------------------------------------
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_push.erl b/src/mod_push.erl
index 409ded8cd..f8783b314 100644
--- a/src/mod_push.erl
+++ b/src/mod_push.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jul 2017 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -31,7 +31,7 @@
%% gen_mod callbacks.
-export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2]).
-
+-export([mod_doc/0]).
%% ejabberd_hooks callbacks.
-export([disco_sm_features/5, c2s_session_pending/1, c2s_copy_session/2,
c2s_handle_cast/2, c2s_stanza/3, mam_message/7, offline_message/1,
@@ -153,6 +153,60 @@ mod_options(Host) ->
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements the XMPP server's part of "
+ "the push notification solution specified in "
+ "https://xmpp.org/extensions/xep-0357.html"
+ "[XEP-0357: Push Notifications]. It does not generate, "
+ "for example, APNS or FCM notifications directly. "
+ "Instead, it's designed to work with so-called "
+ "\"app servers\" operated by third-party vendors of "
+ "mobile apps. Those app servers will usually trigger "
+ "notification delivery to the user's mobile device using "
+ "platform-dependant backend services such as FCM or APNS."),
+ opts =>
+ [{include_sender,
+ #{value => "true | false",
+ desc =>
+ ?T("If this option is set to 'true', the sender's JID "
+ "is included with push notifications generated for "
+ "incoming messages with a body. "
+ "The default value is 'false'.")}},
+ {include_body,
+ #{value => "true | false | Text",
+ desc =>
+ ?T("If this option is set to 'true', the message text "
+ "is included with push notifications generated for "
+ "incoming messages with a body. The option can instead "
+ "be set to a static 'Text', in which case the specified "
+ "text will be included in place of the actual message "
+ "body. This can be useful to signal the app server "
+ "whether the notification was triggered by a message "
+ "with body (as opposed to other types of traffic) "
+ "without leaking actual message contents. "
+ "The default value is \"New message\".")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
+
%%--------------------------------------------------------------------
%% ejabberd command callback.
%%--------------------------------------------------------------------
@@ -209,8 +263,8 @@ register_hooks(Host) ->
c2s_stanza, 50),
ejabberd_hooks:add(store_mam_message, Host, ?MODULE,
mam_message, 50),
- ejabberd_hooks:add(store_offline_message, Host, ?MODULE,
- offline_message, 50),
+ ejabberd_hooks:add(offline_message_hook, Host, ?MODULE,
+ offline_message, 55),
ejabberd_hooks:add(remove_user, Host, ?MODULE,
remove_user, 50).
@@ -228,8 +282,8 @@ unregister_hooks(Host) ->
c2s_stanza, 50),
ejabberd_hooks:delete(store_mam_message, Host, ?MODULE,
mam_message, 50),
- ejabberd_hooks:delete(store_offline_message, Host, ?MODULE,
- offline_message, 50),
+ ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE,
+ offline_message, 55),
ejabberd_hooks:delete(remove_user, Host, ?MODULE,
remove_user, 50).
@@ -373,10 +427,12 @@ mam_message(#message{} = Pkt, LUser, LServer, _Peer, _Nick, chat, Dir) ->
mam_message(Pkt, _LUser, _LServer, _Peer, _Nick, _Type, _Dir) ->
Pkt.
--spec offline_message(message()) -> message().
-offline_message(#message{meta = #{mam_archived := true}} = Pkt) ->
- Pkt; % Push notification was triggered via MAM.
-offline_message(#message{to = #jid{luser = LUser, lserver = LServer}} = Pkt) ->
+-spec offline_message({any(), message()}) -> {any(), message()}.
+offline_message({offlined, #message{meta = #{mam_archived := true}}} = Acc) ->
+ Acc; % Push notification was triggered via MAM.
+offline_message({offlined,
+ #message{to = #jid{luser = LUser,
+ lserver = LServer}} = Pkt} = Acc) ->
case lookup_sessions(LUser, LServer) of
{ok, [_|_] = Clients} ->
?DEBUG("Notifying ~ts@~ts of offline message", [LUser, LServer]),
@@ -384,7 +440,9 @@ offline_message(#message{to = #jid{luser = LUser, lserver = LServer}} = Pkt) ->
_ ->
ok
end,
- Pkt.
+ Acc;
+offline_message(Acc) ->
+ Acc.
-spec c2s_session_pending(c2s_state()) -> c2s_state().
c2s_session_pending(#{push_enabled := true, mgmt_queue := Queue} = State) ->
diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl
index abbc49a57..fd05a53f0 100644
--- a/src/mod_push_keepalive.erl
+++ b/src/mod_push_keepalive.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jul 2017 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -30,13 +30,14 @@
%% gen_mod callbacks.
-export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2]).
-
+-export([mod_doc/0]).
%% ejabberd_hooks callbacks.
-export([c2s_session_pending/1, c2s_session_resumed/1, c2s_copy_session/2,
c2s_handle_cast/2, c2s_handle_info/2, c2s_stanza/3]).
-include("logger.hrl").
-include("xmpp.hrl").
+-include("translate.hrl").
-define(PUSH_BEFORE_TIMEOUT_PERIOD, 120000). % 2 minutes.
@@ -90,6 +91,45 @@ mod_options(_Host) ->
{wake_on_start, false},
{wake_on_timeout, true}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module tries to keep the stream management "
+ "session (see 'mod_stream_mgmt') of a disconnected "
+ "mobile client alive if the client enabled push "
+ "notifications for that session. However, the normal "
+ "session resumption timeout is restored once a push "
+ "notification is issued, so the session will be closed "
+ "if the client doesn't respond to push notifications."), "",
+ ?T("The module depends on 'mod_push'.")],
+ opts =>
+ [{resume_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("This option specifies the period of time until "
+ "the session of a disconnected push client times out. "
+ "This timeout is only in effect as long as no push "
+ "notification is issued. Once that happened, the "
+ "resumption timeout configured for the 'mod_stream_mgmt' "
+ "module is restored. "
+ "The default value is '72' minutes.")}},
+ {wake_on_start,
+ #{value => "true | false",
+ desc =>
+ ?T("If this option is set to 'true', notifications "
+ "are generated for **all** registered push clients "
+ "during server startup. This option should not be "
+ "enabled on servers with many push clients as it "
+ "can generate significant load on the involved push "
+ "services and the server itself. "
+ "The default value is 'false'.")}},
+ {wake_on_timeout,
+ #{value => "true | false",
+ desc =>
+ ?T("If this option is set to 'true', a notification "
+ "is generated shortly before the session would time "
+ "out as per the 'resume_timeout' option. "
+ "The default value is 'true'.")}}]}.
+
%%--------------------------------------------------------------------
%% Register/unregister hooks.
%%--------------------------------------------------------------------
diff --git a/src/mod_push_mnesia.erl b/src/mod_push_mnesia.erl
index 1459446ba..f08503c1f 100644
--- a/src/mod_push_mnesia.erl
+++ b/src/mod_push_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jul 2017 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl
index be3e58b63..819888f7e 100644
--- a/src/mod_push_sql.erl
+++ b/src/mod_push_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Oct 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_register.erl b/src/mod_register.erl
index 1f943e61a..ac0e667ea 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -35,7 +35,7 @@
c2s_unauthenticated_packet/2, try_register/4,
process_iq/1, send_registration_notifications/3,
mod_opt_type/1, mod_options/1, depends/2,
- format_error/1]).
+ format_error/1, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -223,7 +223,7 @@ process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ,
"with this server")),
URL = mod_register_opt:redirect_url(Server),
if (URL /= undefined) and not IsRegistered ->
- Txt = translate:translate(Lang, ?T("To register, visit ~ts")),
+ Txt = translate:translate(Lang, ?T("To register, visit ~s")),
Desc = str:format(Txt, [URL]),
xmpp:make_iq_result(
IQ, #register{instructions = Desc,
@@ -419,8 +419,8 @@ send_registration_notifications(Mod, UJID, Source) ->
[] -> ok;
JIDs when is_list(JIDs) ->
Body =
- (str:format("[~ts] The account ~ts was registered from "
- "IP address ~ts on node ~w using ~p.",
+ (str:format("[~s] The account ~s was registered from "
+ "IP address ~s on node ~w using ~p.",
[get_time_string(),
jid:encode(UJID),
ejabberd_config:may_hide_data(
@@ -620,3 +620,68 @@ mod_options(_Host) ->
{registration_watchers, []},
{redirect_url, undefined},
{welcome_message, {<<>>, <<>>}}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module adds support for https://xmpp.org/extensions/xep-0077.html"
+ "[XEP-0077: In-Band Registration]. "
+ "This protocol enables end users to use a XMPP client to:"), "",
+ ?T("* Register a new account on the server."), "",
+ ?T("* Change the password from an existing account on the server."), "",
+ ?T("* Delete an existing account on the server.")],
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Specify rules to restrict what usernames can be registered and "
+ "unregistered. If a rule returns 'deny' on the requested username, "
+ "registration and unregistration of that user name is denied. "
+ "There are no restrictions by default.")}},
+ {access_from,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("By default, 'ejabberd' doesn't allow to register new accounts "
+ "from s2s or existing c2s sessions. You can change it by defining "
+ "access rule in this option. Use with care: allowing registration "
+ "from s2s leads to uncontrolled massive accounts creation by rogue users.")}},
+ {access_remove,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Specify rules to restrict access for user unregistration. "
+ "By default any user is able to unregister their account.")}},
+ {captcha_protected,
+ #{value => "true | false",
+ desc =>
+ ?T("Protect registrations with CAPTCHA (see section "
+ "https://docs.ejabberd.im/admin/configuration/#captcha[CAPTCHA] "
+ "of the Configuration Guide). The default is 'false'.")}},
+ {ip_access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("Define rules to allow or deny account registration depending "
+ "on the IP address of the XMPP client. The 'AccessName' should "
+ "be of type 'ip'. The default value is 'all'.")}},
+ {password_strength,
+ #{value => "Entropy",
+ desc =>
+ ?T("This option sets the minimum "
+ "https://en.wikipedia.org/wiki/Entropy_(information_theory)"
+ "[Shannon entropy] for passwords. The value 'Entropy' is a "
+ "number of bits of entropy. The recommended minimum is 32 bits. "
+ "The default is 0, i.e. no checks are performed.")}},
+ {registration_watchers,
+ #{value => "[JID, ...]",
+ desc =>
+ ?T("This option defines a list of JIDs which will be notified each "
+ "time a new account is registered.")}},
+ {redirect_url,
+ #{value => ?T("URL"),
+ desc =>
+ ?T("This option enables registration redirection as described in "
+ "https://xmpp.org/extensions/xep-0077.html#redirect"
+ "[XEP-0077: In-Band Registration: Redirection].")}},
+ {welcome_message,
+ #{value => "{subject: Subject, body: Body}",
+ desc =>
+ ?T("Set a welcome message that is sent to each newly registered account. "
+ "The message will have subject 'Subject' and text 'Body'.")}}]}.
diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl
index 3ff172f13..a2249430c 100644
--- a/src/mod_register_web.erl
+++ b/src/mod_register_web.erl
@@ -5,7 +5,7 @@
%%% Created : 4 May 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -56,6 +56,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process/2, mod_options/1, depends/2]).
+-export([mod_doc/0]).
-include("logger.hrl").
@@ -92,31 +93,20 @@ process([], #request{method = 'GET', lang = Lang}) ->
process([<<"register.css">>],
#request{method = 'GET'}) ->
serve_css();
-process([<<"new">>],
+process([Section],
#request{method = 'GET', lang = Lang, host = Host,
- ip = IP}) ->
- case ejabberd_router:is_my_host(Host) of
- true ->
- {Addr, _Port} = IP,
- form_new_get(Host, Lang, Addr);
- false ->
- {400, [], <<"Host not served">>}
- end;
-process([<<"delete">>],
- #request{method = 'GET', lang = Lang, host = Host}) ->
- case ejabberd_router:is_my_host(Host) of
+ ip = {Addr, _Port}}) ->
+ Host2 = case ejabberd_router:is_my_host(Host) of
true ->
- form_del_get(Host, Lang);
+ Host;
false ->
- {400, [], <<"Host not served">>}
- end;
-process([<<"change_password">>],
- #request{method = 'GET', lang = Lang, host = Host}) ->
- case ejabberd_router:is_my_host(Host) of
- true ->
- form_changepass_get(Host, Lang);
- false ->
- {400, [], <<"Host not served">>}
+ <<"">>
+ end,
+ case Section of
+ <<"new">> -> form_new_get(Host2, Lang, Addr);
+ <<"delete">> -> form_del_get(Host2, Lang);
+ <<"change_password">> -> form_changepass_get(Host2, Lang);
+ _ -> {404, [], "Not Found"}
end;
process([<<"new">>],
#request{method = 'POST', q = Q, ip = {Ip, _Port},
@@ -628,3 +618,12 @@ get_error_text({error, wrong_parameters}) ->
mod_options(_) ->
[].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module provides a web page where users can:"), "",
+ ?T("- Register a new account on the server."), "",
+ ?T("- Change the password from an existing account on the server."), "",
+ ?T("- Delete an existing account on the server."), "",
+ ?T("The module depends on 'mod_register' where all the configuration "
+ "is performed.")]}.
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index e2d98ec13..cccc7e3f4 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -48,7 +48,7 @@
out_subscription/1, set_items/3, remove_user/2,
get_jid_info/4, encode_item/1, webadmin_page/3,
webadmin_user/4, get_versioning_feature/2,
- roster_version/2,
+ roster_version/2, mod_doc/0,
mod_opt_type/1, mod_options/1, set_roster/1, del_roster/3,
process_rosteritems/5,
depends/2, set_item_and_notify_clients/3]).
@@ -1255,3 +1255,68 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements roster management as "
+ "defined in https://tools.ietf.org/html/rfc6121#section-2"
+ "[RFC6121 Section 2]. The module also adds support for "
+ "https://xmpp.org/extensions/xep-0237.html"
+ "[XEP-0237: Roster Versioning]."),
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("This option can be configured to specify "
+ "rules to restrict roster management. "
+ "If the rule returns 'deny' on the requested "
+ "user name, that user cannot modify their personal "
+ "roster, i.e. they cannot add/remove/modify contacts "
+ "or send presence subscriptions. "
+ "The default value is 'all', i.e. no restrictions.")}},
+ {versioning,
+ #{value => "true | false",
+ desc =>
+ ?T("Enables/disables Roster Versioning. "
+ "The default value is 'false'.")}},
+ {store_current_id,
+ #{value => "true | false",
+ desc =>
+ ?T("If this option is set to 'true', the current "
+ "roster version number is stored on the database. "
+ "If set to 'false', the roster version number is "
+ "calculated on the fly each time. Enabling this "
+ "option reduces the load for both ejabberd and the database. "
+ "This option does not affect the client in any way. "
+ "This option is only useful if option 'versioning' is "
+ "set to 'true'. The default value is 'false'. "
+ "IMPORTANT: if you use 'mod_shared_roster' or "
+ "'mod_shared_roster_ldap', you must set the value "
+ "of the option to 'false'.")}},
+ {db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}],
+ example =>
+ ["modules:",
+ " ...",
+ " mod_roster:",
+ " versioning: true",
+ " store_current_id: false",
+ " ..."]}.
diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl
index 00e76c30b..cb46f47f5 100644
--- a/src/mod_roster_mnesia.erl
+++ b/src/mod_roster_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl
index 6fbcaf80a..969bbc5be 100644
--- a/src/mod_roster_sql.erl
+++ b/src/mod_roster_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl
index dd941a3d2..bf241bf52 100644
--- a/src/mod_s2s_dialback.erl
+++ b/src/mod_s2s_dialback.erl
@@ -2,7 +2,7 @@
%%% Created : 16 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -26,6 +26,7 @@
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
+-export([mod_doc/0]).
%% Hooks
-export([s2s_out_auth_result/2, s2s_out_downgraded/2,
s2s_in_packet/2, s2s_out_packet/2, s2s_in_recv/3,
@@ -95,6 +96,40 @@ mod_opt_type(access) ->
mod_options(_Host) ->
[{access, all}].
+mod_doc() ->
+ #{desc =>
+ [?T("The module adds support for "
+ "https://xmpp.org/extensions/xep-0220.html"
+ "[XEP-0220: Server Dialback] to provide server identity "
+ "verification based on DNS."), "",
+ ?T("WARNING: DNS-based verification is vulnerable to "
+ "https://en.wikipedia.org/wiki/DNS_spoofing"
+ "[DNS cache poisoning], so modern servers rely on "
+ "verification based on PKIX certificates. Thus this module "
+ "is only recommended for backward compatibility "
+ "with servers running outdated software or non-TLS servers, "
+ "or those with invalid certificates (as long as you accept "
+ "the risks, e.g. you assume that the remote server has "
+ "an invalid certificate due to poor administration and "
+ "not because it's compromised).")],
+ opts =>
+ [{access,
+ #{value => ?T("AccessName"),
+ desc =>
+ ?T("An access rule that can be used to restrict "
+ "dialback for some servers. The default value "
+ "is 'all'.")}}],
+ example =>
+ ["modules:",
+ " ...",
+ " mod_s2s_dialback:",
+ " access:",
+ " allow:",
+ " server: legacy.domain.tld",
+ " server: invalid-cert.example.org",
+ " deny: all",
+ " ..."]}.
+
s2s_in_features(Acc, _) ->
[#db_feature{errors = true}|Acc].
@@ -242,7 +277,7 @@ s2s_out_packet(#{server := LServer, remote_server := RServer} = State,
State2 = ejabberd_s2s_out:handle_auth_success(<<"dialback">>, State1),
ejabberd_s2s_out:establish(State2);
_ ->
- Reason = str:format("Peer responded with error: ~ts",
+ Reason = str:format("Peer responded with error: ~s",
[format_error(Result)]),
ejabberd_s2s_out:handle_auth_failure(
<<"dialback">>, {auth, Reason}, State1)
@@ -302,7 +337,7 @@ send_db_result(State, #db_verify{from = From, to = To,
From, <<"dialback">>, undefined, State1),
ejabberd_s2s_in:establish(State2);
_ ->
- Reason = str:format("Verification failed: ~ts",
+ Reason = str:format("Verification failed: ~s",
[format_error(Response)]),
ejabberd_s2s_in:handle_auth_failure(
From, <<"dialback">>, Reason, State1)
diff --git a/src/mod_service_log.erl b/src/mod_service_log.erl
index eca95cb27..5f9361877 100644
--- a/src/mod_service_log.erl
+++ b/src/mod_service_log.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Aug 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -30,10 +30,10 @@
-behaviour(gen_mod).
-export([start/2, stop/1, log_user_send/1, mod_options/1,
- log_user_receive/1, mod_opt_type/1, depends/2]).
+ log_user_receive/1, mod_opt_type/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-
+-include("translate.hrl").
-include("xmpp.hrl").
start(Host, _Opts) ->
@@ -82,3 +82,26 @@ mod_opt_type(loggers) ->
mod_options(_) ->
[{loggers, []}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module forwards copies of all stanzas "
+ "to remote XMPP servers or components. "
+ "Every stanza is encapsulated into <forwarded/> "
+ "element as described in "
+ "https://xmpp.org/extensions/xep-0297.html"
+ "[XEP-0297: Stanza Forwarding]."),
+ opts =>
+ [{loggers,
+ #{value => "[Domain, ...]",
+ desc =>
+ ?T("A list of servers or connected components "
+ "to which stanzas will be forwarded.")}}],
+ example =>
+ ["modules:",
+ " ...",
+ " mod_service_log:",
+ " loggers:",
+ " - xmpp-server.tld",
+ " - component.domain.tld",
+ " ..."]}.
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index 0ce97d1ca..676ca4570 100644
--- a/src/mod_shared_roster.erl
+++ b/src/mod_shared_roster.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -39,7 +39,7 @@
delete_group/2, get_group_opts/2, set_group_opts/3,
get_group_users/2, get_group_explicit_users/2,
is_user_in_group/3, add_user_to_group/3, opts_to_binary/1,
- remove_user_from_group/3, mod_opt_type/1, mod_options/1, depends/2]).
+ remove_user_from_group/3, mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]).
-include("logger.hrl").
@@ -1031,3 +1031,83 @@ mod_opt_type(db_type) ->
mod_options(Host) ->
[{db_type, ejabberd_config:default_db(Host, ?MODULE)}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module enables you to create shared roster groups: "
+ "groups of accounts that can see members from (other) groups "
+ "in their rosters."), "",
+ ?T("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."), "",
+ ?T("Shared roster groups can be edited only via the Web Admin. "
+ "Each group has unique identification and those parameters:"), "",
+ ?T("- Name: The group's name will be displayed in the roster."), "",
+ ?T("- Description: of the group, which has no effect."), "",
+ ?T("- Members: A list of JIDs of group members, entered one per "
+ "line in the Web Admin. The special member directive '@all@' "
+ "represents all the registered users in the virtual host; "
+ "which is only recommended for a small server with just a few "
+ "hundred users. The special member directive '@online@' "
+ "represents the online users in the virtual host. With those "
+ "two directives, the actual list of members in those shared "
+ "rosters is generated dynamically at retrieval time."), "",
+ ?T("- Displayed groups: A list of groups that will be in the "
+ "rosters of this group's members. A group of other vhost can "
+ "be identified with 'groupid@vhost'."), "",
+ ?T("This module depends on 'mod_roster'. "
+ "If not enabled, roster queries will return 503 errors.")],
+ opts =>
+ [{db_type,
+ #{value => "mnesia | sql",
+ desc =>
+ ?T("Define the type of storage where the module will create "
+ "the tables and store user information. The default is "
+ "the storage defined by the global option 'default_db', "
+ "or 'mnesia' if omitted. If 'sql' value is defined, "
+ "make sure you have defined the database.")}}],
+ example =>
+ [{?T("Take the case of a computer club that wants all its members "
+ "seeing each other in their rosters. To achieve this, they "
+ "need to create a shared roster group similar to this one:"),
+ ["Identification: club_members",
+ "Name: Club Members",
+ "Description: Members from the computer club",
+ "Members: member1@example.org, member2@example.org, member3@example.org",
+ "Displayed Groups: club_members"]},
+ {?T("In another case we have a company which has three divisions: "
+ "Management, Marketing and Sales. All group members should see "
+ "all other members in their rosters. Additionally, all managers "
+ "should have all marketing and sales people in their roster. "
+ "Simultaneously, all marketeers and the whole sales team "
+ "should see all managers. This scenario can be achieved by "
+ "creating shared roster groups as shown in the following lists:"),
+ ["First list:",
+ "Identification: management",
+ "Name: Management",
+ "Description: Management",
+ "Members: manager1@example.org, manager2@example.org",
+ "Displayed Groups: management, marketing, sales",
+ "",
+ "Second list:",
+ "Identification: marketing",
+ "Name: Marketing",
+ "Description: Marketing",
+ "Members: marketeer1@example.org, marketeer2@example.org, marketeer3@example.org",
+ "Displayed Groups: management, marketing",
+ "",
+ "Third list:",
+ "Identification: sales",
+ "Name: Sales",
+ "Description: Sales",
+ "Members: salesman1@example.org, salesman2@example.org, salesman3@example.org",
+ "Displayed Groups: management, sales"
+ ]}
+ ]}.
diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl
index 737d20f66..1f67fce83 100644
--- a/src/mod_shared_roster_ldap.erl
+++ b/src/mod_shared_roster_ldap.erl
@@ -7,7 +7,7 @@
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_shared_roster_mnesia.erl b/src/mod_shared_roster_mnesia.erl
index f7403d7b9..05faa7a63 100644
--- a/src/mod_shared_roster_mnesia.erl
+++ b/src/mod_shared_roster_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl
index a761f5e11..2ae90ba21 100644
--- a/src/mod_shared_roster_sql.erl
+++ b/src/mod_shared_roster_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_sic.erl b/src/mod_sic.erl
index f8105f2cd..fdeb0fde8 100644
--- a/src/mod_sic.erl
+++ b/src/mod_sic.erl
@@ -5,7 +5,7 @@
%%% Created : 6 Mar 2010 by Karim Gemayel <karim.gemayel@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process_local_iq/1,
- process_sm_iq/1, mod_options/1, depends/2]).
+ process_sm_iq/1, mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -96,3 +96,14 @@ get_ip({User, Server, Resource},
mod_options(_Host) ->
[].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module adds support for "
+ "https://xmpp.org/extensions/xep-0279.html"
+ "[XEP-0279: Server IP Check]. This protocol enables "
+ "a client to discover its external IP address."), "",
+ ?T("WARNING: The protocol extension is deferred and seems "
+ "like there are no clients supporting it, so using this "
+ "module is not recommended and, furthermore, the module "
+ "might be removed in the future.")]}.
diff --git a/src/mod_sip.erl b/src/mod_sip.erl
index 0e8394e37..173bef8bf 100644
--- a/src/mod_sip.erl
+++ b/src/mod_sip.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -27,9 +27,10 @@
-protocol({rfc, 3261}).
-include("logger.hrl").
+-include("translate.hrl").
-ifndef(SIP).
--export([start/2, stop/1, depends/2, mod_options/1]).
+-export([start/2, stop/1, depends/2, mod_options/1, mod_doc/0]).
start(_, _) ->
?CRITICAL_MSG("ejabberd is not compiled with SIP support", []),
{error, sip_not_compiled}.
@@ -39,6 +40,8 @@ depends(_, _) ->
[].
mod_options(_) ->
[].
+mod_doc() ->
+ #{desc => [?T("SIP support has not been enabled.")]}.
-else.
-behaviour(gen_mod).
-behaviour(esip).
@@ -49,7 +52,8 @@ mod_options(_) ->
-export([data_in/2, data_out/2, message_in/2,
message_out/2, request/2, request/3, response/2,
- locate/1, mod_opt_type/1, mod_options/1, depends/2]).
+ locate/1, mod_opt_type/1, mod_options/1, depends/2,
+ mod_doc/0]).
-include_lib("esip/include/esip.hrl").
@@ -336,20 +340,30 @@ mod_opt_type(routes) ->
econf:list(econf:sip_uri());
mod_opt_type(via) ->
econf:list(
- econf:and_then(
- econf:options(
- #{type => econf:enum([tcp, tls, udp]),
- host => econf:domain(),
- port => econf:port()},
- [{required, [type, host]}]),
- fun(Opts) ->
- Type = proplists:get_value(type, Opts),
- Host = proplists:get_value(host, Opts),
- Port = proplists:get_value(port, Opts),
- {Type, {Host, Port}}
- end)).
-
--spec mod_options(binary()) -> [{via, [{tcp | tls | udp, {binary(), 1..65535 | undefined}}]} |
+ fun(L) when is_list(L) ->
+ (econf:and_then(
+ econf:options(
+ #{type => econf:enum([tcp, tls, udp]),
+ host => econf:domain(),
+ port => econf:port()},
+ [{required, [type, host]}]),
+ fun(Opts) ->
+ Type = proplists:get_value(type, Opts),
+ Host = proplists:get_value(host, Opts),
+ Port = proplists:get_value(port, Opts),
+ {Type, {Host, Port}}
+ end))(L);
+ (U) ->
+ (econf:and_then(
+ econf:url([tls, tcp, udp]),
+ fun(URI) ->
+ {ok, {Type, _, Host, Port, _, _}} =
+ http_uri:parse(binary_to_list(URI)),
+ {Type, {unicode:characters_to_binary(Host), Port}}
+ end))(U)
+ end, [unique]).
+
+-spec mod_options(binary()) -> [{via, [{tcp | tls | udp, {binary(), 1..65535}}]} |
{atom(), term()}].
mod_options(Host) ->
Route = #uri{scheme = <<"sip">>,
@@ -362,4 +376,80 @@ mod_options(Host) ->
{routes, [Route]},
{via, []}].
+mod_doc() ->
+ #{desc =>
+ [?T("This module adds SIP proxy/registrar support "
+ "for the corresponding virtual host."), "",
+ ?T("NOTE: It is not enough to just load this module. "
+ "You should also configure listeners and DNS records "
+ "properly. See section "
+ "https://docs.ejabberd.im/admin/configuration/#sip[SIP] "
+ "of the Configuration Guide for details.")],
+ opts =>
+ [{always_record_route,
+ #{value => "true | false",
+ desc =>
+ ?T("Always insert \"Record-Route\" header into "
+ "SIP messages. This approach allows to bypass "
+ "NATs/firewalls a bit more easily. "
+ "The default value is 'true'.")}},
+ {flow_timeout_tcp,
+ #{value => "timeout()",
+ desc =>
+ ?T("The option sets a keep-alive timer for "
+ "https://tools.ietf.org/html/rfc5626[SIP outbound] "
+ "TCP connections. The default value is '2' minutes.")}},
+ {flow_timeout_udp,
+ #{value => "timeout()",
+ desc =>
+ ?T("The options sets a keep-alive timer for "
+ "https://tools.ietf.org/html/rfc5626[SIP outbound] "
+ "UDP connections. The default value is '29' seconds.")}},
+ {record_route,
+ #{value => ?T("URI"),
+ desc =>
+ ?T("When the option 'always_record_route' is set to "
+ "'true' or when https://tools.ietf.org/html/rfc5626"
+ "[SIP outbound] is utilized, ejabberd inserts "
+ "\"Record-Route\" header field with this 'URI' into "
+ "a SIP message. The default is a SIP URI constructed "
+ "from the virtual host on which the module is loaded.")}},
+ {routes,
+ #{value => "[URI, ...]",
+ desc =>
+ ?T("You can set a list of SIP URIs of routes pointing "
+ "to this SIP proxy server. The default is a list containing "
+ "a single SIP URI constructed from the virtual host "
+ "on which the module is loaded.")}},
+ {via,
+ #{value => "[URI, ...]",
+ desc =>
+ ?T("A list to construct \"Via\" headers for "
+ "inserting them into outgoing SIP messages. "
+ "This is useful if you're running your SIP proxy "
+ "in a non-standard network topology. Every 'URI' "
+ "element in the list must be in the form of "
+ "\"scheme://host:port\", where \"transport\" "
+ "must be 'tls', 'tcp', or 'udp', \"host\" must "
+ "be a domain name or an IP address and \"port\" "
+ "must be an internet port number. Note that all "
+ "parts of the 'URI' are mandatory (e.g. you "
+ "cannot omit \"port\" or \"scheme\").")}}],
+ example =>
+ ["modules:",
+ " ...",
+ " mod_sip:",
+ " always_record_route: false",
+ " record_route: \"sip:example.com;lr\"",
+ " routes:",
+ " - \"sip:example.com;lr\"",
+ " - \"sip:sip.example.com;lr\"",
+ " flow_timeout_udp: 30 sec",
+ " flow_timeout_tcp: 1 min",
+ " via:",
+ " - tls://sip-tls.example.com:5061",
+ " - tcp://sip-tcp.example.com:5060",
+ " - udp://sip-udp.example.com:5060",
+ " ..."]}.
+
-endif.
diff --git a/src/mod_sip_proxy.erl b/src/mod_sip_proxy.erl
index ba1b90abf..c7bc10669 100644
--- a/src/mod_sip_proxy.erl
+++ b/src/mod_sip_proxy.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_sip_registrar.erl b/src/mod_sip_registrar.erl
index 55bacaed0..573bdba77 100644
--- a/src/mod_sip_registrar.erl
+++ b/src/mod_sip_registrar.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_stats.erl b/src/mod_stats.erl
index 34d1d4c10..abd83f397 100644
--- a/src/mod_stats.erl
+++ b/src/mod_stats.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process_iq/1,
- mod_options/1, depends/2]).
+ mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -235,3 +235,23 @@ search_running_node(SNode, [Node | Nodes]) ->
mod_options(_Host) ->
[].
+
+mod_doc() ->
+ #{desc =>
+ [?T("This module adds support for "
+ "https://xmpp.org/extensions/xep-0039.html"
+ "[XEP-0039: Statistics Gathering]. This protocol "
+ "allows you to retrieve the following statistics "
+ "from your ejabberd server:"), "",
+ ?T("- Total number of registered users on the current "
+ "virtual host (users/total)."), "",
+ ?T("- Total number of registered users on all virtual "
+ "hosts (users/all-hosts/total)."), "",
+ ?T("- Total number of online users on the current "
+ "virtual host (users/online)."), "",
+ ?T("- Total number of online users on all virtual "
+ "hosts (users/all-hosts/online)."), "",
+ ?T("NOTE: The protocol extension is deferred and seems "
+ "like even a few clients that were supporting it "
+ "are now abandoned. So using this module makes "
+ "very little sense.")]}.
diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl
index 92b9e4020..4c9ff3c8b 100644
--- a/src/mod_stream_mgmt.erl
+++ b/src/mod_stream_mgmt.erl
@@ -3,7 +3,7 @@
%%% Created : 25 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -27,6 +27,7 @@
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
+-export([mod_doc/0]).
%% hooks
-export([c2s_stream_started/2, c2s_stream_features/2,
c2s_authenticated_packet/2, c2s_unauthenticated_packet/2,
@@ -493,11 +494,10 @@ update_num_stanzas_in(State, _El) ->
send_rack(#{mgmt_ack_timer := _} = State) ->
State;
send_rack(#{mgmt_xmlns := Xmlns,
- mgmt_stanzas_out := NumStanzasOut,
- mgmt_ack_timeout := AckTimeout} = State) ->
- TRef = erlang:start_timer(AckTimeout, self(), ack_timeout),
- State1 = State#{mgmt_ack_timer => TRef, mgmt_stanzas_req => NumStanzasOut},
- send(State1, #sm_r{xmlns = Xmlns}).
+ mgmt_stanzas_out := NumStanzasOut} = State) ->
+ State1 = State#{mgmt_stanzas_req => NumStanzasOut},
+ State2 = start_ack_timer(State1),
+ send(State2, #sm_r{xmlns = Xmlns}).
-spec resend_rack(state()) -> state().
resend_rack(#{mgmt_ack_timer := _,
@@ -712,6 +712,13 @@ restart_pending_timer(#{mgmt_pending_timer := TRef} = State, NewTimeout) ->
restart_pending_timer(State, _NewTimeout) ->
State.
+-spec start_ack_timer(state()) -> state().
+start_ack_timer(#{mgmt_ack_timeout := infinity} = State) ->
+ State;
+start_ack_timer(#{mgmt_ack_timeout := AckTimeout} = State) ->
+ TRef = erlang:start_timer(AckTimeout, self(), ack_timeout),
+ State#{mgmt_ack_timer => TRef}.
+
-spec cancel_ack_timer(state()) -> state().
cancel_ack_timer(#{mgmt_ack_timer := TRef} = State) ->
misc:cancel_timer(TRef),
@@ -849,3 +856,82 @@ mod_options(Host) ->
{cache_life_time, timer:hours(48)},
{resend_on_timeout, false},
{queue_type, ejabberd_option:queue_type(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module adds support for "
+ "https://xmpp.org/extensions/xep-0198.html"
+ "[XEP-0198: Stream Management]. This protocol allows "
+ "active management of an XML stream between two XMPP "
+ "entities, including features for stanza acknowledgements "
+ "and stream resumption."),
+ opts =>
+ [{max_ack_queue,
+ #{value => ?T("Size"),
+ desc =>
+ ?T("This option specifies the maximum number of "
+ "unacknowledged stanzas queued for possible "
+ "retransmission. When the limit is exceeded, "
+ "the client session is terminated. The allowed "
+ "values are positive integers and 'infinity'. "
+ "You should be careful when setting this value "
+ "as it should not be set too low, otherwise, "
+ "you could kill sessions in a loop, before they "
+ "get the chance to finish proper session initiation. "
+ "It should definitely be set higher that the size "
+ "of the offline queue (for example at least 3 times "
+ "the value of the max offline queue and never lower "
+ "than '1000'). The default value is '5000'.")}},
+ {resume_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("This option configures the (default) period of time "
+ "until a session times out if the connection is lost. "
+ "During this period of time, a client may resume its "
+ "session. Note that the client may request a different "
+ "timeout value, see the 'max_resume_timeout' option. "
+ "Setting it to '0' effectively disables session resumption. "
+ "The default value is '5' minutes.")}},
+ {max_resume_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("A client may specify the period of time until a session "
+ "times out if the connection is lost. During this period "
+ "of time, the client may resume its session. This option "
+ "limits the period of time a client is permitted to request. "
+ "It must be set to a timeout equal to or larger than the "
+ "default 'resume_timeout'. By default, it is set to the "
+ "same value as the 'resume_timeout' option.")}},
+ {ack_timeout,
+ #{value => "timeout()",
+ desc =>
+ ?T("A time to wait for stanza acknowledgements. "
+ "Setting it to 'infinity' effectively disables the timeout. "
+ "The default value is '1' minute.")}},
+ {resend_on_timeout,
+ #{value => "true | false | if_offline",
+ desc =>
+ ?T("If this option is set to 'true', any message stanzas "
+ "that weren't acknowledged by the client will be resent "
+ "on session timeout. This behavior might often be desired, "
+ "but could have unexpected results under certain circumstances. "
+ "For example, a message that was sent to two resources might "
+ "get resent to one of them if the other one timed out. "
+ "Therefore, the default value for this option is 'false', "
+ "which tells ejabberd to generate an error message instead. "
+ "As an alternative, the option may be set to 'if_offline'. "
+ "In this case, unacknowledged messages are resent only if "
+ "no other resource is online when the session times out. "
+ "Otherwise, error messages are generated.")}},
+ {queue_type,
+ #{value => "ram | file",
+ desc =>
+ ?T("Same as top-level 'queue_type' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
diff --git a/src/mod_time.erl b/src/mod_time.erl
index 53f2e5432..0d978444c 100644
--- a/src/mod_time.erl
+++ b/src/mod_time.erl
@@ -6,7 +6,7 @@
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -33,7 +33,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process_local_iq/1,
- mod_options/1, depends/2]).
+ mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -69,3 +69,10 @@ depends(_Host, _Opts) ->
mod_options(_Host) ->
[].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module adds support for "
+ "https://xmpp.org/extensions/xep-0202.html"
+ "[XEP-0202: Entity Time]. In other words, "
+ "the module reports server's system time.")}.
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index 9adf8f71a..9cad98475 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -5,7 +5,7 @@
%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -33,7 +33,7 @@
-behaviour(gen_server).
-behaviour(gen_mod).
--export([start/2, stop/1, get_sm_features/5, mod_options/1,
+-export([start/2, stop/1, get_sm_features/5, mod_options/1, mod_doc/0,
process_local_iq/1, process_sm_iq/1, string2lower/1,
remove_user/2, export/1, import_info/0, import/5, import_start/2,
depends/2, process_search/1, process_vcard/1, get_vcard/2,
@@ -586,3 +586,94 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module allows end users to store and retrieve "
+ "their vCard, and to retrieve other users vCards, "
+ "as defined in https://xmpp.org/extensions/xep-0054.html"
+ "[XEP-0054: vcard-temp]. The module also implements an "
+ "uncomplicated Jabber User Directory based on the vCards "
+ "of these users. Moreover, it enables the server to send "
+ "its vCard when queried."),
+ opts =>
+ [{allow_return_all,
+ #{value => "true | false",
+ desc =>
+ ?T("This option enables you to specify if search "
+ "operations with empty input fields should return "
+ "all users who added some information to their vCard. "
+ "The default value is 'false'.")}},
+ {host,
+ #{desc => ?T("Deprecated. Use 'hosts' instead.")}},
+ {hosts,
+ #{value => ?T("[Host, ...]"),
+ desc =>
+ ?T("This option defines the Jabber IDs of the service. "
+ "If the 'hosts' option is not specified, the only Jabber ID will "
+ "be the hostname of the virtual host with the prefix \"vjud.\". "
+ "The keyword '@HOST@' is replaced with the real virtual host name.")}},
+ {name,
+ #{value => ?T("Name"),
+ desc =>
+ ?T("The value of the service name. This name is only visible in some "
+ "clients that support https://xmpp.org/extensions/xep-0030.html"
+ "[XEP-0030: Service Discovery]. The default is 'vCard User Search'.")}},
+ {matches,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("With this option, the number of reported search results "
+ "can be limited. If the option's value is set to 'infinity', "
+ "all search results are reported. The default value is '30'.")}},
+ {search,
+ #{value => "true | false",
+ desc =>
+ ?T("This option specifies whether the search functionality "
+ "is enabled or not. If disabled, the options 'hosts', 'name' "
+ "and 'vcard' will be ignored and the Jabber User Directory "
+ "service will not appear in the Service Discovery item list. "
+ "The default value is 'false'.")}},
+ {db_type,
+ #{value => "mnesia | sql | ldap",
+ desc =>
+ ?T("Same as top-level 'default_db' option, but applied to this module only.")}},
+ {use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}},
+ {vcard,
+ #{value => ?T("vCard"),
+ desc =>
+ ?T("A custom vCard of the server that will be displayed "
+ "by some XMPP clients in Service Discovery. The value of "
+ "'vCard' is a YAML map constructed from an XML representation "
+ "of vCard. Since the representation has no attributes, "
+ "the mapping is straightforward."),
+ example =>
+ [{?T("For example, the following XML representation of vCard:"),
+ ["<vCard xmlns='vcard-temp'>",
+ " <FN>Conferences</FN>",
+ " <ADR>",
+ " <WORK/>",
+ " <STREET>Elm Street</STREET>",
+ " </ADR>",
+ "</vCard>"]},
+ {?T("will be translated to:"),
+ ["vcard:",
+ " fn: Conferences",
+ " adr:",
+ " -",
+ " work: true",
+ " street: Elm Street"]}]}}]}.
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl
index c5a9fc617..e694545fb 100644
--- a/src/mod_vcard_ldap.erl
+++ b/src/mod_vcard_ldap.erl
@@ -4,7 +4,7 @@
%%% Created : 29 Jul 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -31,7 +31,7 @@
-export([start_link/2]).
-export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4,
remove_user/2, import/3, search_fields/1, search_reported/1,
- mod_opt_type/1, mod_options/1]).
+ mod_opt_type/1, mod_options/1, mod_doc/0]).
-export([is_search_supported/1]).
%% gen_server callbacks
@@ -475,3 +475,100 @@ mod_options(Host) ->
{ldap_tls_cacertfile, ejabberd_option:ldap_tls_cacertfile(Host)},
{ldap_tls_depth, ejabberd_option:ldap_tls_depth(Host)},
{ldap_tls_verify, ejabberd_option:ldap_tls_verify(Host)}].
+
+mod_doc() ->
+ #{opts =>
+ [{ldap_search_fields,
+ #{value => "{Name: Attribute, ...}",
+ desc =>
+ ?T("This option defines the search form and the LDAP "
+ "attributes to search within. 'Name' is the name of a "
+ "search form field which will be automatically "
+ "translated by using the translation files "
+ "(see 'msgs/*.msg' for available words). "
+ "'Attribute' is the LDAP attribute or the pattern '%u'."),
+ example =>
+ [{?T("The default is:"),
+ ["User: \"%u\"",
+ "\"Full Name\": displayName",
+ "\"Given Name\": givenName",
+ "\"Middle Name\": initials",
+ "\"Family Name\": sn",
+ "Nickname: \"%u\"",
+ "Birthday: birthDay",
+ "Country: c",
+ "City: l",
+ "Email: mail",
+ "\"Organization Name\": o",
+ "\"Organization Unit\": ou"]
+ }]}},
+ {ldap_search_reported,
+ #{value => "{SearchField: VcardField}, ...}",
+ desc =>
+ ?T("This option defines which search fields should be "
+ "reported. 'SearchField' is the name of a search form "
+ "field which will be automatically translated by using "
+ "the translation files (see 'msgs/*.msg' for available "
+ "words). 'VcardField' is the vCard field name defined "
+ "in the 'ldap_vcard_map' option."),
+ example =>
+ [{?T("The default is:"),
+ ["\"Full Name\": FN",
+ "\"Given Name\": FIRST",
+ "\"Middle Name\": MIDDLE",
+ "\"Family Name\": LAST",
+ "\"Nickname\": NICKNAME",
+ "\"Birthday\": BDAY",
+ "\"Country\": CTRY",
+ "\"City\": LOCALITY",
+ "\"Email\": EMAIL",
+ "\"Organization Name\": ORGNAME",
+ "\"Organization Unit\": ORGUNIT"]
+ }]}},
+ {ldap_vcard_map,
+ #{value => "{Name: {Pattern, LDAPattributes}, ...}",
+ desc =>
+ ?T("With this option you can set the table that maps LDAP "
+ "attributes to vCard fields. 'Name' is the type name of "
+ "the vCard as defined in "
+ "http://tools.ietf.org/html/rfc2426[RFC 2426]. "
+ "'Pattern' is a string which contains "
+ "pattern variables '%u', '%d' or '%s'. "
+ "'LDAPattributes' is the list containing LDAP attributes. "
+ "The pattern variables '%s' will be sequentially replaced "
+ "with the values of LDAP attributes from "
+ "'List_of_LDAP_attributes', '%u' will be replaced with "
+ "the user part of a JID, and '%d' will be replaced with "
+ "the domain part of a JID."),
+ example =>
+ [{?T("The default is:"),
+ ["NICKNAME: {\"%u\": []}",
+ "FN: {\"%s\": [displayName]}",
+ "LAST: {\"%s\": [sn]}",
+ "FIRST: {\"%s\": [givenName]}",
+ "MIDDLE: {\"%s\": [initials]}",
+ "ORGNAME: {\"%s\": [o]}",
+ "ORGUNIT: {\"%s\": [ou]}",
+ "CTRY: {\"%s\": [c]}",
+ "LOCALITY: {\"%s\": [l]}",
+ "STREET: {\"%s\": [street]}",
+ "REGION: {\"%s\": [st]}",
+ "PCODE: {\"%s\": [postalCode]}",
+ "TITLE: {\"%s\": [title]}",
+ "URL: {\"%s\": [labeleduri]}",
+ "DESC: {\"%s\": [description]}",
+ "TEL: {\"%s\": [telephoneNumber]}",
+ "EMAIL: {\"%s\": [mail]}",
+ "BDAY: {\"%s\": [birthDay]}",
+ "ROLE: {\"%s\": [employeeType]}",
+ "PHOTO: {\"%s\": [jpegPhoto]}"]
+ }]}}] ++
+ [{Opt,
+ #{desc =>
+ {?T("Same as top-level '~s' option, but "
+ "applied to this module only."), [Opt]}}}
+ || Opt <- [ldap_base, ldap_servers, ldap_uids,
+ ldap_deref_aliases, ldap_encrypt, ldap_password,
+ ldap_port, ldap_rootdn, ldap_filter,
+ ldap_tls_certfile, ldap_tls_cacertfile,
+ ldap_tls_depth, ldap_tls_verify, ldap_backups]]}.
diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl
index d4394b677..f9c53c44b 100644
--- a/src/mod_vcard_mnesia.erl
+++ b/src/mod_vcard_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -31,7 +31,7 @@
search_fields/1, search_reported/1, remove_user/2]).
-export([is_search_supported/1]).
-export([need_transform/1, transform/1]).
--export([mod_opt_type/1, mod_options/1]).
+-export([mod_opt_type/1, mod_options/1, mod_doc/0]).
-include("xmpp.hrl").
-include("mod_vcard.hrl").
@@ -274,3 +274,12 @@ mod_opt_type(search_all_hosts) ->
mod_options(_) ->
[{search_all_hosts, true}].
+
+mod_doc() ->
+ #{opts =>
+ [{search_all_hosts,
+ #{value => "true | false",
+ desc =>
+ ?T("Whether to perform search on all "
+ "virtual hosts or not. The default "
+ "value is 'true'.")}}]}.
diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl
index 5c195fbf8..1a8820227 100644
--- a/src/mod_vcard_sql.erl
+++ b/src/mod_vcard_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl
index 927ea9594..470593890 100644
--- a/src/mod_vcard_xupdate.erl
+++ b/src/mod_vcard_xupdate.erl
@@ -5,7 +5,7 @@
%%% Created : 9 Mar 2007 by Igor Goryachev <igor@goryachev.org>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -31,13 +31,14 @@
%% gen_mod callbacks
-export([start/2, stop/1, reload/3]).
--export([update_presence/1, vcard_set/1, remove_user/2,
+-export([update_presence/1, vcard_set/1, remove_user/2, mod_doc/0,
user_send_packet/1, mod_opt_type/1, mod_options/1, depends/2]).
%% API
-export([compute_hash/1]).
-include("logger.hrl").
-include("xmpp.hrl").
+-include("translate.hrl").
-define(VCARD_XUPDATE_CACHE, vcard_xupdate_cache).
@@ -202,3 +203,51 @@ mod_options(Host) ->
{cache_size, ejabberd_option:cache_size(Host)},
{cache_missed, ejabberd_option:cache_missed(Host)},
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
+
+mod_doc() ->
+ #{desc =>
+ [?T("The user's client can store an avatar in the "
+ "user vCard. The vCard-Based Avatars protocol "
+ "(https://xmpp.org/extensions/xep-0153.html[XEP-0153]) "
+ "provides a method for clients to inform the contacts "
+ "what is the avatar hash value. However, simple or small "
+ "clients may not implement that protocol."), "",
+ ?T("If this module is enabled, all the outgoing client presence "
+ "stanzas get automatically the avatar hash on behalf of the "
+ "client. So, the contacts receive the presence stanzas with "
+ "the 'Update Data' described in "
+ "https://xmpp.org/extensions/xep-0153.html[XEP-0153] as if the "
+ "client would had inserted it itself. If the client had already "
+ "included such element in the presence stanza, it is replaced "
+ "with the element generated by ejabberd."), "",
+ ?T("By enabling this module, each vCard modification produces "
+ "a hash recalculation, and each presence sent by a client "
+ "produces hash retrieval and a presence stanza rewrite. "
+ "For this reason, enabling this module will introduce a "
+ "computational overhead in servers with clients that change "
+ "frequently their presence. However, the overhead is significantly "
+ "reduced by the use of caching, so you probably don't want "
+ "to set 'use_cache' to 'false'."), "",
+ ?T("The module depends on 'mod_vcard'."), "",
+ ?T("NOTE: Nowadays https://xmpp.org/extensions/xep-0153.html"
+ "[XEP-0153] is used mostly as \"read-only\", i.e. modern "
+ "clients don't publish their avatars inside vCards. Thus "
+ "in the majority of cases the module is only used along "
+ "with 'mod_avatar' module for providing backward compatibility.")],
+ opts =>
+ [{use_cache,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
+ {cache_size,
+ #{value => "pos_integer() | infinity",
+ desc =>
+ ?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
+ {cache_missed,
+ #{value => "true | false",
+ desc =>
+ ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
+ {cache_life_time,
+ #{value => "timeout()",
+ desc =>
+ ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
diff --git a/src/mod_version.erl b/src/mod_version.erl
index 6def24951..52dce0e13 100644
--- a/src/mod_version.erl
+++ b/src/mod_version.erl
@@ -5,7 +5,7 @@
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,7 +32,7 @@
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process_local_iq/1,
- mod_opt_type/1, mod_options/1, depends/2]).
+ mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -81,3 +81,16 @@ mod_opt_type(show_os) ->
mod_options(_Host) ->
[{show_os, true}].
+
+mod_doc() ->
+ #{desc =>
+ ?T("This module implements "
+ "https://xmpp.org/extensions/xep-0092.html"
+ "[XEP-0092: Software Version]. Consequently, "
+ "it answers ejabberd's version when queried."),
+ opts =>
+ [{show_os,
+ #{value => "true | false",
+ desc =>
+ ?T("Should the operating system be revealed or not. "
+ "The default value is 'true'.")}}]}.
diff --git a/src/mqtt_codec.erl b/src/mqtt_codec.erl
index cb77cd934..3156d19a0 100644
--- a/src/mqtt_codec.erl
+++ b/src/mqtt_codec.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2019 ProcessOne, SARL. All Rights Reserved.
+%%% @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
diff --git a/src/node_flat.erl b/src/node_flat.erl
index f0deeb7e2..7b2c93de0 100644
--- a/src/node_flat.erl
+++ b/src/node_flat.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl
index 76b1c8ccc..38feed821 100644
--- a/src/node_flat_sql.erl
+++ b/src/node_flat_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -393,7 +393,7 @@ get_entity_subscriptions(Host, Owner) ->
?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s "
"from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and "
- "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host=%(H)s");
+ "(jid=%(GJ)s or jid like %(GJLike)s %ESCAPE) and host=%(H)s");
_ ->
SJ = encode_jid(SubKey),
?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s "
@@ -436,7 +436,7 @@ get_entity_subscriptions_for_send_last(Host, Owner) ->
"from pubsub_state i, pubsub_node n, pubsub_node_option o "
"where i.nodeid = n.nodeid and n.nodeid = o.nodeid and "
"name='send_last_published_item' and val='on_sub_and_presence' and "
- "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host=%(H)s");
+ "(jid=%(GJ)s or jid like %(GJLike)s %ESCAPE) and host=%(H)s");
_ ->
SJ = encode_jid(SubKey),
?SQL("select @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s "
@@ -869,7 +869,7 @@ itemids(Nidx, {_U, _S, _R} = JID) ->
ejabberd_sql:sql_query_t(
?SQL("select @(itemid)s from pubsub_item where "
"nodeid=%(Nidx)d and (publisher=%(SJID)s"
- " or publisher like %(SJIDLike)s escape '^') "
+ " or publisher like %(SJIDLike)s %ESCAPE) "
"order by modification desc"))
of
{selected, RItems} ->
@@ -894,10 +894,23 @@ select_affiliation_subscriptions(Nidx, JID) ->
select_affiliation_subscriptions(Nidx, JID, JID) ->
select_affiliation_subscriptions(Nidx, JID);
select_affiliation_subscriptions(Nidx, GenKey, SubKey) ->
- {result, Affiliation} = get_affiliation(Nidx, GenKey),
- {result, BareJidSubs} = get_subscriptions(Nidx, GenKey),
- {result, FullJidSubs} = get_subscriptions(Nidx, SubKey),
- {Affiliation, BareJidSubs++FullJidSubs}.
+ GJ = encode_jid(GenKey),
+ SJ = encode_jid(SubKey),
+ case catch
+ ejabberd_sql:sql_query_t(
+ ?SQL("select jid = %(GJ)s as @(G)d, @(affiliation)s, @(subscriptions)s from "
+ " pubsub_state where nodeid=%(Nidx)d and jid in (%(GJ)s, %(SJ)s)"))
+ of
+ {selected, Res} ->
+ lists:foldr(
+ fun({1, A, S}, {_, Subs}) ->
+ {decode_affiliation(A), Subs ++ decode_subscriptions(S)};
+ ({_, _, S}, {Aff, Subs}) ->
+ {Aff, Subs ++ decode_subscriptions(S)}
+ end, {none, []}, Res);
+ _ ->
+ {none, []}
+ end.
update_affiliation(Nidx, JID, Affiliation) ->
J = encode_jid(JID),
@@ -955,16 +968,16 @@ encode_jid(JID) ->
-spec encode_jid_like(JID :: ljid()) -> binary().
encode_jid_like(JID) ->
- ejabberd_sql:escape_like_arg_circumflex(jid:encode(JID)).
+ ejabberd_sql:escape_like_arg(jid:encode(JID)).
-spec encode_host(Host :: host()) -> binary().
encode_host({_U, _S, _R} = LJID) -> encode_jid(LJID);
encode_host(Host) -> Host.
-spec encode_host_like(Host :: host()) -> binary().
-encode_host_like({_U, _S, _R} = LJID) -> ejabberd_sql:escape(encode_jid_like(LJID));
+encode_host_like({_U, _S, _R} = LJID) -> encode_jid_like(LJID);
encode_host_like(Host) ->
- ejabberd_sql:escape(ejabberd_sql:escape_like_arg_circumflex(Host)).
+ ejabberd_sql:escape_like_arg(Host).
-spec encode_affiliation(Arg :: atom()) -> binary().
encode_affiliation(owner) -> <<"o">>;
diff --git a/src/node_pep.erl b/src/node_pep.erl
index 334681578..861a3b9a2 100644
--- a/src/node_pep.erl
+++ b/src/node_pep.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl
index 6c1e642b2..3d1458084 100644
--- a/src/node_pep_sql.erl
+++ b/src/node_pep_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -125,13 +125,13 @@ get_entity_subscriptions(_Host, Owner) ->
?SQL("select @(host)s, @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s "
"from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and "
- "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host like %(HLike)s escape '^'");
+ "(jid=%(GJ)s or jid like %(GJLike)s %ESCAPE) and host like %(HLike)s %ESCAPE");
_ ->
SJ = node_flat_sql:encode_jid(SubKey),
?SQL("select @(host)s, @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s "
"from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and "
- "jid in (%(SJ)s,%(GJ)s) and host like %(HLike)s escape '^'")
+ "jid in (%(SJ)s,%(GJ)s) and host like %(HLike)s %ESCAPE")
end,
{result,
case ejabberd_sql:sql_query_t(Query) of
@@ -162,14 +162,14 @@ get_entity_subscriptions_for_send_last(_Host, Owner) ->
"from pubsub_state i, pubsub_node n, pubsub_node_option o "
"where i.nodeid = n.nodeid and n.nodeid = o.nodeid and "
"name='send_last_published_item' and val='on_sub_and_presence' and "
- "(jid=%(GJ)s or jid like %(GJLike)s escape '^') and host like %(HLike)s escape '^'");
+ "(jid=%(GJ)s or jid like %(GJLike)s %ESCAPE) and host like %(HLike)s %ESCAPE");
_ ->
SJ = node_flat_sql:encode_jid(SubKey),
?SQL("select @(host)s, @(node)s, @(plugin)s, @(i.nodeid)d, @(jid)s, @(subscriptions)s "
"from pubsub_state i, pubsub_node n, pubsub_node_option o "
"where i.nodeid = n.nodeid and n.nodeid = o.nodeid and "
"name='send_last_published_item' and val='on_sub_and_presence' and "
- "jid in (%(SJ)s,%(GJ)s) and host like %(HLike)s escape '^'")
+ "jid in (%(SJ)s,%(GJ)s) and host like %(HLike)s %ESCAPE")
end,
{result,
case ejabberd_sql:sql_query_t(Query) of
diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl
index c94ba197b..43453b1cd 100644
--- a/src/nodetree_tree.erl
+++ b/src/nodetree_tree.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl
index bb9a27a61..a9e022091 100644
--- a/src/nodetree_tree_sql.erl
+++ b/src/nodetree_tree_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -220,12 +220,12 @@ get_subnodes_tree(Host, Node) ->
Rec ->
Type = Rec#pubsub_node.type,
H = node_flat_sql:encode_host(Host),
- N = <<(ejabberd_sql:escape_like_arg_circumflex(Node))/binary, "/%">>,
+ N = <<(ejabberd_sql:escape_like_arg(Node))/binary, "/%">>,
Sub = case catch
ejabberd_sql:sql_query_t(
?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d from pubsub_node "
"where host=%(H)s and plugin=%(Type)s and"
- " (parent=%(Node)s or parent like %(N)s escape '^')"))
+ " (parent=%(Node)s or parent like %(N)s %ESCAPE)"))
of
{selected, RItems} ->
[raw_to_node(Host, Item) || Item <- RItems];
diff --git a/src/nodetree_virtual.erl b/src/nodetree_virtual.erl
index 627eca092..88d46df92 100644
--- a/src/nodetree_virtual.erl
+++ b/src/nodetree_virtual.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl
index 904ac7a45..8e3fc23b7 100644
--- a/src/prosody2ejabberd.erl
+++ b/src/prosody2ejabberd.erl
@@ -4,7 +4,7 @@
%%% Created : 20 Jan 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/proxy_protocol.erl b/src/proxy_protocol.erl
index 5c33b130d..926424923 100644
--- a/src/proxy_protocol.erl
+++ b/src/proxy_protocol.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Nov 2018 by Paweł Chmielowski <pawel@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl
index 558b696cb..da77901d0 100644
--- a/src/pubsub_db_sql.erl
+++ b/src/pubsub_db_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Aug 2009 by Pablo Polvorin <pablo.polvorin@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_index.erl b/src/pubsub_index.erl
index de71b6395..be2046079 100644
--- a/src/pubsub_index.erl
+++ b/src/pubsub_index.erl
@@ -5,7 +5,7 @@
%%% Created : 30 Apr 2009 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl
index a0841dbf0..f349315d5 100644
--- a/src/pubsub_migrate.erl
+++ b/src/pubsub_migrate.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Jul 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl
index ac5137d2c..b9662f7a3 100644
--- a/src/pubsub_subscription.erl
+++ b/src/pubsub_subscription.erl
@@ -5,7 +5,7 @@
%%% Created : 29 May 2009 by Brian Cully <bjc@kublai.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -206,13 +206,13 @@ val_xfield(digest_frequency = Opt, [Val]) ->
case catch binary_to_integer(Val) of
N when is_integer(N) -> N;
_ ->
- Txt = {?T("Value of '~ts' should be integer"), [Opt]},
+ Txt = {?T("Value of '~s' should be integer"), [Opt]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(expire = Opt, [Val]) ->
try xmpp_util:decode_timestamp(Val)
catch _:{bad_timestamp, _} ->
- Txt = {?T("Value of '~ts' should be datetime string"), [Opt]},
+ Txt = {?T("Value of '~s' should be datetime string"), [Opt]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
@@ -224,7 +224,7 @@ val_xfield(subscription_depth = Opt, [Depth]) ->
case catch binary_to_integer(Depth) of
N when is_integer(N) -> N;
_ ->
- Txt = {?T("Value of '~ts' should be integer"), [Opt]},
+ Txt = {?T("Value of '~s' should be integer"), [Opt]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end.
@@ -234,7 +234,7 @@ xopt_to_bool(_, <<"1">>) -> true;
xopt_to_bool(_, <<"false">>) -> false;
xopt_to_bool(_, <<"true">>) -> true;
xopt_to_bool(Option, _) ->
- Txt = {?T("Value of '~ts' should be boolean"), [Option]},
+ Txt = {?T("Value of '~s' should be boolean"), [Option]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}.
%% Return a field for an XForm for Key, with data filled in, if
diff --git a/src/pubsub_subscription_sql.erl b/src/pubsub_subscription_sql.erl
index 9419545df..310f4a87b 100644
--- a/src/pubsub_subscription_sql.erl
+++ b/src/pubsub_subscription_sql.erl
@@ -6,7 +6,7 @@
%%% Created : 7 Aug 2009 by Pablo Polvorin <pablo.polvorin@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -171,13 +171,13 @@ val_xfield(digest_frequency = Opt, [Val]) ->
case catch binary_to_integer(Val) of
N when is_integer(N) -> N;
_ ->
- Txt = {?T("Value of '~ts' should be integer"), [Opt]},
+ Txt = {?T("Value of '~s' should be integer"), [Opt]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(expire = Opt, [Val]) ->
try xmpp_util:decode_timestamp(Val)
catch _:{bad_timestamp, _} ->
- Txt = {?T("Value of '~ts' should be datetime string"), [Opt]},
+ Txt = {?T("Value of '~s' should be datetime string"), [Opt]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
@@ -189,7 +189,7 @@ val_xfield(subscription_depth = Opt, [Depth]) ->
case catch binary_to_integer(Depth) of
N when is_integer(N) -> N;
_ ->
- Txt = {?T("Value of '~ts' should be integer"), [Opt]},
+ Txt = {?T("Value of '~s' should be integer"), [Opt]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end.
@@ -199,7 +199,7 @@ xopt_to_bool(_, <<"1">>) -> true;
xopt_to_bool(_, <<"false">>) -> false;
xopt_to_bool(_, <<"true">>) -> true;
xopt_to_bool(Option, _) ->
- Txt = {?T("Value of '~ts' should be boolean"), [Option]},
+ Txt = {?T("Value of '~s' should be boolean"), [Option]},
{error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}.
%% Return a field for an XForm for Key, with data filled in, if
diff --git a/src/rest.erl b/src/rest.erl
index b8cd84dea..ef7080312 100644
--- a/src/rest.erl
+++ b/src/rest.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Oct 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/str.erl b/src/str.erl
index 16f866972..16e167664 100644
--- a/src/str.erl
+++ b/src/str.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Feb 2012 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/translate.erl b/src/translate.erl
index 14f7b292f..b17a3b836 100644
--- a/src/translate.erl
+++ b/src/translate.erl
@@ -5,7 +5,7 @@
%%% Created : 6 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/win32_dns.erl b/src/win32_dns.erl
index a70e81f30..8ef4a374c 100644
--- a/src/win32_dns.erl
+++ b/src/win32_dns.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Mar 2009 by Geoff Cant
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as