%%%---------------------------------------------------------------------- %%% File : mod_configure.erl %%% Author : Alexey Shchepin %%% Purpose : Support for online configuration of ejabberd %%% Created : 19 Jan 2003 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2013 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. %%% %%%---------------------------------------------------------------------- %%% Implements most of XEP-0133: Service Administration Version 1.1 %%% (2005-08-19) -module(mod_configure). -author('alexey@process-one.net'). -behaviour(gen_mod). -export([start/2, stop/1, get_local_identity/5, get_local_features/5, get_local_items/5, 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]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("jlib.hrl"). -include("adhoc.hrl"). -define(T(Lang, Text), translate:translate(Lang, Text)). %% Copied from ejabberd_sm.erl -record(session, {sid, usr, us, priority, info}). start(Host, _Opts) -> ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_items, 50), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 50), ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 50), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50), ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, adhoc_local_items, 50), ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, adhoc_local_commands, 50), ejabberd_hooks:add(adhoc_sm_items, Host, ?MODULE, adhoc_sm_items, 50), ejabberd_hooks:add(adhoc_sm_commands, Host, ?MODULE, adhoc_sm_commands, 50), ok. stop(Host) -> ejabberd_hooks:delete(adhoc_sm_commands, Host, ?MODULE, adhoc_sm_commands, 50), ejabberd_hooks:delete(adhoc_sm_items, Host, ?MODULE, adhoc_sm_items, 50), ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, adhoc_local_commands, 50), ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, adhoc_local_items, 50), ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 50), ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 50), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 50), ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_items, 50), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS). %%%----------------------------------------------------------------------- -define(INFO_IDENTITY(Category, Type, Name, Lang), [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, Category}, {<<"type">>, Type}, {<<"name">>, ?T(Lang, Name)}], children = []}]). -define(INFO_COMMAND(Name, Lang), ?INFO_IDENTITY(<<"automation">>, <<"command-node">>, Name, Lang)). -define(NODEJID(To, Name, Node), #xmlel{name = <<"item">>, attrs = [{<<"jid">>, jlib:jid_to_string(To)}, {<<"name">>, ?T(Lang, Name)}, {<<"node">>, Node}], children = []}). -define(NODE(Name, Node), #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Server}, {<<"name">>, ?T(Lang, Name)}, {<<"node">>, Node}], children = []}). -define(NS_ADMINX(Sub), <<(?NS_ADMIN)/binary, "#", Sub/binary>>). -define(NS_ADMINL(Sub), [<<"http:">>, <<"jabber.org">>, <<"protocol">>, <<"admin">>, Sub]). tokenize(Node) -> str:tokens(Node, <<"/#">>). get_sm_identity(Acc, _From, _To, Node, Lang) -> case Node of <<"config">> -> ?INFO_COMMAND(<<"Configuration">>, Lang); _ -> Acc end. get_local_identity(Acc, _From, _To, Node, Lang) -> LNode = tokenize(Node), case LNode of [<<"running nodes">>, ENode] -> ?INFO_IDENTITY(<<"ejabberd">>, <<"node">>, ENode, Lang); [<<"running nodes">>, _ENode, <<"DB">>] -> ?INFO_COMMAND(<<"Database">>, Lang); [<<"running nodes">>, _ENode, <<"modules">>, <<"start">>] -> ?INFO_COMMAND(<<"Start Modules">>, Lang); [<<"running nodes">>, _ENode, <<"modules">>, <<"stop">>] -> ?INFO_COMMAND(<<"Stop Modules">>, Lang); [<<"running nodes">>, _ENode, <<"backup">>, <<"backup">>] -> ?INFO_COMMAND(<<"Backup">>, Lang); [<<"running nodes">>, _ENode, <<"backup">>, <<"restore">>] -> ?INFO_COMMAND(<<"Restore">>, Lang); [<<"running nodes">>, _ENode, <<"backup">>, <<"textfile">>] -> ?INFO_COMMAND(<<"Dump to Text File">>, Lang); [<<"running nodes">>, _ENode, <<"import">>, <<"file">>] -> ?INFO_COMMAND(<<"Import File">>, Lang); [<<"running nodes">>, _ENode, <<"import">>, <<"dir">>] -> ?INFO_COMMAND(<<"Import Directory">>, Lang); [<<"running nodes">>, _ENode, <<"restart">>] -> ?INFO_COMMAND(<<"Restart Service">>, Lang); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?INFO_COMMAND(<<"Shut Down Service">>, Lang); ?NS_ADMINL(<<"add-user">>) -> ?INFO_COMMAND(<<"Add User">>, Lang); ?NS_ADMINL(<<"delete-user">>) -> ?INFO_COMMAND(<<"Delete User">>, Lang); ?NS_ADMINL(<<"end-user-session">>) -> ?INFO_COMMAND(<<"End User Session">>, Lang); ?NS_ADMINL(<<"get-user-password">>) -> ?INFO_COMMAND(<<"Get User Password">>, Lang); ?NS_ADMINL(<<"change-user-password">>) -> ?INFO_COMMAND(<<"Change User Password">>, Lang); ?NS_ADMINL(<<"get-user-lastlogin">>) -> ?INFO_COMMAND(<<"Get User Last Login Time">>, Lang); ?NS_ADMINL(<<"user-stats">>) -> ?INFO_COMMAND(<<"Get User Statistics">>, Lang); ?NS_ADMINL(<<"get-registered-users-num">>) -> ?INFO_COMMAND(<<"Get Number of Registered Users">>, Lang); ?NS_ADMINL(<<"get-online-users-num">>) -> ?INFO_COMMAND(<<"Get Number of Online Users">>, Lang); [<<"config">>, <<"acls">>] -> ?INFO_COMMAND(<<"Access Control Lists">>, Lang); [<<"config">>, <<"access">>] -> ?INFO_COMMAND(<<"Access Rules">>, Lang); _ -> Acc end. %%%----------------------------------------------------------------------- -define(INFO_RESULT(Allow, Feats), case Allow of deny -> {error, ?ERR_FORBIDDEN}; allow -> {result, Feats} end). get_sm_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Allow = acl:match_rule(LServer, configure, From), case Node of <<"config">> -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); _ -> Acc end end. get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> LNode = tokenize(Node), Allow = acl:match_rule(LServer, configure, From), case LNode of [<<"config">>] -> ?INFO_RESULT(Allow, []); [<<"user">>] -> ?INFO_RESULT(Allow, []); [<<"online users">>] -> ?INFO_RESULT(Allow, []); [<<"all users">>] -> ?INFO_RESULT(Allow, []); [<<"all users">>, <<$@, _/binary>>] -> ?INFO_RESULT(Allow, []); [<<"outgoing s2s">> | _] -> ?INFO_RESULT(Allow, []); [<<"running nodes">>] -> ?INFO_RESULT(Allow, []); [<<"stopped nodes">>] -> ?INFO_RESULT(Allow, []); [<<"running nodes">>, _ENode] -> ?INFO_RESULT(Allow, [?NS_STATS]); [<<"running nodes">>, _ENode, <<"DB">>] -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); [<<"running nodes">>, _ENode, <<"modules">>] -> ?INFO_RESULT(Allow, []); [<<"running nodes">>, _ENode, <<"modules">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); [<<"running nodes">>, _ENode, <<"backup">>] -> ?INFO_RESULT(Allow, []); [<<"running nodes">>, _ENode, <<"backup">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); [<<"running nodes">>, _ENode, <<"import">>] -> ?INFO_RESULT(Allow, []); [<<"running nodes">>, _ENode, <<"import">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); [<<"running nodes">>, _ENode, <<"restart">>] -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); [<<"config">>, _] -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"add-user">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"delete-user">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"end-user-session">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"get-user-password">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"change-user-password">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"get-user-lastlogin">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"user-stats">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"get-registered-users-num">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); ?NS_ADMINL(<<"get-online-users-num">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS]); _ -> Acc end end. %%%----------------------------------------------------------------------- adhoc_sm_items(Acc, From, #jid{lserver = LServer} = To, Lang) -> case acl:match_rule(LServer, configure, From) of allow -> Items = case Acc of {result, Its} -> Its; empty -> [] end, Nodes = [#xmlel{name = <<"item">>, attrs = [{<<"jid">>, jlib:jid_to_string(To)}, {<<"name">>, ?T(Lang, <<"Configuration">>)}, {<<"node">>, <<"config">>}], children = []}], {result, Items ++ Nodes}; _ -> Acc end. %%%----------------------------------------------------------------------- get_sm_items(Acc, From, #jid{user = User, server = Server, lserver = LServer} = To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Items = case Acc of {result, Its} -> Its; empty -> [] end, case {acl:match_rule(LServer, configure, From), Node} of {allow, <<"">>} -> Nodes = [?NODEJID(To, <<"Configuration">>, <<"config">>), ?NODEJID(To, <<"User Management">>, <<"user">>)], {result, Items ++ Nodes ++ get_user_resources(User, Server)}; {allow, <<"config">>} -> {result, []}; {_, <<"config">>} -> {error, ?ERR_FORBIDDEN}; _ -> Acc end end. get_user_resources(User, Server) -> Rs = ejabberd_sm:get_user_resources(User, Server), lists:map(fun (R) -> #xmlel{name = <<"item">>, attrs = [{<<"jid">>, <>}, {<<"name">>, User}], children = []} end, lists:sort(Rs)). %%%----------------------------------------------------------------------- adhoc_local_items(Acc, From, #jid{lserver = LServer, server = Server} = To, Lang) -> case acl:match_rule(LServer, configure, From) of allow -> Items = case Acc of {result, Its} -> Its; empty -> [] end, PermLev = get_permission_level(From), Nodes = recursively_get_local_items(PermLev, LServer, <<"">>, Server, Lang), Nodes1 = lists:filter(fun (N) -> Nd = xml:get_tag_attr_s(<<"node">>, N), F = get_local_features([], From, To, Nd, Lang), case F of {result, [?NS_COMMANDS]} -> true; _ -> false end end, Nodes), {result, Items ++ Nodes1}; _ -> Acc end. recursively_get_local_items(_PermLev, _LServer, <<"online users">>, _Server, _Lang) -> []; recursively_get_local_items(_PermLev, _LServer, <<"all users">>, _Server, _Lang) -> []; recursively_get_local_items(PermLev, LServer, Node, Server, Lang) -> LNode = tokenize(Node), Items = case get_local_items({PermLev, LServer}, LNode, Server, Lang) of {result, Res} -> Res; {error, _Error} -> [] end, Nodes = lists:flatten(lists:map(fun (N) -> S = xml:get_tag_attr_s(<<"jid">>, N), Nd = xml:get_tag_attr_s(<<"node">>, N), if (S /= Server) or (Nd == <<"">>) -> []; true -> [N, recursively_get_local_items(PermLev, LServer, Nd, Server, Lang)] end end, Items)), Nodes. get_permission_level(JID) -> case acl:match_rule(global, configure, JID) of allow -> global; deny -> vhost end. %%%----------------------------------------------------------------------- -define(ITEMS_RESULT(Allow, LNode, Fallback), case Allow of deny -> Fallback; allow -> PermLev = get_permission_level(From), case get_local_items({PermLev, LServer}, LNode, jlib:jid_to_string(To), Lang) of {result, Res} -> {result, Res}; {error, Error} -> {error, Error} end end). get_local_items(Acc, From, #jid{lserver = LServer} = To, <<"">>, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> Items = case Acc of {result, Its} -> Its; empty -> [] end, Allow = acl:match_rule(LServer, configure, From), case Allow of deny -> {result, Items}; allow -> PermLev = get_permission_level(From), case get_local_items({PermLev, LServer}, [], jlib:jid_to_string(To), Lang) of {result, Res} -> {result, Items ++ Res}; {error, _Error} -> {result, Items} end end end; get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) -> case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> LNode = tokenize(Node), Allow = acl:match_rule(LServer, configure, From), case LNode of [<<"config">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"user">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"online users">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"all users">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"all users">>, <<$@, _/binary>>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"outgoing s2s">> | _] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"stopped nodes">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"DB">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"modules">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"modules">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"backup">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"backup">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"import">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"import">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"restart">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); [<<"config">>, _] -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"add-user">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"delete-user">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"end-user-session">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"get-user-password">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"change-user-password">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"get-user-lastlogin">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"user-stats">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"get-registered-users-num">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?NS_ADMINL(<<"get-online-users-num">>) -> ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); _ -> Acc end end. %%%----------------------------------------------------------------------- %% @spec ({PermissionLevel, Host}, [string()], Server::string(), Lang) %% -> {result, [xmlelement()]} %% PermissionLevel = global | vhost get_local_items(_Host, [], Server, Lang) -> {result, [?NODE(<<"Configuration">>, <<"config">>), ?NODE(<<"User Management">>, <<"user">>), ?NODE(<<"Online Users">>, <<"online users">>), ?NODE(<<"All Users">>, <<"all users">>), ?NODE(<<"Outgoing s2s Connections">>, <<"outgoing s2s">>), ?NODE(<<"Running Nodes">>, <<"running nodes">>), ?NODE(<<"Stopped Nodes">>, <<"stopped nodes">>)]}; get_local_items(_Host, [<<"config">>], Server, Lang) -> {result, [?NODE(<<"Access Control Lists">>, <<"config/acls">>), ?NODE(<<"Access Rules">>, <<"config/access">>)]}; get_local_items(_Host, [<<"config">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"user">>], Server, Lang) -> {result, [?NODE(<<"Add User">>, (?NS_ADMINX(<<"add-user">>))), ?NODE(<<"Delete User">>, (?NS_ADMINX(<<"delete-user">>))), ?NODE(<<"End User Session">>, (?NS_ADMINX(<<"end-user-session">>))), ?NODE(<<"Get User Password">>, (?NS_ADMINX(<<"get-user-password">>))), ?NODE(<<"Change User Password">>, (?NS_ADMINX(<<"change-user-password">>))), ?NODE(<<"Get User Last Login Time">>, (?NS_ADMINX(<<"get-user-lastlogin">>))), ?NODE(<<"Get User Statistics">>, (?NS_ADMINX(<<"user-stats">>))), ?NODE(<<"Get Number of Registered Users">>, (?NS_ADMINX(<<"get-registered-users-num">>))), ?NODE(<<"Get Number of Online Users">>, (?NS_ADMINX(<<"get-online-users-num">>)))]}; get_local_items(_Host, [<<"http:">> | _], _Server, _Lang) -> {result, []}; get_local_items({_, Host}, [<<"online users">>], _Server, _Lang) -> {result, get_online_vh_users(Host)}; get_local_items({_, Host}, [<<"all users">>], _Server, _Lang) -> {result, get_all_vh_users(Host)}; get_local_items({_, Host}, [<<"all users">>, <<$@, Diap/binary>>], _Server, _Lang) -> case catch ejabberd_auth:get_vh_registered_users(Host) of {'EXIT', _Reason} -> ?ERR_INTERNAL_SERVER_ERROR; Users -> SUsers = lists:sort([{S, U} || {U, S} <- Users]), case catch begin [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), N1 = jlib:binary_to_integer(S1), N2 = jlib:binary_to_integer(S2), Sub = lists:sublist(SUsers, N1, N2 - N1 + 1), lists:map(fun ({S, U}) -> #xmlel{name = <<"item">>, attrs = [{<<"jid">>, <>}, {<<"name">>, <>}], children = []} end, Sub) end of {'EXIT', _Reason} -> ?ERR_NOT_ACCEPTABLE; Res -> {result, Res} end end; get_local_items({_, Host}, [<<"outgoing s2s">>], _Server, Lang) -> {result, get_outgoing_s2s(Host, Lang)}; get_local_items({_, Host}, [<<"outgoing s2s">>, To], _Server, Lang) -> {result, get_outgoing_s2s(Host, Lang, To)}; get_local_items(_Host, [<<"running nodes">>], Server, Lang) -> {result, get_running_nodes(Server, Lang)}; get_local_items(_Host, [<<"stopped nodes">>], _Server, Lang) -> {result, get_stopped_nodes(Lang)}; get_local_items({global, _Host}, [<<"running nodes">>, ENode], Server, Lang) -> {result, [?NODE(<<"Database">>, <<"running nodes/", ENode/binary, "/DB">>), ?NODE(<<"Modules">>, <<"running nodes/", ENode/binary, "/modules">>), ?NODE(<<"Backup Management">>, <<"running nodes/", ENode/binary, "/backup">>), ?NODE(<<"Import Users From jabberd14 Spool Files">>, <<"running nodes/", ENode/binary, "/import">>), ?NODE(<<"Restart Service">>, <<"running nodes/", ENode/binary, "/restart">>), ?NODE(<<"Shut Down Service">>, <<"running nodes/", ENode/binary, "/shutdown">>)]}; get_local_items({vhost, _Host}, [<<"running nodes">>, ENode], Server, Lang) -> {result, [?NODE(<<"Modules">>, <<"running nodes/", ENode/binary, "/modules">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"DB">>], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, ENode, <<"modules">>], Server, Lang) -> {result, [?NODE(<<"Start Modules">>, <<"running nodes/", ENode/binary, "/modules/start">>), ?NODE(<<"Stop Modules">>, <<"running nodes/", ENode/binary, "/modules/stop">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"modules">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, ENode, <<"backup">>], Server, Lang) -> {result, [?NODE(<<"Backup">>, <<"running nodes/", ENode/binary, "/backup/backup">>), ?NODE(<<"Restore">>, <<"running nodes/", ENode/binary, "/backup/restore">>), ?NODE(<<"Dump to Text File">>, <<"running nodes/", ENode/binary, "/backup/textfile">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"backup">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, ENode, <<"import">>], Server, Lang) -> {result, [?NODE(<<"Import File">>, <<"running nodes/", ENode/binary, "/import/file">>), ?NODE(<<"Import Directory">>, <<"running nodes/", ENode/binary, "/import/dir">>)]}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"import">>, _], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"restart">>], _Server, _Lang) -> {result, []}; get_local_items(_Host, [<<"running nodes">>, _ENode, <<"shutdown">>], _Server, _Lang) -> {result, []}; get_local_items(_Host, _, _Server, _Lang) -> {error, ?ERR_ITEM_NOT_FOUND}. get_online_vh_users(Host) -> case catch ejabberd_sm:get_vh_session_list(Host) of {'EXIT', _Reason} -> []; USRs -> SURs = lists:sort([{S, U, R} || {U, S, R} <- USRs]), lists:map(fun ({S, U, R}) -> #xmlel{name = <<"item">>, attrs = [{<<"jid">>, <>}, {<<"name">>, <>}], children = []} end, SURs) end. get_all_vh_users(Host) -> case catch ejabberd_auth:get_vh_registered_users(Host) of {'EXIT', _Reason} -> []; Users -> SUsers = lists:sort([{S, U} || {U, S} <- Users]), case length(SUsers) of N when N =< 100 -> lists:map(fun ({S, U}) -> #xmlel{name = <<"item">>, attrs = [{<<"jid">>, <>}, {<<"name">>, <>}], children = []} end, SUsers); N -> NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1)) + 1, M = trunc(N / NParts) + 1, lists:map(fun (K) -> L = K + M - 1, Node = <<"@", (iolist_to_binary(integer_to_list(K)))/binary, "-", (iolist_to_binary(integer_to_list(L)))/binary>>, {FS, FU} = lists:nth(K, SUsers), {LS, LU} = if L < N -> lists:nth(L, SUsers); true -> lists:last(SUsers) end, Name = <>, #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Host}, {<<"node">>, <<"all users/", Node/binary>>}, {<<"name">>, Name}], children = []} end, lists:seq(1, N, M)) end end. get_outgoing_s2s(Host, Lang) -> case catch ejabberd_s2s:dirty_get_connections() of {'EXIT', _Reason} -> []; Connections -> DotHost = <<".", Host/binary>>, TConns = [TH || {FH, TH} <- Connections, Host == FH orelse str:suffix(DotHost, FH)], lists:map(fun (T) -> #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Host}, {<<"node">>, <<"outgoing s2s/", T/binary>>}, {<<"name">>, iolist_to_binary(io_lib:format(?T(Lang, <<"To ~s">>), [T]))}], children = []} end, lists:usort(TConns)) end. get_outgoing_s2s(Host, Lang, To) -> case catch ejabberd_s2s:dirty_get_connections() of {'EXIT', _Reason} -> []; Connections -> lists:map(fun ({F, _T}) -> #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Host}, {<<"node">>, <<"outgoing s2s/", To/binary, "/", F/binary>>}, {<<"name">>, iolist_to_binary(io_lib:format(?T(Lang, <<"From ~s">>), [F]))}], children = []} end, lists:keysort(1, lists:filter(fun (E) -> element(2, E) == To end, Connections))) end. get_running_nodes(Server, _Lang) -> case catch mnesia:system_info(running_db_nodes) of {'EXIT', _Reason} -> []; DBNodes -> lists:map(fun (N) -> S = iolist_to_binary(atom_to_list(N)), #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Server}, {<<"node">>, <<"running nodes/", S/binary>>}, {<<"name">>, S}], children = []} end, lists:sort(DBNodes)) end. get_stopped_nodes(_Lang) -> case catch lists:usort(mnesia:system_info(db_nodes) ++ mnesia:system_info(extra_db_nodes)) -- mnesia:system_info(running_db_nodes) of {'EXIT', _Reason} -> []; DBNodes -> lists:map(fun (N) -> S = iolist_to_binary(atom_to_list(N)), #xmlel{name = <<"item">>, attrs = [{<<"jid">>, ?MYNAME}, {<<"node">>, <<"stopped nodes/", S/binary>>}, {<<"name">>, S}], children = []} end, lists:sort(DBNodes)) end. %%------------------------------------------------------------------------- -define(COMMANDS_RESULT(LServerOrGlobal, From, To, Request), case acl:match_rule(LServerOrGlobal, configure, From) of deny -> {error, ?ERR_FORBIDDEN}; allow -> adhoc_local_commands(From, To, Request) end). adhoc_local_commands(Acc, From, #jid{lserver = LServer} = To, #adhoc_request{node = Node} = Request) -> LNode = tokenize(Node), case LNode of [<<"running nodes">>, _ENode, <<"DB">>] -> ?COMMANDS_RESULT(global, From, To, Request); [<<"running nodes">>, _ENode, <<"modules">>, _] -> ?COMMANDS_RESULT(LServer, From, To, Request); [<<"running nodes">>, _ENode, <<"backup">>, _] -> ?COMMANDS_RESULT(global, From, To, Request); [<<"running nodes">>, _ENode, <<"import">>, _] -> ?COMMANDS_RESULT(global, From, To, Request); [<<"running nodes">>, _ENode, <<"restart">>] -> ?COMMANDS_RESULT(global, From, To, Request); [<<"running nodes">>, _ENode, <<"shutdown">>] -> ?COMMANDS_RESULT(global, From, To, Request); [<<"config">>, _] -> ?COMMANDS_RESULT(LServer, From, To, Request); ?NS_ADMINL(_) -> ?COMMANDS_RESULT(LServer, From, To, Request); _ -> Acc end. adhoc_local_commands(From, #jid{lserver = LServer} = _To, #adhoc_request{lang = Lang, node = Node, sessionid = SessionID, action = Action, xdata = XData} = Request) -> LNode = tokenize(Node), ActionIsExecute = lists:member(Action, [<<"">>, <<"execute">>, <<"complete">>]), if Action == <<"cancel">> -> adhoc:produce_response(Request, #adhoc_response{status = canceled}); XData == false, ActionIsExecute -> case get_form(LServer, LNode, Lang) of {result, Form} -> adhoc:produce_response(Request, #adhoc_response{status = executing, elements = Form}); {result, Status, Form} -> adhoc:produce_response(Request, #adhoc_response{status = Status, elements = Form}); {error, Error} -> {error, Error} end; XData /= false, ActionIsExecute -> case jlib:parse_xdata_submit(XData) of invalid -> {error, ?ERR_BAD_REQUEST}; Fields -> case catch set_form(From, LServer, LNode, Lang, Fields) of {result, Res} -> adhoc:produce_response(#adhoc_response{lang = Lang, node = Node, sessionid = SessionID, elements = Res, status = completed}); {'EXIT', _} -> {error, ?ERR_BAD_REQUEST}; {error, Error} -> {error, Error} end end; true -> {error, ?ERR_BAD_REQUEST} end. -define(TVFIELD(Type, Var, Val), #xmlel{name = <<"field">>, attrs = [{<<"type">>, Type}, {<<"var">>, Var}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Val}]}]}). -define(HFIELD(), ?TVFIELD(<<"hidden">>, <<"FORM_TYPE">>, (?NS_ADMIN))). -define(TLFIELD(Type, Label, Var), #xmlel{name = <<"field">>, attrs = [{<<"type">>, Type}, {<<"label">>, ?T(Lang, Label)}, {<<"var">>, Var}], children = []}). -define(XFIELD(Type, Label, Var, Val), #xmlel{name = <<"field">>, attrs = [{<<"type">>, Type}, {<<"label">>, ?T(Lang, Label)}, {<<"var">>, Var}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Val}]}]}). -define(XMFIELD(Type, Label, Var, Vals), #xmlel{name = <<"field">>, attrs = [{<<"type">>, Type}, {<<"label">>, ?T(Lang, Label)}, {<<"var">>, Var}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Val}]} || Val <- Vals]}). -define(TABLEFIELD(Table, Val), #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"list-single">>}, {<<"label">>, iolist_to_binary(atom_to_list(Table))}, {<<"var">>, iolist_to_binary(atom_to_list(Table))}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, iolist_to_binary(atom_to_list(Val))}]}, #xmlel{name = <<"option">>, attrs = [{<<"label">>, ?T(Lang, <<"RAM copy">>)}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, <<"ram_copies">>}]}]}, #xmlel{name = <<"option">>, attrs = [{<<"label">>, ?T(Lang, <<"RAM and disc copy">>)}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, <<"disc_copies">>}]}]}, #xmlel{name = <<"option">>, attrs = [{<<"label">>, ?T(Lang, <<"Disc only copy">>)}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, <<"disc_only_copies">>}]}]}, #xmlel{name = <<"option">>, attrs = [{<<"label">>, ?T(Lang, <<"Remote copy">>)}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, <<"unknown">>}]}]}]}). get_form(_Host, [<<"running nodes">>, ENode, <<"DB">>], Lang) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case rpc:call(Node, mnesia, system_info, [tables]) of {badrpc, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; Tables -> STables = lists:sort(Tables), {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Database Tables Configuration at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Choose storage type of tables">>)}]} | lists:map(fun (Table) -> case rpc:call(Node, mnesia, table_info, [Table, storage_type]) of {badrpc, _} -> ?TABLEFIELD(Table, unknown); Type -> ?TABLEFIELD(Table, Type) end end, STables)]}]} end end; get_form(Host, [<<"running nodes">>, ENode, <<"modules">>, <<"stop">>], Lang) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case rpc:call(Node, gen_mod, loaded_modules, [Host]) of {badrpc, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; Modules -> SModules = lists:sort(Modules), {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Stop Modules at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Choose modules to stop">>)}]} | lists:map(fun (M) -> S = jlib:atom_to_binary(M), ?XFIELD(<<"boolean">>, S, S, <<"0">>) end, SModules)]}]} end end; get_form(_Host, [<<"running nodes">>, ENode, <<"modules">>, <<"start">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Start Modules at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Enter list of {Module, [Options]}">>)}]}, ?XFIELD(<<"text-multi">>, <<"List of modules to start">>, <<"modules">>, <<"[].">>)]}]}; get_form(_Host, [<<"running nodes">>, ENode, <<"backup">>, <<"backup">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Backup to File at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Enter path to backup file">>)}]}, ?XFIELD(<<"text-single">>, <<"Path to File">>, <<"path">>, <<"">>)]}]}; get_form(_Host, [<<"running nodes">>, ENode, <<"backup">>, <<"restore">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Restore Backup from File at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Enter path to backup file">>)}]}, ?XFIELD(<<"text-single">>, <<"Path to File">>, <<"path">>, <<"">>)]}]}; get_form(_Host, [<<"running nodes">>, ENode, <<"backup">>, <<"textfile">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Dump Backup to Text File at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Enter path to text file">>)}]}, ?XFIELD(<<"text-single">>, <<"Path to File">>, <<"path">>, <<"">>)]}]}; get_form(_Host, [<<"running nodes">>, ENode, <<"import">>, <<"file">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Import User from File at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Enter path to jabberd14 spool file">>)}]}, ?XFIELD(<<"text-single">>, <<"Path to File">>, <<"path">>, <<"">>)]}]}; get_form(_Host, [<<"running nodes">>, ENode, <<"import">>, <<"dir">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Import Users from Dir at ">>))/binary, ENode/binary>>}]}, #xmlel{name = <<"instructions">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Enter path to jabberd14 spool dir">>)}]}, ?XFIELD(<<"text-single">>, <<"Path to Dir">>, <<"path">>, <<"">>)]}]}; get_form(_Host, [<<"running nodes">>, _ENode, <<"restart">>], Lang) -> Make_option = fun (LabelNum, LabelUnit, Value) -> #xmlel{name = <<"option">>, attrs = [{<<"label">>, <>}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Value}]}]} end, {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Restart Service">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"list-single">>}, {<<"label">>, ?T(Lang, <<"Time delay">>)}, {<<"var">>, <<"delay">>}], children = [Make_option(<<"">>, <<"immediately">>, <<"1">>), Make_option(<<"15 ">>, <<"seconds">>, <<"15">>), Make_option(<<"30 ">>, <<"seconds">>, <<"30">>), Make_option(<<"60 ">>, <<"seconds">>, <<"60">>), Make_option(<<"90 ">>, <<"seconds">>, <<"90">>), Make_option(<<"2 ">>, <<"minutes">>, <<"120">>), Make_option(<<"3 ">>, <<"minutes">>, <<"180">>), Make_option(<<"4 ">>, <<"minutes">>, <<"240">>), Make_option(<<"5 ">>, <<"minutes">>, <<"300">>), Make_option(<<"10 ">>, <<"minutes">>, <<"600">>), Make_option(<<"15 ">>, <<"minutes">>, <<"900">>), Make_option(<<"30 ">>, <<"minutes">>, <<"1800">>), #xmlel{name = <<"required">>, attrs = [], children = []}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"fixed">>}, {<<"label">>, ?T(Lang, <<"Send announcement to all online users " "on all hosts">>)}], children = []}, #xmlel{name = <<"field">>, attrs = [{<<"var">>, <<"subject">>}, {<<"type">>, <<"text-single">>}, {<<"label">>, ?T(Lang, <<"Subject">>)}], children = []}, #xmlel{name = <<"field">>, attrs = [{<<"var">>, <<"announcement">>}, {<<"type">>, <<"text-multi">>}, {<<"label">>, ?T(Lang, <<"Message body">>)}], children = []}]}]}; get_form(_Host, [<<"running nodes">>, _ENode, <<"shutdown">>], Lang) -> Make_option = fun (LabelNum, LabelUnit, Value) -> #xmlel{name = <<"option">>, attrs = [{<<"label">>, <>}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Value}]}]} end, {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Shut Down Service">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"list-single">>}, {<<"label">>, ?T(Lang, <<"Time delay">>)}, {<<"var">>, <<"delay">>}], children = [Make_option(<<"">>, <<"immediately">>, <<"1">>), Make_option(<<"15 ">>, <<"seconds">>, <<"15">>), Make_option(<<"30 ">>, <<"seconds">>, <<"30">>), Make_option(<<"60 ">>, <<"seconds">>, <<"60">>), Make_option(<<"90 ">>, <<"seconds">>, <<"90">>), Make_option(<<"2 ">>, <<"minutes">>, <<"120">>), Make_option(<<"3 ">>, <<"minutes">>, <<"180">>), Make_option(<<"4 ">>, <<"minutes">>, <<"240">>), Make_option(<<"5 ">>, <<"minutes">>, <<"300">>), Make_option(<<"10 ">>, <<"minutes">>, <<"600">>), Make_option(<<"15 ">>, <<"minutes">>, <<"900">>), Make_option(<<"30 ">>, <<"minutes">>, <<"1800">>), #xmlel{name = <<"required">>, attrs = [], children = []}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"fixed">>}, {<<"label">>, ?T(Lang, <<"Send announcement to all online users " "on all hosts">>)}], children = []}, #xmlel{name = <<"field">>, attrs = [{<<"var">>, <<"subject">>}, {<<"type">>, <<"text-single">>}, {<<"label">>, ?T(Lang, <<"Subject">>)}], children = []}, #xmlel{name = <<"field">>, attrs = [{<<"var">>, <<"announcement">>}, {<<"type">>, <<"text-multi">>}, {<<"label">>, ?T(Lang, <<"Message body">>)}], children = []}]}]}; get_form(Host, [<<"config">>, <<"acls">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Access Control List Configuration">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"text-multi">>}, {<<"label">>, ?T(Lang, <<"Access control lists">>)}, {<<"var">>, <<"acls">>}], children = lists:map(fun (S) -> #xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, S}]} end, str:tokens(iolist_to_binary(io_lib:format("~p.", [ets:select(acl, [{{acl, {'$1', '$2'}, '$3'}, [{'==', '$2', Host}], [{{acl, '$1', '$3'}}]}])])), <<"\n">>))}]}]}; get_form(Host, [<<"config">>, <<"access">>], Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Access Configuration">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"text-multi">>}, {<<"label">>, ?T(Lang, <<"Access rules">>)}, {<<"var">>, <<"access">>}], children = lists:map(fun (S) -> #xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, S}]} end, str:tokens(iolist_to_binary(io_lib:format("~p.", [ets:select(local_config, [{{local_config, {access, '$1', '$2'}, '$3'}, [{'==', '$2', Host}], [{{access, '$1', '$3'}}]}])])), <<"\n">>))}]}]}; get_form(_Host, ?NS_ADMINL(<<"add-user">>), Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Add User">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"jid-single">>}, {<<"label">>, ?T(Lang, <<"Jabber ID">>)}, {<<"var">>, <<"accountjid">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"text-private">>}, {<<"label">>, ?T(Lang, <<"Password">>)}, {<<"var">>, <<"password">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"text-private">>}, {<<"label">>, ?T(Lang, <<"Password Verification">>)}, {<<"var">>, <<"password-verify">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}]}]}; get_form(_Host, ?NS_ADMINL(<<"delete-user">>), Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Delete User">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"jid-multi">>}, {<<"label">>, ?T(Lang, <<"Jabber ID">>)}, {<<"var">>, <<"accountjids">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}]}]}; get_form(_Host, ?NS_ADMINL(<<"end-user-session">>), Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"End User Session">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"jid-single">>}, {<<"label">>, ?T(Lang, <<"Jabber ID">>)}, {<<"var">>, <<"accountjid">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}]}]}; get_form(_Host, ?NS_ADMINL(<<"get-user-password">>), Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Get User Password">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"jid-single">>}, {<<"label">>, ?T(Lang, <<"Jabber ID">>)}, {<<"var">>, <<"accountjid">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}]}]}; get_form(_Host, ?NS_ADMINL(<<"change-user-password">>), Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Get User Password">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"jid-single">>}, {<<"label">>, ?T(Lang, <<"Jabber ID">>)}, {<<"var">>, <<"accountjid">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"text-private">>}, {<<"label">>, ?T(Lang, <<"Password">>)}, {<<"var">>, <<"password">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}]}]}; get_form(_Host, ?NS_ADMINL(<<"get-user-lastlogin">>), Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Get User Last Login Time">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"jid-single">>}, {<<"label">>, ?T(Lang, <<"Jabber ID">>)}, {<<"var">>, <<"accountjid">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}]}]}; get_form(_Host, ?NS_ADMINL(<<"user-stats">>), Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, ?T(Lang, <<"Get User Statistics">>)}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"jid-single">>}, {<<"label">>, ?T(Lang, <<"Jabber ID">>)}, {<<"var">>, <<"accountjid">>}], children = [#xmlel{name = <<"required">>, attrs = [], children = []}]}]}]}; get_form(Host, ?NS_ADMINL(<<"get-registered-users-num">>), Lang) -> Num = list_to_binary( io_lib:format("~p", [ejabberd_auth:get_vh_registered_users_number(Host)])), {result, completed, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"text-single">>}, {<<"label">>, ?T(Lang, <<"Number of registered users">>)}, {<<"var">>, <<"registeredusersnum">>}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Num}]}]}]}]}; get_form(Host, ?NS_ADMINL(<<"get-online-users-num">>), Lang) -> Num = list_to_binary( io_lib:format("~p", [length(ejabberd_sm:get_vh_session_list(Host))])), {result, completed, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"text-single">>}, {<<"label">>, ?T(Lang, <<"Number of online users">>)}, {<<"var">>, <<"onlineusersnum">>}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Num}]}]}]}]}; get_form(_Host, _, _Lang) -> {error, ?ERR_SERVICE_UNAVAILABLE}. set_form(_From, _Host, [<<"running nodes">>, ENode, <<"DB">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> lists:foreach(fun ({SVar, SVals}) -> Table = jlib:binary_to_atom(SVar), Type = case SVals of [<<"unknown">>] -> unknown; [<<"ram_copies">>] -> ram_copies; [<<"disc_copies">>] -> disc_copies; [<<"disc_only_copies">>] -> disc_only_copies; _ -> false end, if Type == false -> ok; Type == unknown -> mnesia:del_table_copy(Table, Node); true -> case mnesia:add_table_copy(Table, Node, Type) of {aborted, _} -> mnesia:change_table_copy_type(Table, Node, Type); _ -> ok end end end, XData), {result, []} end; set_form(_From, Host, [<<"running nodes">>, ENode, <<"modules">>, <<"stop">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> lists:foreach(fun ({Var, Vals}) -> case Vals of [<<"1">>] -> Module = jlib:binary_to_atom(Var), rpc:call(Node, gen_mod, stop_module, [Host, Module]); _ -> ok end end, XData), {result, []} end; set_form(_From, Host, [<<"running nodes">>, ENode, <<"modules">>, <<"start">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case lists:keysearch(<<"modules">>, 1, XData) of false -> {error, ?ERR_BAD_REQUEST}; {value, {_, Strings}} -> String = lists:foldl(fun (S, Res) -> <> end, <<"">>, Strings), case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, Modules} -> lists:foreach(fun ({Module, Args}) -> rpc:call(Node, gen_mod, start_module, [Host, Module, Args]) end, Modules), {result, []}; _ -> {error, ?ERR_BAD_REQUEST} end; _ -> {error, ?ERR_BAD_REQUEST} end end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"backup">>, <<"backup">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case lists:keysearch(<<"path">>, 1, XData) of false -> {error, ?ERR_BAD_REQUEST}; {value, {_, [String]}} -> case rpc:call(Node, mnesia, backup, [String]) of {badrpc, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; {error, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; _ -> {result, []} end; _ -> {error, ?ERR_BAD_REQUEST} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"backup">>, <<"restore">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case lists:keysearch(<<"path">>, 1, XData) of false -> {error, ?ERR_BAD_REQUEST}; {value, {_, [String]}} -> case rpc:call(Node, ejabberd_admin, restore, [String]) of {badrpc, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; {error, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; _ -> {result, []} end; _ -> {error, ?ERR_BAD_REQUEST} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"backup">>, <<"textfile">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case lists:keysearch(<<"path">>, 1, XData) of false -> {error, ?ERR_BAD_REQUEST}; {value, {_, [String]}} -> case rpc:call(Node, ejabberd_admin, dump_to_textfile, [String]) of {badrpc, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; {error, _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR}; _ -> {result, []} end; _ -> {error, ?ERR_BAD_REQUEST} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"import">>, <<"file">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case lists:keysearch(<<"path">>, 1, XData) of false -> {error, ?ERR_BAD_REQUEST}; {value, {_, [String]}} -> rpc:call(Node, jd2ejd, import_file, [String]), {result, []}; _ -> {error, ?ERR_BAD_REQUEST} end end; set_form(_From, _Host, [<<"running nodes">>, ENode, <<"import">>, <<"dir">>], _Lang, XData) -> case search_running_node(ENode) of false -> {error, ?ERR_ITEM_NOT_FOUND}; Node -> case lists:keysearch(<<"path">>, 1, XData) of false -> {error, ?ERR_BAD_REQUEST}; {value, {_, [String]}} -> rpc:call(Node, jd2ejd, import_dir, [String]), {result, []}; _ -> {error, ?ERR_BAD_REQUEST} end end; set_form(From, Host, [<<"running nodes">>, ENode, <<"restart">>], _Lang, XData) -> stop_node(From, Host, ENode, restart, XData); set_form(From, Host, [<<"running nodes">>, ENode, <<"shutdown">>], _Lang, XData) -> stop_node(From, Host, ENode, stop, XData); set_form(_From, Host, [<<"config">>, <<"acls">>], _Lang, XData) -> case lists:keysearch(<<"acls">>, 1, XData) of {value, {_, Strings}} -> String = lists:foldl(fun (S, Res) -> <> end, <<"">>, Strings), case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, ACLs} -> acl:add_list(Host, ACLs, true), {result, []}; _ -> {error, ?ERR_BAD_REQUEST} end; _ -> {error, ?ERR_BAD_REQUEST} end; _ -> {error, ?ERR_BAD_REQUEST} end; set_form(_From, Host, [<<"config">>, <<"access">>], _Lang, XData) -> SetAccess = fun (Rs) -> mnesia:transaction(fun () -> Os = mnesia:select(local_config, [{{local_config, {access, '$1', '$2'}, '$3'}, [{'==', '$2', Host}], ['$_']}]), lists:foreach(fun (O) -> mnesia:delete_object(O) end, Os), lists:foreach(fun ({access, Name, Rules}) -> mnesia:write({local_config, {access, Name, Host}, Rules}) end, Rs) end) end, case lists:keysearch(<<"access">>, 1, XData) of {value, {_, Strings}} -> String = lists:foldl(fun (S, Res) -> <> end, <<"">>, Strings), case erl_scan:string(binary_to_list(String)) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, Rs} -> case SetAccess(Rs) of {atomic, _} -> {result, []}; _ -> {error, ?ERR_BAD_REQUEST} end; _ -> {error, ?ERR_BAD_REQUEST} end; _ -> {error, ?ERR_BAD_REQUEST} end; _ -> {error, ?ERR_BAD_REQUEST} end; set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), Password = get_value(<<"password">>, XData), Password = get_value(<<"password-verify">>, XData), AccountJID = jlib:string_to_jid(AccountString), User = AccountJID#jid.luser, Server = AccountJID#jid.lserver, true = lists:member(Server, ?MYHOSTS), true = Server == Host orelse get_permission_level(From) == global, ejabberd_auth:try_register(User, Server, Password), {result, []}; set_form(From, Host, ?NS_ADMINL(<<"delete-user">>), _Lang, XData) -> AccountStringList = get_values(<<"accountjids">>, XData), [_ | _] = AccountStringList, ASL2 = lists:map(fun (AccountString) -> JID = jlib:string_to_jid(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, true = ejabberd_auth:is_user_exists(User, Server), {User, Server} end, AccountStringList), [ejabberd_auth:remove_user(User, Server) || {User, Server} <- ASL2], {result, []}; set_form(From, Host, ?NS_ADMINL(<<"end-user-session">>), _Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jlib:string_to_jid(AccountString), LUser = JID#jid.luser, LServer = JID#jid.lserver, true = LServer == Host orelse get_permission_level(From) == global, case JID#jid.lresource of <<>> -> SIDs = mnesia:dirty_select(session, [{#session{sid = '$1', usr = {LUser, LServer, '_'}, _ = '_'}, [], ['$1']}]), [Pid ! replaced || {_, Pid} <- SIDs]; R -> [{_, Pid}] = mnesia:dirty_select(session, [{#session{sid = '$1', usr = {LUser, LServer, R}, _ = '_'}, [], ['$1']}]), Pid ! replaced end, {result, []}; set_form(From, Host, ?NS_ADMINL(<<"get-user-password">>), Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jlib:string_to_jid(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, Password = ejabberd_auth:get_password(User, Server), true = is_binary(Password), {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), ?XFIELD(<<"jid-single">>, <<"Jabber ID">>, <<"accountjid">>, AccountString), ?XFIELD(<<"text-single">>, <<"Password">>, <<"password">>, Password)]}]}; set_form(From, Host, ?NS_ADMINL(<<"change-user-password">>), _Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), Password = get_value(<<"password">>, XData), JID = jlib:string_to_jid(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, true = ejabberd_auth:is_user_exists(User, Server), ejabberd_auth:set_password(User, Server, Password), {result, []}; set_form(From, Host, ?NS_ADMINL(<<"get-user-lastlogin">>), Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jlib:string_to_jid(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, FLast = case ejabberd_sm:get_user_resources(User, Server) of [] -> _US = {User, Server}, case get_last_info(User, Server) of not_found -> ?T(Lang, <<"Never">>); {ok, Timestamp, _Status} -> Shift = Timestamp, TimeStamp = {Shift div 1000000, Shift rem 1000000, 0}, {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_local_time(TimeStamp), iolist_to_binary(io_lib:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Year, Month, Day, Hour, Minute, Second])) end; _ -> ?T(Lang, <<"Online">>) end, {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}], children = [?HFIELD(), ?XFIELD(<<"jid-single">>, <<"Jabber ID">>, <<"accountjid">>, AccountString), ?XFIELD(<<"text-single">>, <<"Last login">>, <<"lastlogin">>, FLast)]}]}; set_form(From, Host, ?NS_ADMINL(<<"user-stats">>), Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), JID = jlib:string_to_jid(AccountString), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse get_permission_level(From) == global, Resources = ejabberd_sm:get_user_resources(User, Server), IPs1 = [ejabberd_sm:get_user_ip(User, Server, Resource) || Resource <- Resources], IPs = [<<(jlib:ip_to_list(IP))/binary, ":", (jlib:integer_to_binary(Port))/binary>> || {IP, Port} <- IPs1], Items = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]), Rostersize = jlib:integer_to_binary(erlang:length(Items)), {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), ?XFIELD(<<"jid-single">>, <<"Jabber ID">>, <<"accountjid">>, AccountString), ?XFIELD(<<"text-single">>, <<"Roster size">>, <<"rostersize">>, Rostersize), ?XMFIELD(<<"text-multi">>, <<"IP addresses">>, <<"ipaddresses">>, IPs), ?XMFIELD(<<"text-multi">>, <<"Resources">>, <<"onlineresources">>, Resources)]}]}; set_form(_From, _Host, _, _Lang, _XData) -> {error, ?ERR_SERVICE_UNAVAILABLE}. get_value(Field, XData) -> hd(get_values(Field, XData)). get_values(Field, XData) -> {value, {_, ValueList}} = lists:keysearch(Field, 1, XData), ValueList. search_running_node(SNode) -> search_running_node(SNode, mnesia:system_info(running_db_nodes)). 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) end. stop_node(From, Host, ENode, Action, XData) -> Delay = jlib:binary_to_integer(get_value(<<"delay">>, XData)), Subject = case get_value(<<"subject">>, XData) of <<"">> -> []; S -> [#xmlel{name = <<"field">>, attrs = [{<<"var">>, <<"subject">>}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, S}]}]}] end, Announcement = case get_values(<<"announcement">>, XData) of [] -> []; As -> [#xmlel{name = <<"field">>, attrs = [{<<"var">>, <<"body">>}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, Line}]} || Line <- As]}] end, case Subject ++ Announcement of [] -> ok; SubEls -> Request = #adhoc_request{node = ?NS_ADMINX(<<"announce-allhosts">>), action = <<"complete">>, xdata = #xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, <<"jabber:x:data">>}, {<<"type">>, <<"submit">>}], children = SubEls}, others = [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, <<"jabber:x:data">>}, {<<"type">>, <<"submit">>}], children = SubEls}]}, To = jlib:make_jid(<<"">>, Host, <<"">>), mod_announce:announce_commands(empty, From, To, Request) end, Time = timer:seconds(Delay), Node = jlib:binary_to_atom(ENode), {ok, _} = timer:apply_after(Time, rpc, call, [Node, init, Action, []]), {result, []}. get_last_info(User, Server) -> case gen_mod:is_loaded(Server, mod_last) of true -> mod_last:get_last_info(User, Server); false -> not_found end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% adhoc_sm_commands(_Acc, From, #jid{user = User, server = Server, lserver = LServer} = _To, #adhoc_request{lang = Lang, node = <<"config">>, action = Action, xdata = XData} = Request) -> case acl:match_rule(LServer, configure, From) of deny -> {error, ?ERR_FORBIDDEN}; allow -> ActionIsExecute = lists:member(Action, [<<"">>, <<"execute">>, <<"complete">>]), if Action == <<"cancel">> -> adhoc:produce_response(Request, #adhoc_response{status = canceled}); XData == false, ActionIsExecute -> case get_sm_form(User, Server, <<"config">>, Lang) of {result, Form} -> adhoc:produce_response(Request, #adhoc_response{status = executing, elements = Form}); {error, Error} -> {error, Error} end; XData /= false, ActionIsExecute -> case jlib:parse_xdata_submit(XData) of invalid -> {error, ?ERR_BAD_REQUEST}; Fields -> set_sm_form(User, Server, <<"config">>, Request, Fields) end; true -> {error, ?ERR_BAD_REQUEST} end end; adhoc_sm_commands(Acc, _From, _To, _Request) -> Acc. get_sm_form(User, Server, <<"config">>, Lang) -> {result, [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}], children = [?HFIELD(), #xmlel{name = <<"title">>, attrs = [], children = [{xmlcdata, <<(?T(Lang, <<"Administration of ">>))/binary, User/binary>>}]}, #xmlel{name = <<"field">>, attrs = [{<<"type">>, <<"list-single">>}, {<<"label">>, ?T(Lang, <<"Action on user">>)}, {<<"var">>, <<"action">>}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, <<"edit">>}]}, #xmlel{name = <<"option">>, attrs = [{<<"label">>, ?T(Lang, <<"Edit Properties">>)}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, <<"edit">>}]}]}, #xmlel{name = <<"option">>, attrs = [{<<"label">>, ?T(Lang, <<"Remove User">>)}], children = [#xmlel{name = <<"value">>, attrs = [], children = [{xmlcdata, <<"remove">>}]}]}]}, ?XFIELD(<<"text-private">>, <<"Password">>, <<"password">>, (ejabberd_auth:get_password_s(User, Server)))]}]}; get_sm_form(_User, _Server, _Node, _Lang) -> {error, ?ERR_SERVICE_UNAVAILABLE}. set_sm_form(User, Server, <<"config">>, #adhoc_request{lang = Lang, node = Node, sessionid = SessionID}, XData) -> Response = #adhoc_response{lang = Lang, node = Node, sessionid = SessionID, status = completed}, case lists:keysearch(<<"action">>, 1, XData) of {value, {_, [<<"edit">>]}} -> case lists:keysearch(<<"password">>, 1, XData) of {value, {_, [Password]}} -> ejabberd_auth:set_password(User, Server, Password), adhoc:produce_response(Response); _ -> {error, ?ERR_NOT_ACCEPTABLE} end; {value, {_, [<<"remove">>]}} -> catch ejabberd_auth:remove_user(User, Server), adhoc:produce_response(Response); _ -> {error, ?ERR_NOT_ACCEPTABLE} end; set_sm_form(_User, _Server, _Node, _Request, _Fields) -> {error, ?ERR_SERVICE_UNAVAILABLE}.