mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-24 17:29:28 +01:00
Generate ejabberd.yml.5 man page from source code directly
Several documentation callbacks (doc/0 and mod_doc/0) are implemented and `ejabberdctl man` command is added to generate a man page. Note that the command requires a2x to be installed (which is a part of asciidoc package).
This commit is contained in:
parent
c40d8fe11b
commit
97da380acd
@ -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}}
|
||||
].
|
||||
|
||||
|
||||
|
@ -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]).
|
||||
|
||||
|
459
src/ejabberd_doc.erl
Normal file
459
src/ejabberd_doc.erl
Normal file
@ -0,0 +1,459 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_doc.erl
|
||||
%%% Purpose : Options documentation generator
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2019 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>".
|
@ -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).
|
||||
@ -730,6 +730,9 @@ globals() ->
|
||||
websocket_ping_interval,
|
||||
websocket_timeout].
|
||||
|
||||
doc() ->
|
||||
ejabberd_options_doc:doc().
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
1256
src/ejabberd_options_doc.erl
Normal file
1256
src/ejabberd_options_doc.erl
Normal file
File diff suppressed because it is too large
Load Diff
@ -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]).
|
||||
|
@ -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'.")}}]}.
|
||||
|
@ -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\">>}]."]}]}.
|
||||
|
@ -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.")}.
|
||||
|
@ -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.")}}]}.
|
||||
|
@ -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'.")}}]}.
|
||||
|
@ -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'.")}}]}.
|
||||
|
@ -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.")]}.
|
||||
|
@ -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
|
||||
%%%----------------------------------------------------------------------
|
||||
|
@ -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.")}}]}.
|
||||
|
@ -37,7 +37,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]).
|
||||
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]).
|
||||
@ -301,3 +301,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).")}.
|
||||
|
@ -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) ->
|
||||
[].
|
||||
|
@ -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.")}.
|
||||
|
@ -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(_, _) ->
|
||||
[].
|
||||
|
||||
|
@ -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.")}}]}]}.
|
||||
|
@ -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]).
|
||||
@ -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'.")}}]}.
|
||||
|
@ -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.")}.
|
||||
|
@ -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,
|
||||
@ -498,3 +499,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",
|
||||
" ..."]}]}.
|
||||
|
@ -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 \"http://@HOST@:5444\".")}},
|
||||
{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) ->
|
||||
[].
|
||||
|
@ -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}].
|
||||
|
@ -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.
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -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.")}}]}.
|
||||
|
@ -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)
|
||||
|
@ -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.")}}]}.
|
||||
|
@ -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'.")}}]}.
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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).
|
||||
@ -259,6 +261,91 @@ listen_options() ->
|
||||
{tls, false},
|
||||
{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
|
||||
%%%===================================================================
|
||||
|
378
src/mod_muc.erl
378
src/mod_muc.erl
@ -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"]}}]}]}.
|
||||
|
@ -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,
|
||||
@ -1315,3 +1315,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'.")]}.
|
||||
|
@ -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"]}}]}.
|
||||
|
@ -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").
|
||||
@ -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"]}.
|
||||
|
@ -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}).
|
||||
|
||||
@ -1235,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",
|
||||
" ..."
|
||||
]}]}.
|
||||
|
@ -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",
|
||||
" ..."]}.
|
||||
|
@ -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",
|
||||
" ..."]}.
|
||||
|
@ -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.")}}]}.
|
||||
|
@ -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()]}.
|
||||
|
@ -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(_, _) ->
|
||||
[].
|
||||
|
||||
|
@ -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",
|
||||
" ..."]}.
|
||||
|
@ -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.
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -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.
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -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").
|
||||
@ -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'.")}}]}.
|
||||
|
@ -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").
|
||||
|
||||
@ -628,3 +629,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.")]}.
|
||||
|
@ -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",
|
||||
" ..."]}.
|
||||
|
@ -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].
|
||||
|
||||
|
@ -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",
|
||||
" ..."]}.
|
||||
|
@ -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"
|
||||
]}
|
||||
]}.
|
||||
|
@ -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.")]}.
|
||||
|
118
src/mod_sip.erl
118
src/mod_sip.erl
@ -29,7 +29,7 @@
|
||||
-include("logger.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}.
|
||||
@ -49,9 +49,11 @@ 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").
|
||||
-include("translate.hrl").
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
@ -336,20 +338,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)).
|
||||
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 | undefined}}]} |
|
||||
-spec mod_options(binary()) -> [{via, [{tcp | tls | udp, {binary(), 1..65535}}]} |
|
||||
{atom(), term()}].
|
||||
mod_options(Host) ->
|
||||
Route = #uri{scheme = <<"sip">>,
|
||||
@ -363,3 +375,79 @@ mod_options(Host) ->
|
||||
{via, []}].
|
||||
|
||||
-endif.
|
||||
|
||||
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",
|
||||
" ..."]}.
|
||||
|
@ -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.")]}.
|
||||
|
@ -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,
|
||||
@ -849,3 +850,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.")}}]}.
|
||||
|
@ -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.")}.
|
||||
|
@ -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"]}]}}]}.
|
||||
|
@ -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]]}.
|
||||
|
@ -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'.")}}]}.
|
||||
|
@ -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.")}}]}.
|
||||
|
@ -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'.")}}]}.
|
||||
|
Loading…
Reference in New Issue
Block a user