diff options
author | Alexey Shchepin <alexey@process-one.net> | 2005-09-04 01:58:47 +0000 |
---|---|---|
committer | Alexey Shchepin <alexey@process-one.net> | 2005-09-04 01:58:47 +0000 |
commit | 83191198a15690e3caed559f1dacf69461d4f761 (patch) | |
tree | e18e3ebfe85113af648ba6f16198c546b5db7af7 /src/mod_disco.erl | |
parent | * src/ejd2odbc.erl: Converter from mnesia to ODBC (diff) |
* 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
Diffstat (limited to '')
-rw-r--r-- | src/mod_disco.erl | 665 |
1 files changed, 195 insertions, 470 deletions
diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 511b72ea4..b25f79029 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -16,26 +16,22 @@ stop/1, process_local_iq_items/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_info/3, + get_sm_identity/5, + get_sm_features/5, + get_sm_items/5, register_feature/2, unregister_feature/2, register_extra_domain/2, - unregister_extra_domain/2, - register_sm_feature/2, - unregister_sm_feature/2, - register_sm_node/4, - unregister_sm_node/1]). + unregister_extra_domain/2]). -include("ejabberd.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) -> ejabberd_local:refresh_iq_handlers(), @@ -60,9 +56,21 @@ start(Host, Opts) -> ExtraDomains), catch ets:new(disco_sm_features, [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. 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_INFO), 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, "/"), Host = To#jid.lserver, - case acl:match_rule(Host, configure, From) of - deny when Node /= [] -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; - deny -> + case ejabberd_hooks:run_fold(disco_local_items, + Host, + empty, + [From, To, Node, Lang]) of + {result, Items} -> + ANode = case Node of + [] -> []; + _ -> [{"node", SNode}] + end, IQ#iq{type = result, sub_el = [{xmlelement, "query", - [{"xmlns", ?NS_DISCO_ITEMS}], - get_services_only(Host) + [{"xmlns", ?NS_DISCO_ITEMS} | ANode], + Items }]}; - _ -> - case get_local_items(Host, Node, - 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 + {error, Error} -> + IQ#iq{type = error, sub_el = [SubEl, Error]} 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) -> case Type of set -> IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; get -> - LServer = To#jid.lserver, + Host = To#jid.lserver, SNode = xml:get_tag_attr_s("node", SubEl), Node = string:tokens(SNode, "/"), - case {acl:match_rule(LServer, configure, From), Node} of - {_, []} -> - Features = lists:map( - fun feature_to_xml/1, - ets:select(disco_features, - [{{{'$1', LServer}}, - [], - ['$1']}])), - IQ#iq{type = result, - sub_el = [{xmlelement, - "query", - [{"xmlns", ?NS_DISCO_INFO}], - [{xmlelement, "identity", - [{"category", "server"}, - {"type", "im"}, - {"name", "ejabberd"}], []}] ++ - Features - }]}; - {deny, _} -> - IQ#iq{type = error, sub_el = [SubEl, ?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]} -> - 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", _]} -> + Identity = ejabberd_hooks:run_fold(disco_local_identity, + Host, + [], + [From, To, Node, Lang]), + case ejabberd_hooks:run_fold(disco_local_features, + Host, + empty, + [From, To, Node, Lang]) of + {result, Features} -> + ANode = case Node of + [] -> []; + _ -> [{"node", SNode}] + end, 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]} + [{"xmlns", ?NS_DISCO_INFO} | ANode], + Identity ++ + lists:map(fun feature_to_xml/1, Features) + }]}; + {error, Error} -> + IQ#iq{type = error, sub_el = [SubEl, Error]} 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); @@ -225,140 +196,27 @@ domain_to_xml({Domain}) -> domain_to_xml(Domain) -> {xmlelement, "item", [{"jid", Domain}], []}. --define(NODE(Name, Node), - {xmlelement, "item", - [{"jid", Server}, - {"name", translate:translate(Lang, Name)}, - {"node", Node}], []}). - - -get_services_only(Host) -> - 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']}])). - -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, - Domains ++ - [?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_services({error, _Error} = Acc, _From, _To, _Node, _Lang) -> + Acc; -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) -> +get_local_services(Acc, _From, To, [], _Lang) -> + Items = case Acc of + {result, Its} -> Its; + empty -> [] + end, + Host = To#jid.lserver, {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}. + lists:usort( + lists:map(fun domain_to_xml/1, + get_vh_services(Host) ++ + ets:tab2list(disco_extra_domains)) + ) ++ Items}; +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) -> Hosts = lists:sort(fun(H1, H2) -> length(H1) >= length(H2) end, ?MYHOSTS), @@ -374,253 +232,120 @@ get_vh_services(Host) -> end 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) -> - 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)) +process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) -> + case Type of + set -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; + get -> + Host = To#jid.lserver, + SNode = xml:get_tag_attr_s("node", SubEl), + Node = string:tokens(SNode, "/"), + case ejabberd_hooks:run_fold(disco_sm_items, + Host, + empty, + [From, To, Node, Lang]) of + {result, Items} -> + ANode = case Node of + [] -> []; + _ -> [{"node", SNode}] + end, + IQ#iq{type = result, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_DISCO_ITEMS} | ANode], + Items + }]}; + {error, Error} -> + IQ#iq{type = error, sub_el = [SubEl, Error]} 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)) +get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) -> + Acc; + +get_sm_items(Acc, + #jid{luser = LFrom, lserver = LSFrom} = _From, + #jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To, + [], _Lang) -> + Items = case Acc of + {result, Its} -> Its; + empty -> [] + end, + Items1 = case {LFrom, LSFrom} of + {LTo, LSTo} -> get_user_resources(User, Server); + _ -> [] + end, + {result, Items ++ Items1}; + +get_sm_items({result, _} = Acc, _From, _To, _Node, _Lang) -> + Acc; + +get_sm_items(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. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -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, _, _} -> +process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) -> + case Type 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]} + get -> + Host = To#jid.lserver, + SNode = xml:get_tag_attr_s("node", SubEl), + Node = string:tokens(SNode, "/"), + Identity = ejabberd_hooks:run_fold(disco_sm_identity, + Host, + [], + [From, To, Node, Lang]), + case ejabberd_hooks:run_fold(disco_sm_features, + Host, + empty, + [From, To, Node, Lang]) of + {result, Features} -> + ANode = case Node of + [] -> []; + _ -> [{"node", SNode}] + end, + IQ#iq{type = result, + sub_el = [{xmlelement, "query", + [{"xmlns", ?NS_DISCO_INFO} | ANode], + Identity ++ + lists:map(fun feature_to_xml/1, Features) + }]}; + {error, Error} -> + IQ#iq{type = error, sub_el = [SubEl, Error]} + end end. +get_sm_identity(Acc, _From, _To, _Node, _Lang) -> + Acc. -process_sm_iq_info(From, To, #iq{type = Type, xmlns = XMLNS, - sub_el = SubEl} = IQ) -> - #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]}; - {allow, get, _, []} -> - Features = lists:map(fun feature_to_xml/1, - ets:tab2list(disco_sm_features)), - IQ#iq{type = result, - sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], - [feature_to_xml(?NS_EJABBERD_CONFIG)] ++ - Features}]}; - {_, get, _, []} -> - Features = lists:map(fun feature_to_xml/1, - ets:tab2list(disco_sm_features)), - IQ#iq{type = result, - sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], - Features}]}; - {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]}; - _ -> - IQ#iq{type = result, sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}, - {"node", Node}], []}]} - end; - {_, get, _, _} -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}; +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}; _ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]} - end. + {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) -> {xmlelement, "item", - [{"jid", User ++ "@" ++ ?MYNAME ++ "/" ++ R}, + [{"jid", User ++ "@" ++ Server ++ "/" ++ R}, {"name", User}], []} end, lists:sort(Rs)). -node_to_xml(User, Node, Name) -> - {xmlelement, "item", [{"jid", User ++ "@" ++ ?MYNAME}, - {"node", Node}, - {"name", Name}], []}. - |