mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-20 17:27:00 +01:00
* src/mod_disco.erl: Functions register_sm_feature and
register_sm_node replaced with hooks (thanks to Sergei Golovan) * src/mod_vcard.erl: * src/mod_vcard_ldap.erl: * src/mod_disco.erl: Now mod_disco doesn't depend on mod_configure (thanks to Sergei Golovan) * src/mod_configure.erl: Likewise SVN Revision: 408
This commit is contained in:
parent
06a0e1df14
commit
83191198a1
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
|||||||
|
2005-09-04 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
|
* src/mod_disco.erl: Functions register_sm_feature and
|
||||||
|
register_sm_node replaced with hooks (thanks to Sergei Golovan)
|
||||||
|
* src/mod_vcard.erl:
|
||||||
|
* src/mod_vcard_ldap.erl:
|
||||||
|
|
||||||
|
* src/mod_disco.erl: Now mod_disco doesn't depend on mod_configure
|
||||||
|
(thanks to Sergei Golovan)
|
||||||
|
* src/mod_configure.erl: Likewise
|
||||||
|
|
||||||
2005-08-29 Alexey Shchepin <alexey@sevcom.net>
|
2005-08-29 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
* src/ejd2odbc.erl: Converter from mnesia to ODBC
|
* src/ejd2odbc.erl: Converter from mnesia to ODBC
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
-export([start/2,
|
-export([start/2,
|
||||||
stop/1,
|
stop/1,
|
||||||
|
get_local_identity/5,
|
||||||
|
get_local_features/5,
|
||||||
|
get_local_items/5,
|
||||||
|
get_sm_features/5,
|
||||||
|
get_sm_items/5,
|
||||||
process_local_iq/3,
|
process_local_iq/3,
|
||||||
process_sm_iq/3]).
|
process_sm_iq/3]).
|
||||||
|
|
||||||
@ -27,14 +32,399 @@ start(Host, Opts) ->
|
|||||||
?MODULE, process_local_iq, IQDisc),
|
?MODULE, process_local_iq, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_EJABBERD_CONFIG,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_EJABBERD_CONFIG,
|
||||||
?MODULE, process_sm_iq, IQDisc),
|
?MODULE, process_sm_iq, IQDisc),
|
||||||
|
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),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
stop(Host) ->
|
stop(Host) ->
|
||||||
|
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_EJABBERD_CONFIG),
|
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_EJABBERD_CONFIG),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_EJABBERD_CONFIG).
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_EJABBERD_CONFIG).
|
||||||
|
|
||||||
|
|
||||||
process_local_iq(From, To, #iq{id = ID, type = Type, xmlns = XMLNS,
|
-define(EMPTY_INFO_RESULT, {result, Feats}).
|
||||||
|
|
||||||
|
get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_sm_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
|
||||||
|
case {acl:match_rule(LServer, configure, From), Node} of
|
||||||
|
{allow, []} ->
|
||||||
|
case Acc of
|
||||||
|
{result, Features} ->
|
||||||
|
{result, [?NS_EJABBERD_CONFIG | Features]};
|
||||||
|
empty ->
|
||||||
|
{result, [?NS_EJABBERD_CONFIG]}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_local_identity(Acc, _From, _To, Node, _Lang) ->
|
||||||
|
case Node of
|
||||||
|
["running nodes", ENode] ->
|
||||||
|
[{xmlelement, "identity",
|
||||||
|
[{"category", "ejabberd"},
|
||||||
|
{"type", "node"},
|
||||||
|
{"name", ENode}], []}];
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_local_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_local_features(Acc, _From, _To, [], _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
|
||||||
|
Feats = case Acc of
|
||||||
|
{result, Features} -> Features;
|
||||||
|
empty -> []
|
||||||
|
end,
|
||||||
|
case {acl:match_rule(LServer, configure, From), Node} of
|
||||||
|
{deny, _} ->
|
||||||
|
{error, ?ERR_NOT_ALLOWED};
|
||||||
|
{allow, ["config"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["online users"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["all users"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["all users", [$@ | _]]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["outgoing s2s" | _]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["running nodes"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["stopped nodes"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["running nodes", _ENode]} ->
|
||||||
|
{result, [?NS_STATS | Feats]};
|
||||||
|
{allow, ["running nodes", _ENode, "DB"]} ->
|
||||||
|
{result, [?NS_EJABBERD_CONFIG | Feats]};
|
||||||
|
{allow, ["running nodes", _ENode, "modules"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["running nodes", _ENode, "modules", _]} ->
|
||||||
|
{result, [?NS_EJABBERD_CONFIG | Feats]};
|
||||||
|
{allow, ["running nodes", _ENode, "backup"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["running nodes", _ENode, "backup", _]} ->
|
||||||
|
{result, [?NS_EJABBERD_CONFIG | Feats]};
|
||||||
|
{allow, ["running nodes", _ENode, "import"]} ->
|
||||||
|
?EMPTY_INFO_RESULT;
|
||||||
|
{allow, ["running nodes", _ENode, "import", _]} ->
|
||||||
|
{result, [?NS_EJABBERD_CONFIG | Feats]};
|
||||||
|
{allow, ["config", _]} ->
|
||||||
|
{result, [?NS_EJABBERD_CONFIG | Feats]};
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_sm_items(Acc, From,
|
||||||
|
#jid{user = User, server = Server, lserver = LServer} = _To,
|
||||||
|
Node, _Lang) ->
|
||||||
|
case {acl:match_rule(LServer, configure, From), Node} of
|
||||||
|
{allow, []} ->
|
||||||
|
Items = case Acc of
|
||||||
|
{result, Its} ->
|
||||||
|
Its;
|
||||||
|
empty ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
{result, Items ++ get_user_resources(User, Server)};
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_user_resources(User, Server) ->
|
||||||
|
Rs = ejabberd_sm:get_user_resources(User, Server),
|
||||||
|
lists:map(fun(R) ->
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", User ++ "@" ++ Server ++ "/" ++ R},
|
||||||
|
{"name", User}], []}
|
||||||
|
end, lists:sort(Rs)).
|
||||||
|
|
||||||
|
get_local_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) ->
|
||||||
|
Items = case Acc of
|
||||||
|
{result, Its} ->
|
||||||
|
Its;
|
||||||
|
empty ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
case acl:match_rule(LServer, configure, From) of
|
||||||
|
deny when Node /= [] ->
|
||||||
|
{error, ?ERR_NOT_ALLOWED};
|
||||||
|
deny ->
|
||||||
|
{result, Items};
|
||||||
|
_ ->
|
||||||
|
case get_local_items(To#jid.lserver, Node,
|
||||||
|
jlib:jid_to_string(To), Lang) of
|
||||||
|
{result, Res} ->
|
||||||
|
{result, Items ++ Res};
|
||||||
|
{error, Error} ->
|
||||||
|
{error, Error}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-define(NODE(Name, Node),
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", Server},
|
||||||
|
{"name", translate:translate(Lang, Name)},
|
||||||
|
{"node", Node}], []}).
|
||||||
|
|
||||||
|
get_local_items(_Host, [], Server, Lang) ->
|
||||||
|
{result,
|
||||||
|
[?NODE("Configuration", "config"),
|
||||||
|
?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("Host Name", "config/hostname"),
|
||||||
|
?NODE("Access Control Lists", "config/acls"),
|
||||||
|
?NODE("Access Rules", "config/access")
|
||||||
|
% Too expensive on big hosts
|
||||||
|
%?NODE("Remove Users", "config/remusers")
|
||||||
|
]};
|
||||||
|
|
||||||
|
get_local_items(_Host, ["config", _], _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]], _Server, _Lang) ->
|
||||||
|
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||||
|
{'EXIT', _Reason} ->
|
||||||
|
?ERR_INTERNAL_SERVER_ERROR;
|
||||||
|
Users ->
|
||||||
|
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||||
|
case catch begin
|
||||||
|
{ok, [S1, S2]} = regexp:split(Diap, "-"),
|
||||||
|
N1 = list_to_integer(S1),
|
||||||
|
N2 = list_to_integer(S2),
|
||||||
|
Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
|
||||||
|
lists:map(fun({S, U}) ->
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", U ++ "@" ++ S},
|
||||||
|
{"name", U ++ "@" ++ S}], []}
|
||||||
|
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(Lang)};
|
||||||
|
|
||||||
|
get_local_items(_Host, ["stopped nodes"], _Server, Lang) ->
|
||||||
|
{result, get_stopped_nodes(Lang)};
|
||||||
|
|
||||||
|
get_local_items(_Host, ["running nodes", ENode], Server, Lang) ->
|
||||||
|
{result,
|
||||||
|
[?NODE("DB", "running nodes/" ++ ENode ++ "/DB"),
|
||||||
|
?NODE("Modules", "running nodes/" ++ ENode ++ "/modules"),
|
||||||
|
?NODE("Backup Management", "running nodes/" ++ ENode ++ "/backup"),
|
||||||
|
?NODE("Import users from jabberd1.4 spool files",
|
||||||
|
"running nodes/" ++ ENode ++ "/import")
|
||||||
|
]};
|
||||||
|
|
||||||
|
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 ++ "/modules/start"),
|
||||||
|
?NODE("Stop Modules", "running nodes/" ++ ENode ++ "/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 ++ "/backup/backup"),
|
||||||
|
?NODE("Restore", "running nodes/" ++ ENode ++ "/backup/restore"),
|
||||||
|
?NODE("Dump to Text File",
|
||||||
|
"running nodes/" ++ ENode ++ "/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 ++ "/import/file"),
|
||||||
|
?NODE("Import Directory", "running nodes/" ++ ENode ++ "/import/dir")
|
||||||
|
]};
|
||||||
|
|
||||||
|
get_local_items(_Host, ["running nodes", _ENode, "import", _], _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}) ->
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", U ++ "@" ++ S ++ "/" ++ R},
|
||||||
|
{"name", U ++ "@" ++ S}], []}
|
||||||
|
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}) ->
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", U ++ "@" ++ S},
|
||||||
|
{"name", U ++ "@" ++ S}], []}
|
||||||
|
end, SUsers);
|
||||||
|
N ->
|
||||||
|
NParts = trunc(math:sqrt(N * 0.618)) + 1,
|
||||||
|
M = trunc(N / NParts) + 1,
|
||||||
|
lists:map(fun(K) ->
|
||||||
|
L = K + M - 1,
|
||||||
|
Node =
|
||||||
|
"@" ++ integer_to_list(K) ++
|
||||||
|
"-" ++ integer_to_list(L),
|
||||||
|
{FS, FU} = lists:nth(K, SUsers),
|
||||||
|
{LS, LU} =
|
||||||
|
if L < N -> lists:nth(L, SUsers);
|
||||||
|
true -> lists:last(SUsers)
|
||||||
|
end,
|
||||||
|
Name =
|
||||||
|
FU ++ "@" ++ FS ++
|
||||||
|
" -- " ++
|
||||||
|
LU ++ "@" ++ LS,
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", Host},
|
||||||
|
{"node", "all users/" ++ Node},
|
||||||
|
{"name", Name}], []}
|
||||||
|
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,
|
||||||
|
TConns = [TH || {FH, TH} <- Connections,
|
||||||
|
Host == FH orelse lists:suffix(DotHost, FH)],
|
||||||
|
lists:map(
|
||||||
|
fun(T) ->
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", Host},
|
||||||
|
{"node", "outgoing s2s/" ++ T},
|
||||||
|
{"name",
|
||||||
|
lists:flatten(
|
||||||
|
io_lib:format(
|
||||||
|
translate:translate(Lang, "To ~s"), [T]))}],
|
||||||
|
[]}
|
||||||
|
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}) ->
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", Host},
|
||||||
|
{"node", "outgoing s2s/" ++ To ++ "/" ++ F},
|
||||||
|
{"name",
|
||||||
|
lists:flatten(
|
||||||
|
io_lib:format(
|
||||||
|
translate:translate(Lang, "From ~s"), [F]))}],
|
||||||
|
[]}
|
||||||
|
end, lists:keysort(1, lists:filter(fun(E) ->
|
||||||
|
element(2, E) == To
|
||||||
|
end, Connections)))
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
get_running_nodes(_Lang) ->
|
||||||
|
case catch mnesia:system_info(running_db_nodes) of
|
||||||
|
{'EXIT', _Reason} ->
|
||||||
|
[];
|
||||||
|
DBNodes ->
|
||||||
|
lists:map(
|
||||||
|
fun(N) ->
|
||||||
|
S = atom_to_list(N),
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", ?MYNAME},
|
||||||
|
{"node", "running nodes/" ++ S},
|
||||||
|
{"name", S}],
|
||||||
|
[]}
|
||||||
|
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 = atom_to_list(N),
|
||||||
|
{xmlelement, "item",
|
||||||
|
[{"jid", ?MYNAME},
|
||||||
|
{"node", "stopped nodes/" ++ S},
|
||||||
|
{"name", S}],
|
||||||
|
[]}
|
||||||
|
end, lists:sort(DBNodes))
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
process_local_iq(From, To, #iq{type = Type, xmlns = XMLNS,
|
||||||
lang = Lang, sub_el = SubEl} = IQ) ->
|
lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
case acl:match_rule(To#jid.lserver, configure, From) of
|
case acl:match_rule(To#jid.lserver, configure, From) of
|
||||||
deny ->
|
deny ->
|
||||||
@ -46,7 +436,7 @@ process_local_iq(From, To, #iq{id = ID, type = Type, xmlns = XMLNS,
|
|||||||
case XDataEl of
|
case XDataEl of
|
||||||
false ->
|
false ->
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
|
||||||
{xmlelement, _Name, Attrs, SubEls} ->
|
{xmlelement, _Name, Attrs, _SubEls} ->
|
||||||
case xml:get_attr_s("type", Attrs) of
|
case xml:get_attr_s("type", Attrs) of
|
||||||
"cancel" ->
|
"cancel" ->
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
@ -394,7 +784,7 @@ get_form(["config", "remusers"], Lang) ->
|
|||||||
translate:translate(
|
translate:translate(
|
||||||
Lang, "Choose users to remove")}]}] ++
|
Lang, "Choose users to remove")}]}] ++
|
||||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
case catch ejabberd_auth:dirty_get_registered_users() of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
[];
|
[];
|
||||||
Users ->
|
Users ->
|
||||||
lists:map(fun(U) ->
|
lists:map(fun(U) ->
|
||||||
@ -403,12 +793,12 @@ get_form(["config", "remusers"], Lang) ->
|
|||||||
end
|
end
|
||||||
}]};
|
}]};
|
||||||
|
|
||||||
get_form(_, Lang) ->
|
get_form(_, _Lang) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "DB"], Lang, XData) ->
|
set_form(["running nodes", ENode, "DB"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -442,7 +832,7 @@ set_form(["running nodes", ENode, "DB"], Lang, XData) ->
|
|||||||
{result, []}
|
{result, []}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "modules", "stop"], Lang, XData) ->
|
set_form(["running nodes", ENode, "modules", "stop"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -460,7 +850,7 @@ set_form(["running nodes", ENode, "modules", "stop"], Lang, XData) ->
|
|||||||
{result, []}
|
{result, []}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "modules", "start"], Lang, XData) ->
|
set_form(["running nodes", ENode, "modules", "start"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -496,7 +886,7 @@ set_form(["running nodes", ENode, "modules", "start"], Lang, XData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "backup", "backup"], Lang, XData) ->
|
set_form(["running nodes", ENode, "backup", "backup"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -506,9 +896,9 @@ set_form(["running nodes", ENode, "backup", "backup"], Lang, XData) ->
|
|||||||
{error, ?ERR_BAD_REQUEST};
|
{error, ?ERR_BAD_REQUEST};
|
||||||
{value, {_, [String]}} ->
|
{value, {_, [String]}} ->
|
||||||
case rpc:call(Node, mnesia, backup, [String]) of
|
case rpc:call(Node, mnesia, backup, [String]) of
|
||||||
{badrpc, Reason} ->
|
{badrpc, _Reason} ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
{error, Reason} ->
|
{error, _Reason} ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
_ ->
|
_ ->
|
||||||
{result, []}
|
{result, []}
|
||||||
@ -519,7 +909,7 @@ set_form(["running nodes", ENode, "backup", "backup"], Lang, XData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "backup", "restore"], Lang, XData) ->
|
set_form(["running nodes", ENode, "backup", "restore"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -530,9 +920,9 @@ set_form(["running nodes", ENode, "backup", "restore"], Lang, XData) ->
|
|||||||
{value, {_, [String]}} ->
|
{value, {_, [String]}} ->
|
||||||
case rpc:call(Node, mnesia, restore,
|
case rpc:call(Node, mnesia, restore,
|
||||||
[String, [{default_op, keep_tables}]]) of
|
[String, [{default_op, keep_tables}]]) of
|
||||||
{badrpc, Reason} ->
|
{badrpc, _Reason} ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
{error, Reason} ->
|
{error, _Reason} ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
_ ->
|
_ ->
|
||||||
{result, []}
|
{result, []}
|
||||||
@ -543,7 +933,7 @@ set_form(["running nodes", ENode, "backup", "restore"], Lang, XData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "backup", "textfile"], Lang, XData) ->
|
set_form(["running nodes", ENode, "backup", "textfile"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -553,9 +943,9 @@ set_form(["running nodes", ENode, "backup", "textfile"], Lang, XData) ->
|
|||||||
{error, ?ERR_BAD_REQUEST};
|
{error, ?ERR_BAD_REQUEST};
|
||||||
{value, {_, [String]}} ->
|
{value, {_, [String]}} ->
|
||||||
case rpc:call(Node, mnesia, dump_to_textfile, [String]) of
|
case rpc:call(Node, mnesia, dump_to_textfile, [String]) of
|
||||||
{badrpc, Reason} ->
|
{badrpc, _Reason} ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
{error, Reason} ->
|
{error, _Reason} ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
_ ->
|
_ ->
|
||||||
{result, []}
|
{result, []}
|
||||||
@ -566,7 +956,7 @@ set_form(["running nodes", ENode, "backup", "textfile"], Lang, XData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "import", "file"], Lang, XData) ->
|
set_form(["running nodes", ENode, "import", "file"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -583,7 +973,7 @@ set_form(["running nodes", ENode, "import", "file"], Lang, XData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
set_form(["running nodes", ENode, "import", "dir"], Lang, XData) ->
|
set_form(["running nodes", ENode, "import", "dir"], _Lang, XData) ->
|
||||||
case search_running_node(ENode) of
|
case search_running_node(ENode) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
@ -600,7 +990,7 @@ set_form(["running nodes", ENode, "import", "dir"], Lang, XData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
set_form(["config", "hostname"], Lang, XData) ->
|
set_form(["config", "hostname"], _Lang, XData) ->
|
||||||
case lists:keysearch("hostname", 1, XData) of
|
case lists:keysearch("hostname", 1, XData) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
{error, ?ERR_BAD_REQUEST};
|
||||||
@ -613,7 +1003,7 @@ set_form(["config", "hostname"], Lang, XData) ->
|
|||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
set_form(["config", "acls"], Lang, XData) ->
|
set_form(["config", "acls"], _Lang, XData) ->
|
||||||
case lists:keysearch("acls", 1, XData) of
|
case lists:keysearch("acls", 1, XData) of
|
||||||
{value, {_, Strings}} ->
|
{value, {_, Strings}} ->
|
||||||
String = lists:foldl(fun(S, Res) ->
|
String = lists:foldl(fun(S, Res) ->
|
||||||
@ -639,7 +1029,7 @@ set_form(["config", "acls"], Lang, XData) ->
|
|||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
set_form(["config", "access"], Lang, XData) ->
|
set_form(["config", "access"], _Lang, XData) ->
|
||||||
SetAccess =
|
SetAccess =
|
||||||
fun(Rs) ->
|
fun(Rs) ->
|
||||||
mnesia:transaction(
|
mnesia:transaction(
|
||||||
@ -685,7 +1075,7 @@ set_form(["config", "access"], Lang, XData) ->
|
|||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
set_form(["config", "remusers"], Lang, XData) ->
|
set_form(["config", "remusers"], _Lang, XData) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({Var, Vals}) ->
|
fun({Var, Vals}) ->
|
||||||
case Vals of
|
case Vals of
|
||||||
@ -697,7 +1087,7 @@ set_form(["config", "remusers"], Lang, XData) ->
|
|||||||
end, XData),
|
end, XData),
|
||||||
{result, []};
|
{result, []};
|
||||||
|
|
||||||
set_form(_, Lang, XData) ->
|
set_form(_, _Lang, _XData) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||||
|
|
||||||
|
|
||||||
@ -730,7 +1120,7 @@ process_sm_iq(From, To,
|
|||||||
case XDataEl of
|
case XDataEl of
|
||||||
false ->
|
false ->
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
|
||||||
{xmlelement, _Name, Attrs, SubEls} ->
|
{xmlelement, _Name, Attrs, _SubEls} ->
|
||||||
case xml:get_attr_s("type", Attrs) of
|
case xml:get_attr_s("type", Attrs) of
|
||||||
"cancel" ->
|
"cancel" ->
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
@ -815,11 +1205,11 @@ get_sm_form(User, Server, [], Lang) ->
|
|||||||
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
|
||||||
]}]};
|
]}]};
|
||||||
|
|
||||||
get_sm_form(_User, _Server, _Node, Lang) ->
|
get_sm_form(_User, _Server, _Node, _Lang) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||||
|
|
||||||
|
|
||||||
set_sm_form(User, Server, [], Lang, XData) ->
|
set_sm_form(User, Server, [], _Lang, XData) ->
|
||||||
case lists:keysearch("action", 1, XData) of
|
case lists:keysearch("action", 1, XData) of
|
||||||
{value, {_, ["edit"]}} ->
|
{value, {_, ["edit"]}} ->
|
||||||
case lists:keysearch("password", 1, XData) of
|
case lists:keysearch("password", 1, XData) of
|
||||||
@ -835,7 +1225,7 @@ set_sm_form(User, Server, [], Lang, XData) ->
|
|||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
end;
|
end;
|
||||||
set_sm_form(_User, _Server, _Node, Lang, XData) ->
|
set_sm_form(_User, _Server, _Node, _Lang, _XData) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||||
|
|
||||||
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
|
||||||
|
@ -16,26 +16,22 @@
|
|||||||
stop/1,
|
stop/1,
|
||||||
process_local_iq_items/3,
|
process_local_iq_items/3,
|
||||||
process_local_iq_info/3,
|
process_local_iq_info/3,
|
||||||
|
get_local_identity/5,
|
||||||
|
get_local_features/5,
|
||||||
|
get_local_services/5,
|
||||||
process_sm_iq_items/3,
|
process_sm_iq_items/3,
|
||||||
process_sm_iq_info/3,
|
process_sm_iq_info/3,
|
||||||
|
get_sm_identity/5,
|
||||||
|
get_sm_features/5,
|
||||||
|
get_sm_items/5,
|
||||||
register_feature/2,
|
register_feature/2,
|
||||||
unregister_feature/2,
|
unregister_feature/2,
|
||||||
register_extra_domain/2,
|
register_extra_domain/2,
|
||||||
unregister_extra_domain/2,
|
unregister_extra_domain/2]).
|
||||||
register_sm_feature/2,
|
|
||||||
unregister_sm_feature/2,
|
|
||||||
register_sm_node/4,
|
|
||||||
unregister_sm_node/1]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
|
||||||
-define(EMPTY_INFO_RESULT,
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", ?NS_DISCO_INFO},
|
|
||||||
{"node", SNode}], []}]}).
|
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
ejabberd_local:refresh_iq_handlers(),
|
ejabberd_local:refresh_iq_handlers(),
|
||||||
|
|
||||||
@ -60,9 +56,21 @@ start(Host, Opts) ->
|
|||||||
ExtraDomains),
|
ExtraDomains),
|
||||||
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
|
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
|
||||||
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
|
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
|
||||||
|
ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_services, 100),
|
||||||
|
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 100),
|
||||||
|
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 100),
|
||||||
|
ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 100),
|
||||||
|
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 100),
|
||||||
|
ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
stop(Host) ->
|
stop(Host) ->
|
||||||
|
ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100),
|
||||||
|
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 100),
|
||||||
|
ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 100),
|
||||||
|
ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 100),
|
||||||
|
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 100),
|
||||||
|
ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_services, 100),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
|
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
|
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS),
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS),
|
||||||
@ -97,123 +105,86 @@ process_local_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} =
|
|||||||
Node = string:tokens(SNode, "/"),
|
Node = string:tokens(SNode, "/"),
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
|
|
||||||
case acl:match_rule(Host, configure, From) of
|
case ejabberd_hooks:run_fold(disco_local_items,
|
||||||
deny when Node /= [] ->
|
Host,
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
empty,
|
||||||
deny ->
|
[From, To, Node, Lang]) of
|
||||||
|
{result, Items} ->
|
||||||
|
ANode = case Node of
|
||||||
|
[] -> [];
|
||||||
|
_ -> [{"node", SNode}]
|
||||||
|
end,
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "query",
|
||||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
[{"xmlns", ?NS_DISCO_ITEMS} | ANode],
|
||||||
get_services_only(Host)
|
Items
|
||||||
}]};
|
}]};
|
||||||
_ ->
|
{error, Error} ->
|
||||||
case get_local_items(Host, Node,
|
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||||
jlib:jid_to_string(To), Lang) of
|
|
||||||
{result, Res} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", ?NS_DISCO_ITEMS},
|
|
||||||
{"node", SNode}],
|
|
||||||
Res
|
|
||||||
}]};
|
|
||||||
{error, Error} ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
process_local_iq_info(From, To, #iq{type = Type, xmlns = XMLNS,
|
process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
|
||||||
sub_el = SubEl} = IQ) ->
|
sub_el = SubEl} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
set ->
|
set ->
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||||
get ->
|
get ->
|
||||||
LServer = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
SNode = xml:get_tag_attr_s("node", SubEl),
|
SNode = xml:get_tag_attr_s("node", SubEl),
|
||||||
Node = string:tokens(SNode, "/"),
|
Node = string:tokens(SNode, "/"),
|
||||||
case {acl:match_rule(LServer, configure, From), Node} of
|
Identity = ejabberd_hooks:run_fold(disco_local_identity,
|
||||||
{_, []} ->
|
Host,
|
||||||
Features = lists:map(
|
[],
|
||||||
fun feature_to_xml/1,
|
[From, To, Node, Lang]),
|
||||||
ets:select(disco_features,
|
case ejabberd_hooks:run_fold(disco_local_features,
|
||||||
[{{{'$1', LServer}},
|
Host,
|
||||||
[],
|
empty,
|
||||||
['$1']}])),
|
[From, To, Node, Lang]) of
|
||||||
|
{result, Features} ->
|
||||||
|
ANode = case Node of
|
||||||
|
[] -> [];
|
||||||
|
_ -> [{"node", SNode}]
|
||||||
|
end,
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement,
|
sub_el = [{xmlelement, "query",
|
||||||
"query",
|
[{"xmlns", ?NS_DISCO_INFO} | ANode],
|
||||||
[{"xmlns", ?NS_DISCO_INFO}],
|
Identity ++
|
||||||
[{xmlelement, "identity",
|
lists:map(fun feature_to_xml/1, Features)
|
||||||
[{"category", "server"},
|
|
||||||
{"type", "im"},
|
|
||||||
{"name", "ejabberd"}], []}] ++
|
|
||||||
Features
|
|
||||||
}]};
|
}]};
|
||||||
{deny, _} ->
|
{error, Error} ->
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||||
{allow, ["config"]} -> ?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["online users"]} -> ?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["all users"]} -> ?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["all users", [$@ | _]]} -> ?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["outgoing s2s" | _]} -> ?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["running nodes"]} -> ?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["stopped nodes"]} -> ?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["running nodes", ENode]} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement,
|
|
||||||
"query",
|
|
||||||
[{"xmlns", XMLNS},
|
|
||||||
{"node", SNode}],
|
|
||||||
[{xmlelement, "identity",
|
|
||||||
[{"category", "ejabberd"},
|
|
||||||
{"type", "node"},
|
|
||||||
{"name", ENode}], []},
|
|
||||||
feature_to_xml(?NS_STATS)
|
|
||||||
]
|
|
||||||
}]};
|
|
||||||
{allow, ["running nodes", ENode, "DB"]} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement,
|
|
||||||
"query",
|
|
||||||
[{"xmlns", XMLNS},
|
|
||||||
{"node", SNode}],
|
|
||||||
[feature_to_xml(?NS_EJABBERD_CONFIG)]}]};
|
|
||||||
{allow, ["running nodes", ENode, "modules"]} ->
|
|
||||||
?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["running nodes", ENode, "modules", _]} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", XMLNS},
|
|
||||||
{"node", SNode}],
|
|
||||||
[feature_to_xml(?NS_EJABBERD_CONFIG)]}]};
|
|
||||||
{allow, ["running nodes", ENode, "backup"]} ->
|
|
||||||
?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["running nodes", ENode, "backup", _]} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", XMLNS},
|
|
||||||
{"node", SNode}],
|
|
||||||
[feature_to_xml(?NS_EJABBERD_CONFIG)]}]};
|
|
||||||
{allow, ["running nodes", ENode, "import"]} ->
|
|
||||||
?EMPTY_INFO_RESULT;
|
|
||||||
{allow, ["running nodes", ENode, "import", _]} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", XMLNS},
|
|
||||||
{"node", SNode}],
|
|
||||||
[feature_to_xml(?NS_EJABBERD_CONFIG)]}]};
|
|
||||||
{allow, ["config", _]} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", XMLNS},
|
|
||||||
{"node", SNode}],
|
|
||||||
[feature_to_xml(?NS_EJABBERD_CONFIG)]}]};
|
|
||||||
_ ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
|
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
get_local_identity(_Acc, _From, _To, [], _Lang) ->
|
||||||
|
[{xmlelement, "identity",
|
||||||
|
[{"category", "server"},
|
||||||
|
{"type", "im"},
|
||||||
|
{"name", "ejabberd"}], []}];
|
||||||
|
|
||||||
|
get_local_identity(Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc.
|
||||||
|
|
||||||
|
get_local_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_local_features(Acc, _From, _To, [], _Lang) ->
|
||||||
|
Feats = case Acc of
|
||||||
|
{result, Features} -> Features;
|
||||||
|
empty -> []
|
||||||
|
end,
|
||||||
|
{result, ets:tab2list(disco_features) ++ Feats};
|
||||||
|
|
||||||
|
get_local_features(Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
case Acc of
|
||||||
|
{result, _Features} ->
|
||||||
|
Acc;
|
||||||
|
empty ->
|
||||||
|
{error, ?ERR_ITEM_NOT_FOUND}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
feature_to_xml({{Feature, _Host}}) ->
|
feature_to_xml({{Feature, _Host}}) ->
|
||||||
feature_to_xml(Feature);
|
feature_to_xml(Feature);
|
||||||
@ -225,140 +196,27 @@ domain_to_xml({Domain}) ->
|
|||||||
domain_to_xml(Domain) ->
|
domain_to_xml(Domain) ->
|
||||||
{xmlelement, "item", [{"jid", Domain}], []}.
|
{xmlelement, "item", [{"jid", Domain}], []}.
|
||||||
|
|
||||||
-define(NODE(Name, Node),
|
get_local_services({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
{xmlelement, "item",
|
Acc;
|
||||||
[{"jid", Server},
|
|
||||||
{"name", translate:translate(Lang, Name)},
|
|
||||||
{"node", Node}], []}).
|
|
||||||
|
|
||||||
|
get_local_services(Acc, _From, To, [], _Lang) ->
|
||||||
get_services_only(Host) ->
|
Items = case Acc of
|
||||||
lists:map(fun domain_to_xml/1,
|
{result, Its} -> Its;
|
||||||
get_vh_services(Host)) ++
|
empty -> []
|
||||||
lists:map(fun domain_to_xml/1,
|
end,
|
||||||
ets:select(disco_extra_domains,
|
Host = To#jid.lserver,
|
||||||
[{{{'$1', Host}},
|
|
||||||
[],
|
|
||||||
['$1']}])).
|
|
||||||
|
|
||||||
get_local_items(Host, [], Server, Lang) ->
|
|
||||||
Domains =
|
|
||||||
lists:map(fun domain_to_xml/1,
|
|
||||||
get_vh_services(Host)) ++
|
|
||||||
lists:map(fun domain_to_xml/1,
|
|
||||||
ets:select(disco_extra_domains,
|
|
||||||
[{{{'$1', Host}},
|
|
||||||
[],
|
|
||||||
['$1']}])),
|
|
||||||
{result,
|
{result,
|
||||||
Domains ++
|
lists:usort(
|
||||||
[?NODE("Configuration", "config"),
|
lists:map(fun domain_to_xml/1,
|
||||||
?NODE("Online Users", "online users"),
|
get_vh_services(Host) ++
|
||||||
?NODE("All Users", "all users"),
|
ets:tab2list(disco_extra_domains))
|
||||||
?NODE("Outgoing S2S connections", "outgoing s2s"),
|
) ++ Items};
|
||||||
?NODE("Running Nodes", "running nodes"),
|
|
||||||
?NODE("Stopped Nodes", "stopped nodes")
|
|
||||||
]};
|
|
||||||
|
|
||||||
get_local_items(Host, ["config"], Server, Lang) ->
|
|
||||||
{result,
|
|
||||||
[?NODE("Host Name", "config/hostname"),
|
|
||||||
?NODE("Access Control Lists", "config/acls"),
|
|
||||||
?NODE("Access Rules", "config/access")
|
|
||||||
% Too expensive on big hosts
|
|
||||||
%?NODE("Remove Users", "config/remusers")
|
|
||||||
]};
|
|
||||||
|
|
||||||
get_local_items(Host, ["config", _], 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]], Server, Lang) ->
|
|
||||||
case catch ejabberd_auth:dirty_get_registered_users() of
|
|
||||||
{'EXIT', Reason} ->
|
|
||||||
?ERR_INTERNAL_SERVER_ERROR;
|
|
||||||
Users ->
|
|
||||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
|
||||||
case catch begin
|
|
||||||
{ok, [S1, S2]} = regexp:split(Diap, "-"),
|
|
||||||
N1 = list_to_integer(S1),
|
|
||||||
N2 = list_to_integer(S2),
|
|
||||||
Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
|
|
||||||
lists:map(fun({S, U}) ->
|
|
||||||
{xmlelement, "item",
|
|
||||||
[{"jid", U ++ "@" ++ S},
|
|
||||||
{"name", U ++ "@" ++ S}], []}
|
|
||||||
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(Lang)};
|
|
||||||
|
|
||||||
get_local_items(Host, ["stopped nodes"], Server, Lang) ->
|
|
||||||
{result, get_stopped_nodes(Lang)};
|
|
||||||
|
|
||||||
get_local_items(Host, ["running nodes", ENode], Server, Lang) ->
|
|
||||||
{result,
|
|
||||||
[?NODE("DB", "running nodes/" ++ ENode ++ "/DB"),
|
|
||||||
?NODE("Modules", "running nodes/" ++ ENode ++ "/modules"),
|
|
||||||
?NODE("Backup Management", "running nodes/" ++ ENode ++ "/backup"),
|
|
||||||
?NODE("Import users from jabberd1.4 spool files",
|
|
||||||
"running nodes/" ++ ENode ++ "/import")
|
|
||||||
]};
|
|
||||||
|
|
||||||
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 ++ "/modules/start"),
|
|
||||||
?NODE("Stop Modules", "running nodes/" ++ ENode ++ "/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 ++ "/backup/backup"),
|
|
||||||
?NODE("Restore", "running nodes/" ++ ENode ++ "/backup/restore"),
|
|
||||||
?NODE("Dump to Text File",
|
|
||||||
"running nodes/" ++ ENode ++ "/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 ++ "/import/file"),
|
|
||||||
?NODE("Import Directory", "running nodes/" ++ ENode ++ "/import/dir")
|
|
||||||
]};
|
|
||||||
|
|
||||||
get_local_items(Host, ["running nodes", ENode, "import", _], Server, Lang) ->
|
|
||||||
{result, []};
|
|
||||||
|
|
||||||
get_local_items(_Host, _, _, _) ->
|
|
||||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}.
|
|
||||||
|
|
||||||
|
get_local_services({result, _} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_local_services(empty, _From, _To, _Node, _Lang) ->
|
||||||
|
{error, ?ERR_ITEM_NOT_FOUND}.
|
||||||
|
|
||||||
get_vh_services(Host) ->
|
get_vh_services(Host) ->
|
||||||
Hosts = lists:sort(fun(H1, H2) -> length(H1) >= length(H2) end, ?MYHOSTS),
|
Hosts = lists:sort(fun(H1, H2) -> length(H1) >= length(H2) end, ?MYHOSTS),
|
||||||
@ -374,253 +232,120 @@ get_vh_services(Host) ->
|
|||||||
end
|
end
|
||||||
end, ejabberd_router:dirty_get_all_routes()).
|
end, ejabberd_router:dirty_get_all_routes()).
|
||||||
|
|
||||||
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}) ->
|
|
||||||
{xmlelement, "item",
|
|
||||||
[{"jid", U ++ "@" ++ S ++ "/" ++ R},
|
|
||||||
{"name", U ++ "@" ++ S}], []}
|
|
||||||
end, SURs)
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_all_vh_users(Host) ->
|
process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
case catch ejabberd_auth:get_vh_registered_users(Host) of
|
case Type of
|
||||||
{'EXIT', Reason} ->
|
set ->
|
||||||
[];
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||||
Users ->
|
get ->
|
||||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
Host = To#jid.lserver,
|
||||||
case length(SUsers) of
|
SNode = xml:get_tag_attr_s("node", SubEl),
|
||||||
N when N =< 100 ->
|
Node = string:tokens(SNode, "/"),
|
||||||
lists:map(fun({S, U}) ->
|
case ejabberd_hooks:run_fold(disco_sm_items,
|
||||||
{xmlelement, "item",
|
Host,
|
||||||
[{"jid", U ++ "@" ++ S},
|
empty,
|
||||||
{"name", U ++ "@" ++ S}], []}
|
[From, To, Node, Lang]) of
|
||||||
end, SUsers);
|
{result, Items} ->
|
||||||
N ->
|
ANode = case Node of
|
||||||
NParts = trunc(math:sqrt(N * 0.618)) + 1,
|
[] -> [];
|
||||||
M = trunc(N / NParts) + 1,
|
_ -> [{"node", SNode}]
|
||||||
lists:map(fun(K) ->
|
end,
|
||||||
L = K + M - 1,
|
IQ#iq{type = result,
|
||||||
Node =
|
sub_el = [{xmlelement, "query",
|
||||||
"@" ++ integer_to_list(K) ++
|
[{"xmlns", ?NS_DISCO_ITEMS} | ANode],
|
||||||
"-" ++ integer_to_list(L),
|
Items
|
||||||
{FS, FU} = lists:nth(K, SUsers),
|
}]};
|
||||||
{LS, LU} =
|
{error, Error} ->
|
||||||
if L < N -> lists:nth(L, SUsers);
|
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||||
true -> lists:last(SUsers)
|
|
||||||
end,
|
|
||||||
Name =
|
|
||||||
FU ++ "@" ++ FS ++
|
|
||||||
" -- " ++
|
|
||||||
LU ++ "@" ++ LS,
|
|
||||||
{xmlelement, "item",
|
|
||||||
[{"jid", Host},
|
|
||||||
{"node", "all users/" ++ Node},
|
|
||||||
{"name", Name}], []}
|
|
||||||
end, lists:seq(1, N, M))
|
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_outgoing_s2s(Host, Lang) ->
|
get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
case catch ejabberd_s2s:dirty_get_connections() of
|
Acc;
|
||||||
{'EXIT', Reason} ->
|
|
||||||
[];
|
|
||||||
Connections ->
|
|
||||||
DotHost = "." ++ Host,
|
|
||||||
TConns = [TH || {FH, TH} <- Connections,
|
|
||||||
Host == FH orelse lists:suffix(DotHost, FH)],
|
|
||||||
lists:map(
|
|
||||||
fun(T) ->
|
|
||||||
{xmlelement, "item",
|
|
||||||
[{"jid", Host},
|
|
||||||
{"node", "outgoing s2s/" ++ T},
|
|
||||||
{"name",
|
|
||||||
lists:flatten(
|
|
||||||
io_lib:format(
|
|
||||||
translate:translate(Lang, "To ~s"), [T]))}],
|
|
||||||
[]}
|
|
||||||
end, lists:usort(TConns))
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_outgoing_s2s(Host, Lang, To) ->
|
get_sm_items(Acc,
|
||||||
case catch ejabberd_s2s:dirty_get_connections() of
|
#jid{luser = LFrom, lserver = LSFrom} = _From,
|
||||||
{'EXIT', Reason} ->
|
#jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
|
||||||
[];
|
[], _Lang) ->
|
||||||
Connections ->
|
Items = case Acc of
|
||||||
lists:map(
|
{result, Its} -> Its;
|
||||||
fun({F, T}) ->
|
empty -> []
|
||||||
{xmlelement, "item",
|
end,
|
||||||
[{"jid", Host},
|
Items1 = case {LFrom, LSFrom} of
|
||||||
{"node", "outgoing s2s/" ++ To ++ "/" ++ F},
|
{LTo, LSTo} -> get_user_resources(User, Server);
|
||||||
{"name",
|
_ -> []
|
||||||
lists:flatten(
|
end,
|
||||||
io_lib:format(
|
{result, Items ++ Items1};
|
||||||
translate:translate(Lang, "From ~s"), [F]))}],
|
|
||||||
[]}
|
get_sm_items({result, _} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
end, lists:keysort(1, lists:filter(fun(E) ->
|
Acc;
|
||||||
element(2, E) == To
|
|
||||||
end, Connections)))
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
get_sm_items(empty, From, To, _Node, _Lang) ->
|
||||||
get_running_nodes(Lang) ->
|
#jid{luser = LFrom, lserver = LSFrom} = From,
|
||||||
case catch mnesia:system_info(running_db_nodes) of
|
#jid{luser = LTo, lserver = LSTo} = To,
|
||||||
{'EXIT', Reason} ->
|
case {LFrom, LSFrom} of
|
||||||
[];
|
{LTo, LSTo} ->
|
||||||
DBNodes ->
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
lists:map(
|
|
||||||
fun(N) ->
|
|
||||||
S = atom_to_list(N),
|
|
||||||
{xmlelement, "item",
|
|
||||||
[{"jid", ?MYNAME},
|
|
||||||
{"node", "running nodes/" ++ S},
|
|
||||||
{"name", S}],
|
|
||||||
[]}
|
|
||||||
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 = atom_to_list(N),
|
|
||||||
{xmlelement, "item",
|
|
||||||
[{"jid", ?MYNAME},
|
|
||||||
{"node", "stopped nodes/" ++ S},
|
|
||||||
{"name", S}],
|
|
||||||
[]}
|
|
||||||
end, lists:sort(DBNodes))
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
|
|
||||||
register_sm_feature(Host, Feature) ->
|
|
||||||
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
|
|
||||||
ets:insert(disco_sm_features, {{Feature, Host}}).
|
|
||||||
|
|
||||||
unregister_sm_feature(Host, Feature) ->
|
|
||||||
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
|
|
||||||
ets:delete(disco_sm_features, {Feature, Host}).
|
|
||||||
|
|
||||||
register_sm_node(Node, Name, Module, Function) ->
|
|
||||||
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
|
|
||||||
ets:insert(disco_sm_nodes, {Node, Name, Module, Function}).
|
|
||||||
|
|
||||||
unregister_sm_node(Node) ->
|
|
||||||
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
|
|
||||||
ets:delete(disco_sm_nodes, Node).
|
|
||||||
|
|
||||||
process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
|
||||||
#jid{user = User, luser = LTo} = To,
|
|
||||||
#jid{luser = LFrom, lserver = LServer} = From,
|
|
||||||
Self = (LTo == LFrom) andalso (LServer == ?MYNAME),
|
|
||||||
Node = xml:get_tag_attr_s("node", SubEl),
|
|
||||||
case {acl:match_rule(To#jid.lserver, configure, From), Type, Self, Node} of
|
|
||||||
{_, set, _, _} ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
|
||||||
{_, get, true, []} ->
|
|
||||||
Nodes = lists:map(fun({Nod, Name, _, _}) ->
|
|
||||||
node_to_xml(User,
|
|
||||||
Nod,
|
|
||||||
translate:translate(Lang, Name))
|
|
||||||
end, ets:tab2list(disco_sm_nodes)),
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
|
||||||
get_user_resources(User) ++ Nodes}]};
|
|
||||||
{allow, get, _, []} ->
|
|
||||||
Nodes = lists:map(fun({Nod, Name, _, _}) ->
|
|
||||||
node_to_xml(User,
|
|
||||||
Nod,
|
|
||||||
translate:translate(Lang, Name))
|
|
||||||
end, ets:tab2list(disco_sm_nodes)),
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", ?NS_DISCO_ITEMS}],
|
|
||||||
get_user_resources(User) ++ Nodes}]};
|
|
||||||
{A, get, S, _} when (A == allow) or (S == true) ->
|
|
||||||
case ets:lookup(disco_sm_nodes, Node) of
|
|
||||||
[] ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
|
|
||||||
[{Node, _Name, Module, Function}] ->
|
|
||||||
case Module:Function(From, To, IQ) of
|
|
||||||
{error, Err} ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, Err]};
|
|
||||||
{result, Res} ->
|
|
||||||
IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", ?NS_DISCO_ITEMS},
|
|
||||||
{"node", Node}],
|
|
||||||
Res}]}
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
{_, get, _, _} ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]};
|
|
||||||
_ ->
|
_ ->
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
{error, ?ERR_NOT_ALLOWED}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
process_sm_iq_info(From, To, #iq{type = Type, xmlns = XMLNS,
|
case Type of
|
||||||
sub_el = SubEl} = IQ) ->
|
set ->
|
||||||
#jid{luser = LTo} = To,
|
|
||||||
#jid{luser = LFrom, lserver = LServer} = From,
|
|
||||||
Self = (LTo == LFrom) andalso (LServer == ?MYNAME),
|
|
||||||
Node = xml:get_tag_attr_s("node", SubEl),
|
|
||||||
case {acl:match_rule(To#jid.lserver, configure, From), Type, Self, Node} of
|
|
||||||
{_, set, _, _} ->
|
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||||
{allow, get, _, []} ->
|
get ->
|
||||||
Features = lists:map(fun feature_to_xml/1,
|
Host = To#jid.lserver,
|
||||||
ets:tab2list(disco_sm_features)),
|
SNode = xml:get_tag_attr_s("node", SubEl),
|
||||||
IQ#iq{type = result,
|
Node = string:tokens(SNode, "/"),
|
||||||
sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}],
|
Identity = ejabberd_hooks:run_fold(disco_sm_identity,
|
||||||
[feature_to_xml(?NS_EJABBERD_CONFIG)] ++
|
Host,
|
||||||
Features}]};
|
[],
|
||||||
{_, get, _, []} ->
|
[From, To, Node, Lang]),
|
||||||
Features = lists:map(fun feature_to_xml/1,
|
case ejabberd_hooks:run_fold(disco_sm_features,
|
||||||
ets:tab2list(disco_sm_features)),
|
Host,
|
||||||
IQ#iq{type = result,
|
empty,
|
||||||
sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}],
|
[From, To, Node, Lang]) of
|
||||||
Features}]};
|
{result, Features} ->
|
||||||
{A, get, S, _} when (A == allow) or (S == true) ->
|
ANode = case Node of
|
||||||
case ets:lookup(disco_sm_nodes, Node) of
|
[] -> [];
|
||||||
[] ->
|
_ -> [{"node", SNode}]
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
|
end,
|
||||||
_ ->
|
IQ#iq{type = result,
|
||||||
IQ#iq{type = result, sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "query",
|
||||||
[{"xmlns", XMLNS},
|
[{"xmlns", ?NS_DISCO_INFO} | ANode],
|
||||||
{"node", Node}], []}]}
|
Identity ++
|
||||||
end;
|
lists:map(fun feature_to_xml/1, Features)
|
||||||
{_, get, _, _} ->
|
}]};
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]};
|
{error, Error} ->
|
||||||
_ ->
|
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc.
|
||||||
|
|
||||||
|
get_sm_features(empty, From, To, _Node, _Lang) ->
|
||||||
|
#jid{luser = LFrom, lserver = LSFrom} = From,
|
||||||
|
#jid{luser = LTo, lserver = LSTo} = To,
|
||||||
|
case {LFrom, LSFrom} of
|
||||||
|
{LTo, LSTo} ->
|
||||||
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
|
_ ->
|
||||||
|
{error, ?ERR_NOT_ALLOWED}
|
||||||
|
end;
|
||||||
|
|
||||||
|
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc.
|
||||||
|
|
||||||
|
|
||||||
get_user_resources(User) ->
|
|
||||||
Rs = ejabberd_sm:get_user_resources(User, 'TODO'),
|
get_user_resources(User, Server) ->
|
||||||
|
Rs = ejabberd_sm:get_user_resources(User, Server),
|
||||||
lists:map(fun(R) ->
|
lists:map(fun(R) ->
|
||||||
{xmlelement, "item",
|
{xmlelement, "item",
|
||||||
[{"jid", User ++ "@" ++ ?MYNAME ++ "/" ++ R},
|
[{"jid", User ++ "@" ++ Server ++ "/" ++ R},
|
||||||
{"name", User}], []}
|
{"name", User}], []}
|
||||||
end, lists:sort(Rs)).
|
end, lists:sort(Rs)).
|
||||||
|
|
||||||
node_to_xml(User, Node, Name) ->
|
|
||||||
{xmlelement, "item", [{"jid", User ++ "@" ++ ?MYNAME},
|
|
||||||
{"node", Node},
|
|
||||||
{"name", Name}], []}.
|
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
-export([start/2, init/3, stop/1,
|
-export([start/2, init/3, stop/1,
|
||||||
|
get_sm_features/5,
|
||||||
process_local_iq/3,
|
process_local_iq/3,
|
||||||
process_sm_iq/3,
|
process_sm_iq/3,
|
||||||
reindex_vcards/0,
|
reindex_vcards/0,
|
||||||
@ -69,7 +70,7 @@ start(Host, Opts) ->
|
|||||||
?MODULE, process_local_iq, IQDisc),
|
?MODULE, process_local_iq, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
|
||||||
?MODULE, process_sm_iq, IQDisc),
|
?MODULE, process_sm_iq, IQDisc),
|
||||||
catch mod_disco:register_sm_feature(Host, ?NS_VCARD),
|
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
|
||||||
MyHost = gen_mod:get_opt(host, Opts, "vjud." ++ Host),
|
MyHost = gen_mod:get_opt(host, Opts, "vjud." ++ Host),
|
||||||
Search = gen_mod:get_opt(search, Opts, true),
|
Search = gen_mod:get_opt(search, Opts, true),
|
||||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
@ -107,11 +108,27 @@ stop(Host) ->
|
|||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
|
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
|
||||||
catch mod_disco:unregister_sm_feature(Host, ?NS_VCARD),
|
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
|
||||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
Proc ! stop,
|
Proc ! stop,
|
||||||
{wait, Proc}.
|
{wait, Proc}.
|
||||||
|
|
||||||
|
get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
get_sm_features(Acc, _From, _To, Node, _Lang) ->
|
||||||
|
case Node of
|
||||||
|
[] ->
|
||||||
|
case Acc of
|
||||||
|
{result, Features} ->
|
||||||
|
{result, [?NS_VCARD | Features]};
|
||||||
|
empty ->
|
||||||
|
{result, [?NS_VCARD]}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end.
|
||||||
|
|
||||||
process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
set ->
|
set ->
|
||||||
@ -140,7 +157,7 @@ process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
|
|||||||
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
set ->
|
set ->
|
||||||
#jid{user = User, lserver = LServer, luser = LUser} = From,
|
#jid{user = User, lserver = LServer} = From,
|
||||||
case lists:member(LServer, ?MYHOSTS) of
|
case lists:member(LServer, ?MYHOSTS) of
|
||||||
true ->
|
true ->
|
||||||
set_vcard(User, LServer, SubEl),
|
set_vcard(User, LServer, SubEl),
|
||||||
@ -327,7 +344,7 @@ do_route(ServerHost, From, To, Packet) ->
|
|||||||
From,
|
From,
|
||||||
jlib:iq_to_xml(ResIQ))
|
jlib:iq_to_xml(ResIQ))
|
||||||
end;
|
end;
|
||||||
#iq{type = Type, xmlns = ?NS_DISCO_INFO, sub_el = SubEl} ->
|
#iq{type = Type, xmlns = ?NS_DISCO_INFO} ->
|
||||||
case Type of
|
case Type of
|
||||||
set ->
|
set ->
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
@ -355,7 +372,7 @@ do_route(ServerHost, From, To, Packet) ->
|
|||||||
From,
|
From,
|
||||||
jlib:iq_to_xml(ResIQ))
|
jlib:iq_to_xml(ResIQ))
|
||||||
end;
|
end;
|
||||||
#iq{type = Type, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} ->
|
#iq{type = Type, xmlns = ?NS_DISCO_ITEMS} ->
|
||||||
case Type of
|
case Type of
|
||||||
set ->
|
set ->
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
-export([start/2, init/3, stop/1,
|
-export([start/2, init/3, stop/1,
|
||||||
|
get_sm_features/5,
|
||||||
process_local_iq/3,
|
process_local_iq/3,
|
||||||
process_sm_iq/3,
|
process_sm_iq/3,
|
||||||
remove_user/1]).
|
remove_user/1]).
|
||||||
@ -29,6 +30,7 @@ start(Host, Opts) ->
|
|||||||
?MODULE, process_local_iq, IQDisc),
|
?MODULE, process_local_iq, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
|
||||||
?MODULE, process_sm_iq, IQDisc),
|
?MODULE, process_sm_iq, IQDisc),
|
||||||
|
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
|
||||||
LDAPServers = ejabberd_config:get_local_option({ldap_servers, Host}),
|
LDAPServers = ejabberd_config:get_local_option({ldap_servers, Host}),
|
||||||
RootDN = ejabberd_config:get_local_option({ldap_rootdn, Host}),
|
RootDN = ejabberd_config:get_local_option({ldap_rootdn, Host}),
|
||||||
Password = ejabberd_config:get_local_option({ldap_password, Host}),
|
Password = ejabberd_config:get_local_option({ldap_password, Host}),
|
||||||
@ -67,10 +69,26 @@ loop(Host, ServerHost) ->
|
|||||||
stop(Host) ->
|
stop(Host) ->
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
|
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
|
||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
|
||||||
|
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
|
||||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
Proc ! stop,
|
Proc ! stop,
|
||||||
{wait, Proc}.
|
{wait, Proc}.
|
||||||
|
|
||||||
|
get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||||
|
Acc;
|
||||||
|
get_sm_features(Acc, _From, _To, Node, _Lang) ->
|
||||||
|
case Node of
|
||||||
|
[] ->
|
||||||
|
case Acc of
|
||||||
|
{result, Features} ->
|
||||||
|
{result, [?NS_VCARD | Features]};
|
||||||
|
empty ->
|
||||||
|
{result, [?NS_VCARD]}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end.
|
||||||
|
|
||||||
process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
set ->
|
set ->
|
||||||
@ -180,16 +198,6 @@ ldap_attributes_to_vcard(Attributes,From,To) ->
|
|||||||
{xmlelement,"ORG",[],FOElts}])
|
{xmlelement,"ORG",[],FOElts}])
|
||||||
}].
|
}].
|
||||||
|
|
||||||
is_self_request(From,To) ->
|
|
||||||
#jid{luser = RUser, lserver = RServer } = From,
|
|
||||||
#jid{luser = LUser} = To,
|
|
||||||
case RServer == ?MYNAME of
|
|
||||||
true ->
|
|
||||||
LUser == RUser;
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
set ->
|
set ->
|
||||||
|
Loading…
Reference in New Issue
Block a user