diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 86b6d3b49..63d136a2f 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -23,7 +23,8 @@ try_register/2]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + dirty_get_registered_users/0]). -record(state, {}). @@ -158,3 +159,6 @@ try_register(User, Password) -> end, mnesia:transaction(F). +dirty_get_registered_users() -> + mnesia:dirty_all_keys(passwd). + diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index b75ffe08c..c42a8a019 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -22,13 +22,14 @@ start() -> register(ejabberd_local, spawn(ejabberd_local, init, [])), mod_register:start(), mod_roster:start(), + mod_disco:start(), ok. init() -> MyDomain = ?MYNAME, ejabberd_router:register_local_route(MyDomain), loop(#state{mydomain = MyDomain, - iqtable = ets:new(iqtable, [])}). + iqtable = ets:new(local_iqtable, [named_table])}). loop(State) -> receive @@ -37,6 +38,7 @@ loop(State) -> loop(State); {register_iq_handler, XMLNS, Module, Function} -> ets:insert(State#state.iqtable, {XMLNS, Module, Function}), + mod_disco:register_feature(XMLNS), loop(State) end. diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 9ca416801..8c092eb39 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -13,7 +13,9 @@ -export([start/0, init/0, open_session/2, close_session/2, get_user_resources/1, set_presence/3, - unset_presence/2]). + unset_presence/2, + dirty_get_sessions_list/0, + register_iq_handler/3]). -include_lib("mnemosyne/include/mnemosyne.hrl"). -include("ejabberd.hrl"). @@ -41,6 +43,7 @@ init() -> {attributes, record_info(fields, presence)}]), mnesia:add_table_index(presence, user), mnesia:subscribe(system), + ets:new(sm_iqtable, [named_table]), loop(). loop() -> @@ -61,6 +64,9 @@ loop() -> {route, From, To, Packet} -> do_route(From, To, Packet), loop(); + {register_iq_handler, XMLNS, Module, Function} -> + ets:insert(sm_iqtable, {XMLNS, Module, Function}), + loop(); _ -> loop() end. @@ -231,6 +237,7 @@ do_route(From, To, Packet) -> Packet} end; "iq" -> + process_iq(From, To, Packet), % TODO ok; "broadcast" -> @@ -313,3 +320,41 @@ get_user_present_resources(User) -> [] end. +dirty_get_sessions_list() -> + mnesia:dirty_all_keys(session). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +process_iq(From, To, Packet) -> + IQ = jlib:iq_query_info(Packet), + case IQ of + {iq, ID, Type, XMLNS, SubEl} -> + case ets:lookup(sm_iqtable, XMLNS) of + [{_, Module, Function}] -> + ResIQ = apply(Module, Function, [From, To, IQ]), + if + ResIQ /= ignore -> + ejabberd_router ! {route, + To, + From, + jlib:iq_to_xml(ResIQ)}; + true -> + ok + end; + [] -> + Err = jlib:make_error_reply( + Packet, "501", "Not Implemented"), + ejabberd_router ! {route, To, From, Err} + end; + reply -> + ok; + _ -> + Err = jlib:make_error_reply(Packet, "400", "Bad Request"), + ejabberd_router ! {route, To, From, Err}, + ok + end. + +register_iq_handler(XMLNS, Module, Fun) -> + ejabberd_sm ! {register_iq_handler, XMLNS, Module, Fun}. + diff --git a/src/mod_disco.erl b/src/mod_disco.erl new file mode 100644 index 000000000..16512ddf5 --- /dev/null +++ b/src/mod_disco.erl @@ -0,0 +1,202 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_disco.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 1 Jan 2003 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(mod_disco). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([start/0, init/0, + process_local_iq_items/3, + process_local_iq_info/3, + process_sm_iq_items/3, + process_sm_iq_info/3, + register_feature/1]). + +-include("ejabberd.hrl"). + +-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items"). +-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info"). + +-define(EMPTY_INFO_RESULT, + {iq, ID, result, XMLNS, [{xmlelement, "query", + [{"xmlns", ?NS_DISCO_INFO}], []}]}). + +start() -> + ejabberd_local:register_iq_handler(?NS_DISCO_ITEMS, + ?MODULE, process_local_iq_items), + ejabberd_local:register_iq_handler(?NS_DISCO_INFO, + ?MODULE, process_local_iq_info), + ejabberd_sm:register_iq_handler(?NS_DISCO_ITEMS, + ?MODULE, process_sm_iq_items), + ejabberd_sm:register_iq_handler(?NS_DISCO_INFO, + ?MODULE, process_sm_iq_info), + register_feature("iq"), + register_feature("presence"), + register_feature("presence-invisible"), + ok. + +init() -> + ok. + +register_feature(Feature) -> + catch ets:new(disco_features, [named_table, ordered_set, public]), + ets:insert(disco_features, {Feature}). + +process_local_iq_items(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + case Type of + set -> + {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error", + [{"code", "405"}], + [{xmlcdata, "Not Allowed"}]}]}; + get -> + case xml:get_tag_attr_s("node", SubEl) of + "" -> + Domains = [], + {iq, ID, result, XMLNS, + [{xmlelement, + "query", + [{"xmlns", ?NS_DISCO_ITEMS}], + Domains ++ + [{xmlelement, "item", + [{"jid", jlib:jid_to_string(To)}, + {"name", "Online Users"}, + {"node", "online users"}], []}, + {xmlelement, "item", + [{"jid", jlib:jid_to_string(To)}, + {"name", "All Users"}, + {"node", "all users"}], []}] + }]}; + "online users" -> + {iq, ID, result, XMLNS, + [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}], + get_online_users() + }]}; + "all users" -> + {iq, ID, result, XMLNS, + [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}], + get_all_users() + }]}; + _ -> + {iq, ID, error, XMLNS, + [SubEl, {xmlelement, "error", + [{"code", "501"}], + [{xmlcdata, "Not Implemented"}]}]} + end + end. + + +process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + case Type of + set -> + {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error", + [{"code", "405"}], + [{xmlcdata, "Not Allowed"}]}]}; + get -> + case xml:get_tag_attr_s("node", SubEl) of + "" -> + Features = lists:map(fun feature_to_xml/1, + ets:tab2list(disco_features)), + {iq, ID, result, XMLNS, [{xmlelement, + "query", + [{"xmlns", ?NS_DISCO_INFO}], + [{xmlelement, "identity", + [{"category", "service"}, + {"type", "jabber"}, + {"name", "ejabberd"}], []}] ++ + Features + }]}; + "online users" -> ?EMPTY_INFO_RESULT; + "all users" -> ?EMPTY_INFO_RESULT; + _ -> + {iq, ID, error, XMLNS, + [SubEl, {xmlelement, "error", + [{"code", "501"}], + [{xmlcdata, "Not Implemented"}]}]} + end + end. + + +feature_to_xml({Feature}) -> + {xmlelement, "feature", [{"var", Feature}], []}. + + +get_online_users() -> + case catch ejabberd_sm:dirty_get_sessions_list() of + {'EXIT', Reason} -> + []; + URs -> + lists:map(fun({U, R}) -> + {xmlelement, "item", + [{"jid", U ++ "@" ++ ?MYNAME ++ "/" ++ R}, + {"name", U}], []} + end, lists:sort(URs)) + end. + +get_all_users() -> + case catch ejabberd_auth:dirty_get_registered_users() of + {'EXIT', Reason} -> + []; + Users -> + lists:map(fun(U) -> + {xmlelement, "item", + [{"jid", U ++ "@" ++ ?MYNAME}, + {"name", U}], []} + end, lists:sort(Users)) + end. + + +process_sm_iq_items(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + {User, _, _} = To, + case Type of + set -> + {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error", + [{"code", "405"}], + [{xmlcdata, "Not Allowed"}]}]}; + get -> + case xml:get_tag_attr_s("node", SubEl) of + "" -> + {iq, ID, result, XMLNS, + [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}], + get_user_resources(User) + }]}; + _ -> + {iq, ID, error, XMLNS, + [SubEl, {xmlelement, "error", + [{"code", "501"}], + [{xmlcdata, "Not Implemented"}]}]} + end + end. + + +process_sm_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + case Type of + set -> + {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error", + [{"code", "405"}], + [{xmlcdata, "Not Allowed"}]}]}; + get -> + case xml:get_tag_attr_s("node", SubEl) of + "" -> ?EMPTY_INFO_RESULT; + _ -> + {iq, ID, error, XMLNS, + [SubEl, {xmlelement, "error", + [{"code", "501"}], + [{xmlcdata, "Not Implemented"}]}]} + end + end. + + + +get_user_resources(User) -> + Rs = ejabberd_sm:get_user_resources(User), + lists:map(fun(R) -> + {xmlelement, "item", + [{"jid", User ++ "@" ++ ?MYNAME ++ "/" ++ R}, + {"name", User}], []} + end, lists:sort(Rs)). + diff --git a/src/xml.erl b/src/xml.erl index 3bdcb62f7..171607361 100644 --- a/src/xml.erl +++ b/src/xml.erl @@ -12,7 +12,8 @@ -export([element_to_string/1, crypt/1, remove_cdata/1, get_cdata/1, get_tag_cdata/1, - get_attr/2, get_attr_s/2]). + get_attr/2, get_attr_s/2, + get_tag_attr/2, get_tag_attr_s/2]). element_to_string(El) -> case El of @@ -93,4 +94,9 @@ get_attr_s(AttrName, Attrs) -> "" end. +get_tag_attr(AttrName, {xmlelement, Name, Attrs, Els}) -> + get_attr(AttrName, Attrs). + +get_tag_attr_s(AttrName, {xmlelement, Name, Attrs, Els}) -> + get_attr_s(AttrName, Attrs).