mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-26 16:26:24 +01:00
814b80c644
Don't set the PID to 'undefined' when a session goes offline, as this looses the information which node created the session table entry. Fixes #1196.
2233 lines
70 KiB
Erlang
2233 lines
70 KiB
Erlang
%%%----------------------------------------------------------------------
|
|
%%% File : mod_configure.erl
|
|
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
|
%%% Purpose : Support for online configuration of ejabberd
|
|
%%% Created : 19 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
|
%%%
|
|
%%%
|
|
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
|
%%%
|
|
%%% This program is free software; you can redistribute it and/or
|
|
%%% modify it under the terms of the GNU General Public License as
|
|
%%% published by the Free Software Foundation; either version 2 of the
|
|
%%% License, or (at your option) any later version.
|
|
%%%
|
|
%%% This program is distributed in the hope that it will be useful,
|
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
%%% General Public License for more details.
|
|
%%%
|
|
%%% You should have received a copy of the GNU General Public License along
|
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
%%%
|
|
%%%----------------------------------------------------------------------
|
|
|
|
-module(mod_configure).
|
|
|
|
-author('alexey@process-one.net').
|
|
|
|
-protocol({xep, 133, '1.1'}).
|
|
|
|
-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, mod_opt_type/1,
|
|
depends/2]).
|
|
|
|
-include("ejabberd.hrl").
|
|
-include("logger.hrl").
|
|
|
|
-include("jlib.hrl").
|
|
-include("ejabberd_sm.hrl").
|
|
-include("adhoc.hrl").
|
|
|
|
-define(T(Lang, Text), translate:translate(Lang, Text)).
|
|
|
|
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).
|
|
|
|
depends(_Host, _Opts) ->
|
|
[{mod_adhoc, hard}, {mod_last, soft}].
|
|
|
|
%%%-----------------------------------------------------------------------
|
|
|
|
-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">>, 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, Lang),
|
|
case Allow of
|
|
deny -> {error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
|
|
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], Lang);
|
|
_ -> 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, [], Lang);
|
|
[<<"user">>] -> ?INFO_RESULT(Allow, [], Lang);
|
|
[<<"online users">>] -> ?INFO_RESULT(Allow, [], Lang);
|
|
[<<"all users">>] -> ?INFO_RESULT(Allow, [], Lang);
|
|
[<<"all users">>, <<$@, _/binary>>] ->
|
|
?INFO_RESULT(Allow, [], Lang);
|
|
[<<"outgoing s2s">> | _] -> ?INFO_RESULT(Allow, [], Lang);
|
|
[<<"running nodes">>] -> ?INFO_RESULT(Allow, [], Lang);
|
|
[<<"stopped nodes">>] -> ?INFO_RESULT(Allow, [], Lang);
|
|
[<<"running nodes">>, _ENode] ->
|
|
?INFO_RESULT(Allow, [?NS_STATS], Lang);
|
|
[<<"running nodes">>, _ENode, <<"DB">>] ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
[<<"running nodes">>, _ENode, <<"modules">>] ->
|
|
?INFO_RESULT(Allow, [], Lang);
|
|
[<<"running nodes">>, _ENode, <<"modules">>, _] ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
[<<"running nodes">>, _ENode, <<"backup">>] ->
|
|
?INFO_RESULT(Allow, [], Lang);
|
|
[<<"running nodes">>, _ENode, <<"backup">>, _] ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
[<<"running nodes">>, _ENode, <<"import">>] ->
|
|
?INFO_RESULT(Allow, [], Lang);
|
|
[<<"running nodes">>, _ENode, <<"import">>, _] ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
[<<"running nodes">>, _ENode, <<"restart">>] ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
[<<"running nodes">>, _ENode, <<"shutdown">>] ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
[<<"config">>, _] ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"add-user">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"delete-user">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"end-user-session">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"get-user-password">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"change-user-password">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"get-user-lastlogin">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"user-stats">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"get-registered-users-num">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
?NS_ADMINL(<<"get-online-users-num">>) ->
|
|
?INFO_RESULT(Allow, [?NS_COMMANDS], Lang);
|
|
_ -> 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">>, 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, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
|
|
_ -> 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">>,
|
|
<<User/binary, "@", Server/binary, "/",
|
|
R/binary>>},
|
|
{<<"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 = fxml: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 = fxml:get_tag_attr_s(<<"jid">>,
|
|
N),
|
|
Nd = fxml: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,
|
|
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}, [],
|
|
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),
|
|
Err = ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>),
|
|
case LNode of
|
|
[<<"config">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"user">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"online users">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"all users">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"all users">>, <<$@, _/binary>>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"outgoing s2s">> | _] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"stopped nodes">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"DB">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"modules">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"modules">>, _] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"backup">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"backup">>, _] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"import">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"import">>, _] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"restart">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"running nodes">>, _ENode, <<"shutdown">>] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
[<<"config">>, _] ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"add-user">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"delete-user">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"end-user-session">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"get-user-password">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"change-user-password">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"get-user-lastlogin">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"user-stats">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"get-registered-users-num">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
?NS_ADMINL(<<"get-online-users-num">>) ->
|
|
?ITEMS_RESULT(Allow, LNode, {error, Err});
|
|
_ -> 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) ->
|
|
Users = ejabberd_auth:get_vh_registered_users(Host),
|
|
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">>,
|
|
<<U/binary, "@",
|
|
S/binary>>},
|
|
{<<"name">>,
|
|
<<U/binary, "@",
|
|
S/binary>>}],
|
|
children = []}
|
|
end,
|
|
Sub)
|
|
end
|
|
of
|
|
{'EXIT', _Reason} -> ?ERR_NOT_ACCEPTABLE;
|
|
Res -> {result, Res}
|
|
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">>,
|
|
<<U/binary, "@", S/binary, "/",
|
|
R/binary>>},
|
|
{<<"name">>,
|
|
<<U/binary, "@", S/binary>>}],
|
|
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">>,
|
|
<<U/binary, "@", S/binary>>},
|
|
{<<"name">>,
|
|
<<U/binary, "@", S/binary>>}],
|
|
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 = <<FU/binary, "@", FS/binary, " -- ",
|
|
LU/binary, "@", LS/binary>>,
|
|
#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, Lang),
|
|
case acl:match_rule(LServerOrGlobal, configure, From) of
|
|
deny -> {error, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
|
|
allow -> adhoc_local_commands(From, To, Request)
|
|
end).
|
|
|
|
adhoc_local_commands(Acc, From,
|
|
#jid{lserver = LServer} = To,
|
|
#adhoc_request{node = Node, lang = Lang} = Request) ->
|
|
LNode = tokenize(Node),
|
|
case LNode of
|
|
[<<"running nodes">>, _ENode, <<"DB">>] ->
|
|
?COMMANDS_RESULT(global, From, To, Request, Lang);
|
|
[<<"running nodes">>, _ENode, <<"modules">>, _] ->
|
|
?COMMANDS_RESULT(LServer, From, To, Request, Lang);
|
|
[<<"running nodes">>, _ENode, <<"backup">>, _] ->
|
|
?COMMANDS_RESULT(global, From, To, Request, Lang);
|
|
[<<"running nodes">>, _ENode, <<"import">>, _] ->
|
|
?COMMANDS_RESULT(global, From, To, Request, Lang);
|
|
[<<"running nodes">>, _ENode, <<"restart">>] ->
|
|
?COMMANDS_RESULT(global, From, To, Request, Lang);
|
|
[<<"running nodes">>, _ENode, <<"shutdown">>] ->
|
|
?COMMANDS_RESULT(global, From, To, Request, Lang);
|
|
[<<"config">>, _] ->
|
|
?COMMANDS_RESULT(LServer, From, To, Request, Lang);
|
|
?NS_ADMINL(_) ->
|
|
?COMMANDS_RESULT(LServer, From, To, Request, Lang);
|
|
_ -> 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, ?ERRT_BAD_REQUEST(Lang, <<"Incorrect data form">>)};
|
|
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, ?ERRT_BAD_REQUEST(Lang, <<"Incorrect action or data form">>)}
|
|
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 ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of
|
|
{badrpc, Reason} ->
|
|
?ERROR_MSG("RPC call mnesia:system_info(tables) on node "
|
|
"~s failed: ~p", [Node, 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 ejabberd_cluster: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 ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case ejabberd_cluster:call(Node, gen_mod, loaded_modules, [Host]) of
|
|
{badrpc, Reason} ->
|
|
?ERROR_MSG("RPC call gen_mod:loaded_modules(~s) on node "
|
|
"~s failed: ~p", [Host, Node, 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">>,
|
|
<<LabelNum/binary,
|
|
(?T(Lang, LabelUnit))/binary>>}],
|
|
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">>,
|
|
<<LabelNum/binary,
|
|
(?T(Lang, LabelUnit))/binary>>}],
|
|
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(access,
|
|
[{{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 ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
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 ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
lists:foreach(fun ({Var, Vals}) ->
|
|
case Vals of
|
|
[<<"1">>] ->
|
|
Module = jlib:binary_to_atom(Var),
|
|
ejabberd_cluster: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 ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case lists:keysearch(<<"modules">>, 1, XData) of
|
|
false ->
|
|
Txt = <<"No 'modules' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
|
{value, {_, Strings}} ->
|
|
String = lists:foldl(fun (S, Res) ->
|
|
<<Res/binary, S/binary, "\n">>
|
|
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}) ->
|
|
ejabberd_cluster:call(Node, gen_mod,
|
|
start_module,
|
|
[Host, Module, Args])
|
|
end,
|
|
Modules),
|
|
{result, []};
|
|
_ -> {error, ?ERRT_BAD_REQUEST(Lang, <<"Parse failed">>)}
|
|
end;
|
|
_ -> {error, ?ERRT_BAD_REQUEST(Lang, <<"Scan failed">>)}
|
|
end
|
|
end
|
|
end;
|
|
set_form(_From, _Host,
|
|
[<<"running nodes">>, ENode, <<"backup">>,
|
|
<<"backup">>],
|
|
Lang, XData) ->
|
|
case search_running_node(ENode) of
|
|
false ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case lists:keysearch(<<"path">>, 1, XData) of
|
|
false ->
|
|
Txt = <<"No 'path' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
|
{value, {_, [String]}} ->
|
|
case ejabberd_cluster:call(Node, mnesia, backup, [String]) of
|
|
{badrpc, Reason} ->
|
|
?ERROR_MSG("RPC call mnesia:backup(~s) to node ~s "
|
|
"failed: ~p", [String, Node, Reason]),
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
|
{error, Reason} ->
|
|
?ERROR_MSG("RPC call mnesia:backup(~s) to node ~s "
|
|
"failed: ~p", [String, Node, Reason]),
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
|
_ -> {result, []}
|
|
end;
|
|
_ ->
|
|
Txt = <<"Incorrect value of 'path' in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
end
|
|
end;
|
|
set_form(_From, _Host,
|
|
[<<"running nodes">>, ENode, <<"backup">>,
|
|
<<"restore">>],
|
|
Lang, XData) ->
|
|
case search_running_node(ENode) of
|
|
false ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case lists:keysearch(<<"path">>, 1, XData) of
|
|
false ->
|
|
Txt = <<"No 'path' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
|
{value, {_, [String]}} ->
|
|
case ejabberd_cluster:call(Node, ejabberd_admin, restore, [String])
|
|
of
|
|
{badrpc, Reason} ->
|
|
?ERROR_MSG("RPC call ejabberd_admin:restore(~s) to node "
|
|
"~s failed: ~p", [String, Node, Reason]),
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
|
{error, Reason} ->
|
|
?ERROR_MSG("RPC call ejabberd_admin:restore(~s) to node "
|
|
"~s failed: ~p", [String, Node, Reason]),
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
|
_ -> {result, []}
|
|
end;
|
|
_ ->
|
|
Txt = <<"Incorrect value of 'path' in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
end
|
|
end;
|
|
set_form(_From, _Host,
|
|
[<<"running nodes">>, ENode, <<"backup">>,
|
|
<<"textfile">>],
|
|
Lang, XData) ->
|
|
case search_running_node(ENode) of
|
|
false ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case lists:keysearch(<<"path">>, 1, XData) of
|
|
false ->
|
|
Txt = <<"No 'path' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
|
{value, {_, [String]}} ->
|
|
case ejabberd_cluster:call(Node, ejabberd_admin, dump_to_textfile,
|
|
[String])
|
|
of
|
|
{badrpc, Reason} ->
|
|
?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~s) "
|
|
"to node ~s failed: ~p", [String, Node, Reason]),
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
|
{error, Reason} ->
|
|
?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~s) "
|
|
"to node ~s failed: ~p", [String, Node, Reason]),
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
|
_ -> {result, []}
|
|
end;
|
|
_ ->
|
|
Txt = <<"Incorrect value of 'path' in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
end
|
|
end;
|
|
set_form(_From, _Host,
|
|
[<<"running nodes">>, ENode, <<"import">>, <<"file">>],
|
|
Lang, XData) ->
|
|
case search_running_node(ENode) of
|
|
false ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case lists:keysearch(<<"path">>, 1, XData) of
|
|
false ->
|
|
Txt = <<"No 'path' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
|
{value, {_, [String]}} ->
|
|
ejabberd_cluster:call(Node, jd2ejd, import_file, [String]),
|
|
{result, []};
|
|
_ ->
|
|
Txt = <<"Incorrect value of 'path' in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
end
|
|
end;
|
|
set_form(_From, _Host,
|
|
[<<"running nodes">>, ENode, <<"import">>, <<"dir">>],
|
|
Lang, XData) ->
|
|
case search_running_node(ENode) of
|
|
false ->
|
|
Txt = <<"No running node found">>,
|
|
{error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)};
|
|
Node ->
|
|
case lists:keysearch(<<"path">>, 1, XData) of
|
|
false ->
|
|
Txt = <<"No 'path' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
|
{value, {_, [String]}} ->
|
|
ejabberd_cluster:call(Node, jd2ejd, import_dir, [String]),
|
|
{result, []};
|
|
_ ->
|
|
Txt = <<"Incorrect value of 'path' in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
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) ->
|
|
<<Res/binary, S/binary, "\n">>
|
|
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, ?ERRT_BAD_REQUEST(Lang, <<"Parse failed">>)}
|
|
end;
|
|
_ -> {error, ?ERRT_BAD_REQUEST(Lang, <<"Scan failed">>)}
|
|
end;
|
|
_ ->
|
|
Txt = <<"No 'acls' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
end;
|
|
set_form(_From, Host, [<<"config">>, <<"access">>],
|
|
Lang, XData) ->
|
|
SetAccess = fun (Rs) ->
|
|
mnesia:transaction(fun () ->
|
|
Os = mnesia:select(access,
|
|
[{{access,
|
|
{'$1',
|
|
'$2'},
|
|
'$3'},
|
|
[{'==',
|
|
'$2',
|
|
Host}],
|
|
['$_']}]),
|
|
lists:foreach(fun (O) ->
|
|
mnesia:delete_object(O)
|
|
end,
|
|
Os),
|
|
lists:foreach(fun ({access,
|
|
Name,
|
|
Rules}) ->
|
|
mnesia:write({access,
|
|
{Name,
|
|
Host},
|
|
Rules})
|
|
end,
|
|
Rs)
|
|
end)
|
|
end,
|
|
case lists:keysearch(<<"access">>, 1, XData) of
|
|
{value, {_, Strings}} ->
|
|
String = lists:foldl(fun (S, Res) ->
|
|
<<Res/binary, S/binary, "\n">>
|
|
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, ?ERRT_BAD_REQUEST(Lang, <<"Parse failed">>)}
|
|
end;
|
|
_ -> {error, ?ERRT_BAD_REQUEST(Lang, <<"Scan failed">>)}
|
|
end;
|
|
_ ->
|
|
Txt = <<"No 'access' found in data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
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 = jid:from_string(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 = jid:from_string(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 = jid:from_string(AccountString),
|
|
LUser = JID#jid.luser,
|
|
LServer = JID#jid.lserver,
|
|
true = LServer == Host orelse
|
|
get_permission_level(From) == global,
|
|
Xmlelement = ?SERRT_POLICY_VIOLATION(Lang, <<"has been kicked">>),
|
|
case JID#jid.lresource of
|
|
<<>> ->
|
|
SIs = mnesia:dirty_select(session,
|
|
[{#session{usr = {LUser, LServer, '_'},
|
|
sid = '$1',
|
|
info = '$2',
|
|
_ = '_'},
|
|
[], [{{'$1', '$2'}}]}]),
|
|
Pids = [P || {{_, P}, Info} <- SIs,
|
|
not proplists:get_bool(offline, Info)],
|
|
lists:foreach(fun(Pid) ->
|
|
Pid ! {kick, kicked_by_admin, Xmlelement}
|
|
end, Pids);
|
|
R ->
|
|
[{{_, Pid}, Info}] = mnesia:dirty_select(
|
|
session,
|
|
[{#session{usr = {LUser, LServer, R},
|
|
sid = '$1',
|
|
info = '$2',
|
|
_ = '_'},
|
|
[], [{{'$1', '$2'}}]}]),
|
|
case proplists:get_bool(offline, Info) of
|
|
true -> ok;
|
|
false -> Pid ! {kick, kicked_by_admin, Xmlelement}
|
|
end
|
|
end,
|
|
{result, []};
|
|
set_form(From, Host,
|
|
?NS_ADMINL(<<"get-user-password">>), Lang, XData) ->
|
|
AccountString = get_value(<<"accountjid">>, XData),
|
|
JID = jid:from_string(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 = jid:from_string(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 = jid:from_string(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
|
|
[] ->
|
|
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 = jid:from_string(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">>,
|
|
?NS_XDATA},
|
|
{<<"type">>, <<"submit">>}],
|
|
children = SubEls},
|
|
others =
|
|
[#xmlel{name = <<"x">>,
|
|
attrs =
|
|
[{<<"xmlns">>,
|
|
?NS_XDATA},
|
|
{<<"type">>, <<"submit">>}],
|
|
children = SubEls}]},
|
|
To = jid:make(<<"">>, 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, ?ERRT_FORBIDDEN(Lang, <<"Denied by ACL">>)};
|
|
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 ->
|
|
Txt = <<"Incorrect data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
|
Fields ->
|
|
set_sm_form(User, Server, <<"config">>, Request, Fields)
|
|
end;
|
|
true ->
|
|
Txt = <<"Incorrect action or data form">>,
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
|
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);
|
|
_ ->
|
|
Txt = <<"No 'password' found in data form">>,
|
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, Txt)}
|
|
end;
|
|
{value, {_, [<<"remove">>]}} ->
|
|
catch ejabberd_auth:remove_user(User, Server),
|
|
adhoc:produce_response(Response);
|
|
_ ->
|
|
Txt = <<"Incorrect value of 'action' in data form">>,
|
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, Txt)}
|
|
end;
|
|
set_sm_form(_User, _Server, _Node, _Request, _Fields) ->
|
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
|
|
|
mod_opt_type(_) -> [].
|