diff options
Diffstat (limited to 'src/mod_admin_extra.erl')
-rw-r--r-- | src/mod_admin_extra.erl | 402 |
1 files changed, 242 insertions, 160 deletions
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 48732ea35..4b117d50a 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -5,7 +5,7 @@ %%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net> %%% %%% -%%% ejabberd, Copyright (C) 2002-2008 ProcessOne +%%% ejabberd, Copyright (C) 2002-2016 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -30,31 +30,61 @@ -include("logger.hrl"). --export([start/2, stop/1, compile/1, get_cookie/0, - remove_node/1, set_password/3, check_password/3, - check_password_hash/4, delete_old_users/1, - delete_old_users_vhost/2, ban_account/3, +-export([start/2, stop/1, mod_opt_type/1, + get_commands_spec/0, depends/2]). + +% Commands API +-export([ + % Adminsys + compile/1, get_cookie/0, remove_node/1, export2sql/2, + restart_module/2, + + % Sessions num_active_users/2, num_resources/2, resource_num/3, kick_session/4, status_num/2, status_num/1, status_list/2, status_list/1, connected_users_info/0, connected_users_vhost/1, set_presence/7, - user_sessions_info/2, set_nickname/3, get_vcard/3, + get_presence/2, user_sessions_info/2, get_last/2, + + % Accounts + set_password/3, check_password_hash/4, delete_old_users/1, + delete_old_users_vhost/2, ban_account/3, check_password/3, + + % vCard + set_nickname/3, get_vcard/3, get_vcard/4, get_vcard_multi/4, set_vcard/4, - set_vcard/5, add_rosteritem/7, delete_rosteritem/4, + set_vcard/5, + + % Roster + add_rosteritem/7, delete_rosteritem/4, process_rosteritems/5, get_roster/2, push_roster/3, - push_roster_all/1, push_alltoall/2, get_last/2, - private_get/4, private_set/3, srg_create/5, + push_roster_all/1, push_alltoall/2, + + % Private storage + private_get/4, private_set/3, + + % Shared roster + srg_create/5, srg_delete/2, srg_list/1, srg_get_info/2, srg_get_members/2, srg_user_add/4, srg_user_del/4, - send_message/5, send_stanza/3, send_stanza_c2s/4, privacy_set/3, - stats/1, stats/2, mod_opt_type/1, get_commands_spec/0, depends/2]). + + % Send message + send_message/5, send_stanza/3, send_stanza_c2s/4, + + % Privacy list + privacy_set/3, + + % Stats + stats/1, stats/2 + ]). -include("ejabberd.hrl"). -include("ejabberd_commands.hrl"). -include("mod_roster.hrl"). +-include("mod_privacy.hrl"). -include("ejabberd_sm.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). %%% %%% gen_mod @@ -124,9 +154,30 @@ get_commands_spec() -> result = {res, rescode}, result_example = ok, result_desc = "Status code: 0 on success, 1 otherwise"}, + #ejabberd_commands{name = export2sql, tags = [mnesia], + desc = "Export Mnesia tables to files in directory", + module = ?MODULE, function = export2sql, + args = [{host, string}, {path, string}], + args_example = ["myserver.com","/tmp/export/sql"], + args_desc = ["Server name", "File to write sql export"], + result = {res, rescode}, + result_example = ok, + result_desc = "Status code: 0 on success, 1 otherwise"}, + #ejabberd_commands{name = restart_module, tags = [erlang], + desc = "Stop an ejabberd module, reload code and start", + module = ?MODULE, function = restart_module, + args = [{host, binary}, {module, binary}], + args_example = ["myserver.com","mod_admin_extra"], + args_desc = ["Server name", "Module to restart"], + result = {res, integer}, + result_example = 0, + result_desc = "Returns integer code:\n" + " - 0: code reloaded, module restarted\n" + " - 1: error: module not loaded\n" + " - 2: code not reloaded, but module restarted"}, #ejabberd_commands{name = num_active_users, tags = [accounts, stats], desc = "Get number of users active in the last days", - policy = admin, + policy = admin, module = ?MODULE, function = num_active_users, args = [{host, binary}, {days, integer}], args_example = [<<"myserver.com">>, 3], @@ -175,8 +226,8 @@ get_commands_spec() -> desc = "Check if the password hash is correct", longdesc = "Allowed hash methods: md5, sha.", module = ?MODULE, function = check_password_hash, - args = [{user, binary}, {host, binary}, {passwordhash, string}, - {hashmethod, string}], + args = [{user, binary}, {host, binary}, {passwordhash, binary}, + {hashmethod, binary}], args_example = [<<"peter">>, <<"myserver.com">>, <<"5ebe2294ecd0e0f08eab7690d2a6ee69">>, <<"md5">>], args_desc = ["User name to check", "Server to check", @@ -235,7 +286,7 @@ get_commands_spec() -> result_desc = "Status code: 0 on success, 1 otherwise"}, #ejabberd_commands{name = status_num_host, tags = [session, stats], desc = "Number of logged users with this status in host", - policy = admin, + policy = admin, module = ?MODULE, function = status_num, args = [{host, binary}, {status, binary}], args_example = [<<"myserver.com">>, <<"dnd">>], @@ -245,7 +296,7 @@ get_commands_spec() -> result_desc = "Number of connected sessions with given status type"}, #ejabberd_commands{name = status_num, tags = [session, stats], desc = "Number of logged users with this status", - policy = admin, + policy = admin, module = ?MODULE, function = status_num, args = [{status, binary}], args_example = [<<"dnd">>], @@ -297,11 +348,11 @@ get_commands_spec() -> ]}} }}}, #ejabberd_commands{name = connected_users_vhost, - tags = [session], - desc = "Get the list of established sessions in a vhost", - module = ?MODULE, function = connected_users_vhost, - args = [{host, binary}], - result = {connected_users_vhost, {list, {sessions, string}}}}, + tags = [session], + desc = "Get the list of established sessions in a vhost", + module = ?MODULE, function = connected_users_vhost, + args = [{host, binary}], + result = {connected_users_vhost, {list, {sessions, string}}}}, #ejabberd_commands{name = user_sessions_info, tags = [session], desc = "Get information about all sessions of a user", @@ -322,6 +373,28 @@ get_commands_spec() -> ]}} }}}, + #ejabberd_commands{name = get_presence, tags = [session], + desc = + "Retrieve the resource with highest priority, " + "and its presence (show and status message) " + "for a given user.", + longdesc = + "The 'jid' value contains the user jid " + "with resource.\nThe 'show' value contains " + "the user presence flag. It can take " + "limited values:\n - available\n - chat " + "(Free for chat)\n - away\n - dnd (Do " + "not disturb)\n - xa (Not available, " + "extended away)\n - unavailable (Not " + "connected)\n\n'status' is a free text " + "defined by the user client.", + module = ?MODULE, function = get_presence, + args = [{user, binary}, {server, binary}], + result = + {presence, + {tuple, + [{jid, string}, {show, string}, + {status, string}]}}}, #ejabberd_commands{name = set_presence, tags = [session], desc = "Set presence of a session", @@ -533,7 +606,7 @@ get_commands_spec() -> #ejabberd_commands{name = get_offline_count, tags = [offline], desc = "Get the number of unread offline messages", - policy = user, + policy = user, module = mod_offline, function = count_offline_messages, args = [], result = {value, integer}}, @@ -561,13 +634,13 @@ get_commands_spec() -> #ejabberd_commands{name = stats, tags = [stats], desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds processes", - policy = admin, + policy = admin, module = ?MODULE, function = stats, args = [{name, binary}], result = {stat, integer}}, #ejabberd_commands{name = stats_host, tags = [stats], desc = "Get statistical value for this host: registeredusers onlineusers", - policy = admin, + policy = admin, module = ?MODULE, function = stats, args = [{name, binary}, {host, binary}], result = {stat, integer}} @@ -588,6 +661,48 @@ remove_node(Node) -> mnesia:del_table_copy(schema, list_to_atom(Node)), ok. +restart_module(Host, Module) when is_binary(Module) -> + restart_module(Host, jlib:binary_to_atom(Module)); +restart_module(Host, Module) when is_atom(Module) -> + List = gen_mod:loaded_modules_with_opts(Host), + case proplists:get_value(Module, List) of + undefined -> + % not a running module, force code reload anyway + code:purge(Module), + code:delete(Module), + code:load_file(Module), + 1; + Opts -> + gen_mod:stop_module(Host, Module), + case code:soft_purge(Module) of + true -> + code:delete(Module), + code:load_file(Module), + gen_mod:start_module(Host, Module, Opts), + 0; + false -> + gen_mod:start_module(Host, Module, Opts), + 2 + end + end. + +export2sql(Host, Directory) -> + Tables = [{export_last, last}, + {export_offline, offline}, + {export_passwd, passwd}, + {export_private_storage, private_storage}, + {export_roster, roster}, + {export_vcard, vcard}, + {export_vcard_search, vcard_search}], + Export = fun({TableFun, Table}) -> + Filename = filename:join([Directory, atom_to_list(Table)++".txt"]), + io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]), + Res = (catch ejd2sql:TableFun(Host, Filename)), + io:format(" Result: ~p~n", [Res]) + end, + lists:foreach(Export, Tables), + ok. + %%% %%% Accounts %%% @@ -606,10 +721,10 @@ check_password_hash(User, Host, PasswordHash, HashMethod) -> {A, _} when is_tuple(A) -> scrammed; {_, <<"md5">>} -> get_md5(AccountPass); {_, <<"sha">>} -> get_sha(AccountPass); - {_, Method} -> + {_, Method} -> ?ERROR_MSG("check_password_hash called " - "with hash method: ~p", [Method]), - undefined + "with hash method: ~p", [Method]), + undefined end, case AccountPassHash of scrammed -> @@ -627,10 +742,11 @@ get_sha(AccountPass) -> || X <- binary_to_list(p1_sha:sha1(AccountPass))]). num_active_users(Host, Days) -> - list_last_activity(Host, true, Days). + DB_Type = gen_mod:db_type(Host, mod_last), + list_last_activity(Host, true, Days, DB_Type). %% Code based on ejabberd/src/web/ejabberd_web_admin.erl -list_last_activity(Host, Integral, Days) -> +list_last_activity(Host, Integral, Days, mnesia) -> TimeStamp = p1_time_compat:system_time(seconds), TS = TimeStamp - Days * 86400, case catch mnesia:dirty_select( @@ -656,7 +772,11 @@ list_last_activity(Host, Integral, Days) -> end, lists:nth(Days, Hist ++ Tail) end - end. + end; +list_last_activity(_Host, _Integral, _Days, DB_Type) -> + throw({error, iolist_to_binary(io_lib:format("Unsupported backend: ~p", + [DB_Type]))}). + histogram(Values, Integral) -> histogram(lists:sort(Values), Integral, 0, 0, []). histogram([H | T], Integral, Current, Count, Hist) when Current == H -> @@ -760,7 +880,9 @@ set_random_password(User, Server, Reason) -> set_password_auth(User, Server, NewPass). build_random_password(Reason) -> - Date = jlib:timestamp_to_legacy(calendar:universal_time()), + {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:universal_time(), + Date = str:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B", + [Year, Month, Day, Hour, Minute, Second]), RandomString = randoms:get_string(), <<"BANNED_ACCOUNT--", Date/binary, "--", RandomString/binary, "--", Reason/binary>>. @@ -865,9 +987,9 @@ connected_users_vhost(Host) -> %% Code copied from ejabberd_sm.erl and customized dirty_get_sessions_list2() -> Ss = mnesia:dirty_select( - session, + session, [{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4', - _ = '_'}, + _ = '_'}, [], [['$1', '$2', '$3', '$4']]}]), lists:filter(fun([_USR, _SID, _Priority, Info]) -> @@ -879,24 +1001,40 @@ stringize(String) -> %% Replace newline characters with other code ejabberd_regexp:greplace(String, <<"\n">>, <<"\\n">>). +get_presence(U, S) -> + Pids = [ejabberd_sm:get_session_pid(U, S, R) + || R <- ejabberd_sm:get_user_resources(U, S)], + OnlinePids = [Pid || Pid <- Pids, Pid=/=none], + case OnlinePids of + [] -> + {jid:to_string({U, S, <<>>}), <<"unavailable">>, <<"">>}; + [SessionPid|_] -> + {_User, Resource, Show, Status} = + ejabberd_c2s:get_presence(SessionPid), + FullJID = jid:to_string({U, S, Resource}), + {FullJID, Show, Status} + end. + set_presence(User, Host, Resource, Type, Show, Status, Priority) when is_integer(Priority) -> BPriority = integer_to_binary(Priority), set_presence(User, Host, Resource, Type, Show, Status, BPriority); -set_presence(User, Host, Resource, Type, Show, Status, Priority) -> +set_presence(User, Host, Resource, Type, Show, Status, Priority0) -> + Priority = if is_integer(Priority0) -> Priority0; + true -> binary_to_integer(Priority0) + end, case ejabberd_sm:get_session_pid(User, Host, Resource) of none -> error; Pid -> - USR = jid:to_string(jid:make(User, Host, Resource)), - US = jid:to_string(jid:make(User, Host, <<>>)), - Message = {route_xmlstreamelement, - {xmlel, <<"presence">>, - [{<<"from">>, USR}, {<<"to">>, US}, {<<"type">>, Type}], - [{xmlel, <<"show">>, [], [{xmlcdata, Show}]}, - {xmlel, <<"status">>, [], [{xmlcdata, Status}]}, - {xmlel, <<"priority">>, [], [{xmlcdata, Priority}]}]}}, - Pid ! Message, + From = jid:make(User, Host, Resource), + To = jid:make(User, Host), + Presence = #presence{from = From, to = To, + type = jlib:binary_to_atom(Type), + show = jlib:binary_to_atom(Show), + status = xmpp:mk_text(Status), + priority = Priority}, + Pid ! {route, From, To, Presence}, ok end. @@ -934,20 +1072,12 @@ user_sessions_info(User, Host) -> %%% set_nickname(User, Host, Nickname) -> - R = mod_vcard:process_sm_iq( - {jid, User, Host, <<>>, User, Host, <<>>}, - {jid, User, Host, <<>>, User, Host, <<>>}, - {iq, <<>>, set, <<>>, <<"en">>, - {xmlel, <<"vCard">>, [ - {<<"xmlns">>, <<"vcard-temp">>}], [ - {xmlel, <<"NICKNAME">>, [], [{xmlcdata, Nickname}]} - ] - }}), - case R of - {iq, <<>>, result, <<>>, _L, []} -> - ok; - _ -> - error + VCard = xmpp:encode(#vcard_temp{nickname = Nickname}), + case mod_vcard:set_vcard(User, jid:nameprep(Host), VCard) of + {error, badarg} -> + error; + ok -> + ok end. get_vcard(User, Host, Name) -> @@ -971,26 +1101,17 @@ set_vcard(User, Host, Name, Subname, SomeContent) -> %% %% Internal vcard -get_module_resource(Server) -> - case gen_mod:get_module_opt(Server, ?MODULE, module_resource, fun(A) -> A end, none) of - none -> list_to_binary(atom_to_list(?MODULE)); - R when is_binary(R) -> R - end. - get_vcard_content(User, Server, Data) -> - [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}), - JID = jid:make(User, Server, get_module_resource(Server)), - IQ = #iq{type = get, xmlns = ?NS_VCARD}, - IQr = Module:Function(JID, JID, IQ), - [A1] = IQr#iq.sub_el, - case A1#xmlel.children of - [_|_] -> - case get_vcard(Data, A1) of + case mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server)) of + [_|_] = Els -> + case get_vcard(Data, Els) of [false] -> throw(error_no_value_found_in_vcard); ElemList -> ?DEBUG("ELS ~p", [ElemList]), [fxml:get_tag_cdata(Elem) || Elem <- ElemList] end; [] -> - throw(error_no_vcard_found) + throw(error_no_vcard_found); + error -> + throw(database_failure) end. get_vcard([<<"TEL">>, TelType], {_, _, _, OldEls}) -> @@ -1015,25 +1136,19 @@ set_vcard_content(User, Server, Data, SomeContent) -> [Bin | _] when is_binary(Bin) -> SomeContent; Bin when is_binary(Bin) -> [SomeContent] end, - [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}), - JID = jid:make(User, Server, get_module_resource(Server)), - IQ = #iq{type = get, xmlns = ?NS_VCARD}, - IQr = Module:Function(JID, JID, IQ), - %% Get old vcard - A4 = case IQr#iq.sub_el of + A4 = case mod_vcard:get_vcard(jid:nodeprep(User), jid:nameprep(Server)) of [A1] -> {_, _, _, A2} = A1, update_vcard_els(Data, ContentList, A2); [] -> - update_vcard_els(Data, ContentList, []) + update_vcard_els(Data, ContentList, []); + error -> + throw(database_failure) end, - %% Build new vcard SubEl = {xmlel, <<"vCard">>, [{<<"xmlns">>,<<"vcard-temp">>}], A4}, - IQ2 = #iq{type=set, sub_el = SubEl}, - - Module:Function(JID, JID, IQ2), + mod_vcard:set_vcard(User, jid:nameprep(Server), SubEl), ok. take_vcard_tel(TelType, [{xmlel, <<"TEL">>, _, SubEls}=OldEl | OldEls], NewEls, Taken) -> @@ -1094,11 +1209,7 @@ add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) -> subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) -> ItemEl = build_roster_item(User, Server, {add, Nick, Subscription, Group}), - mod_roster:set_items( - LU, LS, - {xmlel, <<"query">>, - [{<<"xmlns">>, ?NS_ROSTER}], - [ItemEl]}). + mod_roster:set_items(LU, LS, #roster_query{items = [ItemEl]}). delete_rosteritem(LocalUser, LocalServer, User, Server) -> case unsubscribe(LocalUser, LocalServer, User, Server) of @@ -1111,11 +1222,7 @@ delete_rosteritem(LocalUser, LocalServer, User, Server) -> unsubscribe(LU, LS, User, Server) -> ItemEl = build_roster_item(User, Server, remove), - mod_roster:set_items( - LU, LS, - {xmlel, <<"query">>, - [{<<"xmlns">>, ?NS_ROSTER}], - [ItemEl]}). + mod_roster:set_items(LU, LS, #roster_query{items = [ItemEl]}). %% ----------------------------- %% Get Roster @@ -1205,30 +1312,17 @@ push_roster_item(LU, LS, R, U, S, Action) -> ejabberd_router:route(jid:remove_resource(LJID), LJID, ResIQ). build_roster_item(U, S, {add, Nick, Subs, Group}) -> - GNames = binary:split(Group,<<";">>, [global]), - GroupEls = [{xmlel, <<"group">>, [], [{xmlcdata, GName}]} || GName <- GNames], - {xmlel, <<"item">>, - [{<<"jid">>, jid:to_string(jid:make(U, S, <<>>))}, - {<<"name">>, Nick}, - {<<"subscription">>, Subs}], - GroupEls - }; + Groups = binary:split(Group,<<";">>, [global]), + #roster_item{jid = jid:make(U, S), + name = Nick, + subscription = jlib:binary_to_atom(Subs), + groups = Groups}; build_roster_item(U, S, remove) -> - {xmlel, <<"item">>, - [{<<"jid">>, jid:to_string(jid:make(U, S, <<>>))}, - {<<"subscription">>, <<"remove">>}], - [] - }. + #roster_item{jid = jid:make(U, S), subscription = remove}. build_iq_roster_push(Item) -> - {xmlel, <<"iq">>, - [{<<"type">>, <<"set">>}, {<<"id">>, <<"push">>}], - [{xmlel, <<"query">>, - [{<<"xmlns">>, ?NS_ROSTER}], - [Item] - } - ] - }. + #iq{type = set, id = <<"push">>, + sub_els = [#roster_query{items = [Item]}]}. build_broadcast(U, S, {add, _Nick, Subs, _Group}) -> build_broadcast(U, S, list_to_atom(binary_to_list(Subs))); @@ -1274,17 +1368,9 @@ get_last(User, Server) -> %% <aa xmlns='bb'>Cluth</aa> private_get(Username, Host, Element, Ns) -> - From = jid:make(Username, Host, <<>>), - To = jid:make(Username, Host, <<>>), - IQ = {iq, <<>>, get, ?NS_PRIVATE, <<>>, - {xmlel, <<"query">>, - [{<<"xmlns">>,?NS_PRIVATE}], - [{xmlel, Element, [{<<"xmlns">>, Ns}], []}]}}, - ResIq = mod_private:process_sm_iq(From, To, IQ), - [{xmlel, <<"query">>, - [{<<"xmlns">>, ?NS_PRIVATE}], - [SubEl]}] = ResIq#iq.sub_el, - binary_to_list(fxml:element_to_binary(SubEl)). + Els = mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host), + [Ns, Element]), + binary_to_list(fxml:element_to_binary(xmpp:encode(#private{xml_els = Els}))). private_set(Username, Host, ElementString) -> case fxml_stream:parse_element(ElementString) of @@ -1297,13 +1383,9 @@ private_set(Username, Host, ElementString) -> end. private_set2(Username, Host, Xml) -> - From = jid:make(Username, Host, <<>>), - To = jid:make(Username, Host, <<>>), - IQ = {iq, <<>>, set, ?NS_PRIVATE, <<>>, - {xmlel, <<"query">>, - [{<<"xmlns">>, ?NS_PRIVATE}], - [Xml]}}, - mod_private:process_sm_iq(From, To, IQ), + NS = fxml:get_tag_attr_s(<<"xmlns">>, Xml), + mod_private:set_data(jid:nodeprep(Username), jid:nameprep(Host), + [{NS, Xml}]), ok. %%% @@ -1366,23 +1448,25 @@ send_message(Type, From, To, Subject, Body) -> ejabberd_router:route(FromJID, ToJID, Packet). build_packet(Type, Subject, Body) -> - Tail = if Subject == <<"">>; Type == <<"chat">> -> []; - true -> [{xmlel, <<"subject">>, [], [{xmlcdata, Subject}]}] - end, - {xmlel, <<"message">>, - [{<<"type">>, Type}, {<<"id">>, randoms:get_string()}], - [{xmlel, <<"body">>, [], [{xmlcdata, Body}]} | Tail] - }. + #message{type = jlib:binary_to_atom(Type), + body = xmpp:mk_text(Body), + subject = xmpp:mk_text(Subject)}. send_stanza(FromString, ToString, Stanza) -> - case fxml_stream:parse_element(Stanza) of - {error, Error} -> - {error, Error}; - XmlEl -> - #xmlel{attrs = Attrs} = XmlEl, - From = jid:from_string(proplists:get_value(<<"from">>, Attrs, FromString)), - To = jid:from_string(proplists:get_value(<<"to">>, Attrs, ToString)), - ejabberd_router:route(From, To, XmlEl) + try + #xmlel{} = El = fxml_stream:parse_element(Stanza), + #jid{} = From = jid:from_string(FromString), + #jid{} = To = jid:to_string(ToString), + Pkt = xmpp:decode(El, ?NS_CLIENT, [ignore_els]), + ejabberd_router:route(From, To, Pkt) + catch _:{xmpp_codec, Why} -> + io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]), + {error, Why}; + _:{badmatch, {error, Why}} -> + io:format("invalid xml: ~p~n", [Why]), + {error, Why}; + _:{badmatch, error} -> + {error, "JID malformed"} end. send_stanza_c2s(Username, Host, Resource, Stanza) -> @@ -1398,17 +1482,16 @@ send_stanza_c2s(Username, Host, Resource, Stanza) -> end. privacy_set(Username, Host, QueryS) -> - From = jid:make(Username, Host, <<"">>), - To = jid:make(<<"">>, Host, <<"">>), + From = jid:make(Username, Host), + To = jid:make(Host), QueryEl = fxml_stream:parse_element(QueryS), - StanzaEl = {xmlel, <<"iq">>, [{<<"type">>, <<"set">>}], [QueryEl]}, - IQ = jlib:iq_query_info(StanzaEl), - ejabberd_hooks:run_fold( - privacy_iq_set, - Host, - {error, ?ERR_FEATURE_NOT_IMPLEMENTED}, - [From, To, IQ] - ), + SubEl = xmpp:decode(QueryEl), + IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl], + from = From, to = To}, + ejabberd_hooks:run_fold(privacy_iq_set, + Host, + {error, xmpp:err_feature_not_implemented()}, + [IQ, #userlist{}]), ok. %%% @@ -1589,5 +1672,4 @@ is_glob_match(String, <<"!", Glob/binary>>) -> is_glob_match(String, Glob) -> is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). -mod_opt_type(module_resource) -> fun (A) -> A end; -mod_opt_type(_) -> [module_resource]. +mod_opt_type(_) -> []. |