-type image_error() :: efbig | enodata | limit | malformed_image | timeout.
-type priority() :: neg_integer().
+-type callback() :: fun((captcha_succeed | captcha_failed) -> any()).
-record(state, {limits = treap:empty() :: treap:treap(),
enabled = false :: boolean()}).
#xdata_field{type = Type, var = Var, values = [Value]}.
-spec create_captcha(binary(), jid(), jid(),
- binary(), any(), any()) -> {error, image_error()} |
- {ok, binary(), [text()], [xmpp_element()]}.
+ binary(), any(),
+ callback() | term()) -> {error, image_error()} |
+ {ok, binary(), [text()], [xmpp_element()]}.
create_captcha(SID, From, To, Lang, Limiter, Args) ->
case create_image(Limiter) of
{ok, Type, Key, Image} ->
end
end.
--spec callback(captcha_succeed | captcha_failed, pid(), term()) -> any().
+-spec callback(captcha_succeed | captcha_failed,
+ pid() | undefined,
+ callback() | term()) -> any().
callback(Result, _Pid, F) when is_function(F) ->
F(Result);
callback(Result, Pid, Args) when is_pid(Pid) ->
%%%===================================================================
%%% Hooks
%%%===================================================================
+-spec pubsub_publish_item(binary(), binary(), jid(), jid(), binary(), [xmlel()]) -> ok.
pubsub_publish_item(LServer, ?NS_AVATAR_METADATA,
#jid{luser = LUser, lserver = LServer} = From,
#jid{luser = LUser, lserver = LServer} = Host,
-define(SETS, gb_sets).
+-type c2s_state() :: ejabberd_c2s:state().
+
%%%===================================================================
%%% Callbacks and hooks
%%%===================================================================
reload(_Host, _NewOpts, _OldOpts) ->
ok.
+-spec filter_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()} |
+ {stop, {drop, c2s_state()}}.
filter_packet({#message{from = From} = Msg, State} = Acc) ->
LFrom = jid:tolower(From),
LBFrom = jid:remove_resource(LFrom),
filter_packet(Acc) ->
Acc.
+-spec filter_offline_msg({_, message()}) -> {_, message()} | {stop, {drop, message()}}.
filter_offline_msg({_Action, #message{} = Msg} = Acc) ->
case check_message(Msg) of
allow -> Acc;
deny -> {stop, {drop, Msg}}
end.
+-spec filter_subscription(boolean(), presence()) -> boolean() | {stop, false}.
filter_subscription(Acc, #presence{meta = #{captcha := passed}}) ->
Acc;
filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
filter_subscription(Acc, _) ->
Acc.
+-spec handle_captcha_result(captcha_succeed | captcha_failed, presence()) -> ok.
handle_captcha_result(captcha_succeed, Pres) ->
Pres1 = xmpp:put_meta(Pres, captcha, passed),
ejabberd_router:route(Pres1);
%%%===================================================================
%%% Internal functions
%%%===================================================================
+-spec check_message(message()) -> allow | deny.
check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
LServer = To#jid.lserver,
case need_check(Msg) of
true
end.
+-spec sets_bare_member(ljid(), ?SETS:set()) -> boolean().
sets_bare_member({U, S, <<"">>} = LBJID, Set) ->
case ?SETS:next(?SETS:iterator_from(LBJID, Set)) of
{{U, S, _}, _} -> true;
err_db_failure(IQ)
end.
+-spec err_db_failure(iq()) -> iq().
err_db_failure(#iq{lang = Lang} = IQ) ->
Txt = ?T("Database failure"),
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)).
-record(state, {host = <<"">> :: binary()}).
+-type digest_type() :: md5 | sha | sha224 | sha256 | sha384 | sha512.
+
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), {binary(), binary()}, [binary() | pos_integer()]) -> ok.
-callback caps_read(binary(), {binary(), binary()}) ->
_Err -> <<"">>
end.
--type digest_type() :: md5 | sha | sha224 | sha256 | sha384 | sha512.
-spec compute_disco_hash(disco_info(), digest_type()) -> binary().
compute_disco_hash(DiscoInfo, Algo) ->
Concat = list_to_binary([concat_identities(DiscoInfo),
concat_features(DiscoInfo), concat_info(DiscoInfo)]),
base64:encode(case Algo of
- md5 -> erlang:md5(Concat);
- sha -> crypto:hash(sha, Concat);
- sha224 -> crypto:hash(sha224, Concat);
- sha256 -> crypto:hash(sha256, Concat);
- sha384 -> crypto:hash(sha384, Concat);
- sha512 -> crypto:hash(sha512, Concat)
- end).
+ md5 -> erlang:md5(Concat);
+ sha -> crypto:hash(sha, Concat);
+ sha224 -> crypto:hash(sha224, Concat);
+ sha256 -> crypto:hash(sha256, Concat);
+ sha384 -> crypto:hash(sha384, Concat);
+ sha512 -> crypto:hash(sha512, Concat)
+ end).
-spec check_hash(caps(), disco_info()) -> boolean().
check_hash(Caps, DiscoInfo) ->
[<<"http:">>, <<"jabber.org">>, <<"protocol">>,
<<"admin">>, Sub]).
+-spec tokenize(binary()) -> [binary()].
tokenize(Node) -> str:tokens(Node, <<"/#">>).
+-spec get_sm_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()].
get_sm_identity(Acc, _From, _To, Node, Lang) ->
case Node of
<<"config">> ->
_ -> Acc
end.
+-spec get_local_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()].
get_local_identity(Acc, _From, _To, Node, Lang) ->
LNode = tokenize(Node),
case LNode of
allow -> {result, Feats}
end).
+-spec get_sm_features(mod_disco:features_acc(), jid(), jid(),
+ binary(), binary()) -> mod_disco:features_acc().
get_sm_features(Acc, From,
#jid{lserver = LServer} = _To, Node, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
end
end.
+-spec get_local_features(mod_disco:features_acc(), jid(), jid(),
+ binary(), binary()) -> mod_disco:features_acc().
get_local_features(Acc, From,
#jid{lserver = LServer} = _To, Node, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
end.
%%%-----------------------------------------------------------------------
--spec adhoc_sm_items(empty | {error, stanza_error()} | {result, [disco_item()]},
- jid(), jid(), binary()) -> {error, stanza_error()} |
- {result, [disco_item()]} |
- empty.
+-spec adhoc_sm_items(mod_disco:items_acc(),
+ jid(), jid(), binary()) -> mod_disco:items_acc().
adhoc_sm_items(Acc, From, #jid{lserver = LServer} = To,
Lang) ->
case acl:match_rule(LServer, configure, From) of
end.
%%%-----------------------------------------------------------------------
-
+-spec get_sm_items(mod_disco:items_acc(), jid(), jid(),
+ binary(), binary()) -> mod_disco:items_acc().
get_sm_items(Acc, From,
#jid{user = User, server = Server, lserver = LServer} =
To,
end
end.
+-spec get_user_resources(binary(), binary()) -> [disco_item()].
get_user_resources(User, Server) ->
Rs = ejabberd_sm:get_user_resources(User, Server),
lists:map(fun (R) ->
%%%-----------------------------------------------------------------------
--spec adhoc_local_items(empty | {error, stanza_error()} | {result, [disco_item()]},
- jid(), jid(), binary()) -> {error, stanza_error()} |
- {result, [disco_item()]} |
- empty.
+-spec adhoc_local_items(mod_disco:items_acc(),
+ jid(), jid(), binary()) -> mod_disco:items_acc().
adhoc_local_items(Acc, From,
#jid{lserver = LServer, server = Server} = To, Lang) ->
case acl:match_rule(LServer, configure, From) of
<<"">>, Server, Lang),
Nodes1 = lists:filter(
fun (#disco_item{node = Nd}) ->
- F = get_local_features([], From, To, Nd, Lang),
+ F = get_local_features(empty, From, To, Nd, Lang),
case F of
{result, [?NS_COMMANDS]} -> true;
_ -> false
_ -> Acc
end.
+-spec recursively_get_local_items(global | vhost, binary(), binary(),
+ binary(), binary()) -> [disco_item()].
recursively_get_local_items(_PermLev, _LServer,
<<"online users">>, _Server, _Lang) ->
[];
end,
Items)).
+-spec get_permission_level(jid()) -> global | vhost.
get_permission_level(JID) ->
case acl:match_rule(global, configure, JID) of
allow -> global;
end
end).
+-spec get_local_items(mod_disco:items_acc(), jid(), jid(),
+ binary(), binary()) -> mod_disco:items_acc().
get_local_items(Acc, From, #jid{lserver = LServer} = To,
<<"">>, Lang) ->
case gen_mod:is_loaded(LServer, mod_adhoc) of
end.
%%%-----------------------------------------------------------------------
-
-%% @spec ({PermissionLevel, Host}, [string()], Server::string(), Lang)
-%% -> {result, [xmlelement()]}
-%% PermissionLevel = global | vhost
+-spec get_local_items({global | vhost, binary()}, [binary()],
+ binary(), binary()) -> {result, [disco_item()]} | {error, stanza_error()}.
get_local_items(_Host, [], Server, Lang) ->
{result,
[?NODE(?T("Configuration"), <<"config">>),
name = <<U/binary, $@, S/binary>>}
end, Sub)}
catch _:_ ->
- xmpp:err_not_acceptable()
+ {error, xmpp:err_not_acceptable()}
end;
get_local_items({_, Host}, [<<"outgoing s2s">>],
_Server, Lang) ->
get_local_items(_Host, _, _Server, _Lang) ->
{error, xmpp:err_item_not_found()}.
+-spec get_online_vh_users(binary()) -> [disco_item()].
get_online_vh_users(Host) ->
case catch ejabberd_sm:get_vh_session_list(Host) of
{'EXIT', _Reason} -> [];
end, SURs)
end.
+-spec get_all_vh_users(binary()) -> [disco_item()].
get_all_vh_users(Host) ->
case catch ejabberd_auth:get_users(Host)
of
end
end.
+-spec get_outgoing_s2s(binary(), binary()) -> [disco_item()].
get_outgoing_s2s(Host, Lang) ->
case catch ejabberd_s2s:dirty_get_connections() of
{'EXIT', _Reason} -> [];
end, lists:usort(TConns))
end.
+-spec get_outgoing_s2s(binary(), binary(), binary()) -> [disco_item()].
get_outgoing_s2s(Host, Lang, To) ->
case catch ejabberd_s2s:dirty_get_connections() of
{'EXIT', _Reason} -> [];
Connections)))
end.
+-spec get_running_nodes(binary(), binary()) -> [disco_item()].
get_running_nodes(Server, _Lang) ->
case catch mnesia:system_info(running_db_nodes) of
{'EXIT', _Reason} -> [];
lists:sort(DBNodes))
end.
+-spec get_stopped_nodes(binary()) -> [disco_item()].
get_stopped_nodes(_Lang) ->
case catch lists:usort(mnesia:system_info(db_nodes) ++
mnesia:system_info(extra_db_nodes))
_ -> Acc
end.
+-spec adhoc_local_commands(jid(), jid(), adhoc_command()) -> adhoc_command() | {error, stanza_error()}.
adhoc_local_commands(From,
#jid{lserver = LServer} = _To,
#adhoc_command{lang = Lang, node = Node,
#xdata_option{label = tr(Lang, ?T("Remote copy")),
value = <<"unknown">>}]}).
+-spec get_form(binary(), [binary()], binary()) -> {result, xdata()} |
+ {result, completed, xdata()} |
+ {error, stanza_error()}.
get_form(_Host, [<<"running nodes">>, ENode, <<"DB">>],
Lang) ->
case search_running_node(ENode) of
get_form(_Host, _, _Lang) ->
{error, xmpp:err_service_unavailable()}.
+-spec set_form(jid(), binary(), [binary()], binary(), xdata()) -> {result, xdata() | undefined} |
+ {error, stanza_error()}.
set_form(_From, _Host,
[<<"running nodes">>, ENode, <<"DB">>], Lang, XData) ->
case search_running_node(ENode) of
set_form(_From, _Host, _, _Lang, _XData) ->
{error, xmpp:err_service_unavailable()}.
-get_value(Field, XData) -> hd(get_values(Field, XData)).
+-spec get_value(binary(), xdata()) -> binary().
+get_value(Field, XData) ->
+ hd(get_values(Field, XData)).
+-spec get_values(binary(), xdata()) -> [binary()].
get_values(Field, XData) ->
xmpp_util:get_xdata_values(Field, XData).
+-spec search_running_node(binary()) -> false | node().
search_running_node(SNode) ->
search_running_node(SNode,
mnesia:system_info(running_db_nodes)).
+-spec search_running_node(binary(), [node()]) -> false | node().
search_running_node(_, []) -> false;
search_running_node(SNode, [Node | Nodes]) ->
- case iolist_to_binary(atom_to_list(Node)) of
- SNode -> Node;
- _ -> search_running_node(SNode, Nodes)
+ case atom_to_binary(Node, utf8) of
+ SNode -> Node;
+ _ -> search_running_node(SNode, Nodes)
end.
+-spec stop_node(jid(), binary(), binary(), restart | stop, xdata()) -> {result, undefined}.
stop_node(From, Host, ENode, Action, XData) ->
Delay = binary_to_integer(get_value(<<"delay">>, XData)),
Subject = case get_value(<<"subject">>, XData) of
end,
Time = timer:seconds(Delay),
Node = misc:binary_to_atom(ENode),
- {ok, _} = timer:apply_after(Time, rpc, call, [Node, init, Action, []]),
+ {ok, _} = timer:apply_after(Time, ejabberd_cluster, call, [Node, init, Action, []]),
{result, undefined}.
+-spec get_last_info(binary(), binary()) -> {ok, non_neg_integer(), binary()} | not_found.
get_last_info(User, Server) ->
case gen_mod:is_loaded(Server, mod_last) of
true -> mod_last:get_last_info(User, Server);
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec adhoc_sm_commands(adhoc_command(), jid(), jid(), adhoc_command()) -> adhoc_command().
+-spec adhoc_sm_commands(adhoc_command(), jid(), jid(), adhoc_command()) -> adhoc_command() |
+ {error, stanza_error()}.
adhoc_sm_commands(_Acc, From,
#jid{user = User, server = Server, lserver = LServer},
#adhoc_command{lang = Lang, node = <<"config">>,
end;
adhoc_sm_commands(Acc, _From, _To, _Request) -> Acc.
+-spec get_sm_form(binary(), binary(), binary(), binary()) -> {result, xdata()} |
+ {error, stanza_error()}.
get_sm_form(User, Server, <<"config">>, Lang) ->
{result,
#xdata{type = form,
get_sm_form(_User, _Server, _Node, _Lang) ->
{error, xmpp:err_service_unavailable()}.
+-spec set_sm_form(binary(), binary(), binary(), adhoc_command()) -> adhoc_command() |
+ {error, stanza_error()}.
set_sm_form(User, Server, <<"config">>,
#adhoc_command{lang = Lang, node = Node,
sid = SessionID, xdata = XData}) ->
-include("xmpp.hrl").
-include("translate.hrl").
--type disco_acc() :: {error, stanza_error()} | {result, [binary()]} | empty.
-type route_type() :: ejabberd_sm | ejabberd_local.
-type delegations() :: #{{binary(), route_type()} => {binary(), disco_info()}}.
-record(state, {server_host = <<"">> :: binary()}).
ejabberd_sm(IQ) ->
process_iq(IQ, ejabberd_sm).
--spec disco_local_features(disco_acc(), jid(), jid(), binary(), binary()) -> disco_acc().
+-spec disco_local_features(mod_disco:features_acc(), jid(), jid(),
+ binary(), binary()) -> mod_disco:features_acc().
disco_local_features(Acc, From, To, Node, Lang) ->
disco_features(Acc, From, To, Node, Lang, ejabberd_local).
--spec disco_sm_features(disco_acc(), jid(), jid(), binary(), binary()) -> disco_acc().
+-spec disco_sm_features(mod_disco:features_acc(), jid(), jid(),
+ binary(), binary()) -> mod_disco:features_acc().
disco_sm_features(Acc, From, To, Node, Lang) ->
disco_features(Acc, From, To, Node, Lang, ejabberd_sm).
--spec disco_local_identity(disco_acc(), jid(), jid(), binary(), binary()) -> disco_acc().
+-spec disco_local_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()].
disco_local_identity(Acc, From, To, Node, Lang) ->
disco_identity(Acc, From, To, Node, Lang, ejabberd_local).
--spec disco_sm_identity(disco_acc(), jid(), jid(), binary(), binary()) -> disco_acc().
+-spec disco_sm_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()].
disco_sm_identity(Acc, From, To, Node, Lang) ->
disco_identity(Acc, From, To, Node, Lang, ejabberd_sm).
end, [{ejabberd_local, <<(?NS_DELEGATION)/binary, "::", NS/binary>>},
{ejabberd_sm, <<(?NS_DELEGATION)/binary, ":bare:", NS/binary>>}]).
--spec disco_features(disco_acc(), jid(), jid(), binary(), binary(),
- route_type()) -> disco_acc().
+-spec disco_features(mod_disco:features_acc(), jid(), jid(), binary(), binary(),
+ route_type()) -> mod_disco:features_acc().
disco_features(Acc, _From, To, <<"">>, _Lang, Type) ->
Delegations = get_delegations(To#jid.lserver),
Features = my_features(Type) ++
disco_features(Acc, _, _, _, _, _) ->
Acc.
--spec disco_identity(disco_acc(), jid(), jid(), binary(), binary(),
- route_type()) -> disco_acc().
+-spec disco_identity([identity()], jid(), jid(), binary(), binary(),
+ route_type()) -> [identity()].
disco_identity(Acc, _From, To, <<"">>, _Lang, Type) ->
Delegations = get_delegations(To#jid.lserver),
Identities = lists:flatmap(
(_) ->
[]
end, maps:to_list(Delegations)),
- case Acc of
- empty when Identities /= [] -> {result, Identities};
- {result, Ids} -> {result, Ids ++ Identities};
- Acc -> Acc
- end;
+ Acc ++ Identities;
disco_identity(Acc, _From, _To, _Node, _Lang, _Type) ->
Acc.
Err = xmpp:serr_policy_violation({Format, Args}, Lang),
{stop, ejabberd_c2s:send(State, Err)}.
+-spec is_whitelisted(binary(), inet:ip_address()) -> boolean().
is_whitelisted(Host, Addr) ->
Access = mod_fail2ban_opt:access(Host),
acl:match_rule(Host, Access, Addr) == allow.
+-spec seconds_to_now(non_neg_integer()) -> erlang:timestamp().
seconds_to_now(Secs) ->
{Secs div 1000000, Secs rem 1000000, 0}.
+-spec format_date(calendar:datetime()) -> iolist().
format_date({{Year, Month, Day}, {Hour, Minute, Second}}) ->
io_lib:format("~2..0w:~2..0w:~2..0w ~2..0w.~2..0w.~4..0w",
[Hour, Minute, Second, Day, Month, Year]).
-define(LAST_CACHE, last_activity_cache).
+-type c2s_state() :: ejabberd_c2s:state().
+
-callback init(binary(), gen_mod:opts()) -> any().
-callback import(binary(), #last_activity{}) -> ok | pass.
-callback get_last(binary(), binary()) ->
xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
end.
+-spec privacy_check_packet(allow | deny, c2s_state(), stanza(), in | out) -> allow | deny | {stop, deny}.
privacy_check_packet(allow, C2SState,
#iq{from = From, to = To, type = T} = IQ, in)
when T == get; T == set ->
reload(_Host, _NewOpts, _OldOpts) ->
ok.
+-spec process_local_iq(iq()) -> iq().
process_local_iq(#iq{type = set, lang = Lang} = IQ) ->
Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
end,
{ok, #state{hosts = MyHosts, server_host = Host}}.
-handle_call(_Call, _From, State) ->
+handle_call(Call, From, State) ->
+ ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Call]),
{noreply, State}.
handle_cast(Cast, State) ->
Lang, ?T("You need an x:data capable client to search")),
xdata = X}.
+-spec make_instructions(module(), binary()) -> binary().
make_instructions(Mod, Lang) ->
Fill = translate:translate(
Lang,