25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-26 17:38:45 +01:00

Convert to exmpp.

SVN Revision: 1513
This commit is contained in:
Jean-Sébastien Pédron 2008-08-06 13:44:58 +00:00
parent 70956ece12
commit d9a493561b
8 changed files with 330 additions and 314 deletions

View File

@ -1,3 +1,9 @@
2008-08-06 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
* src/mod_offline.erl, src/mod_offline_odbc.erl, src/mod_echo.erl,
src/mod_last.erl, src/mod_configure2.erl, src/mod_last_odbc.erl,
src/gen_iq_handler.erl: Convert to exmpp.
2008-07-25 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
* src/adhoc.erl, src/mod_configure.erl, src/mod_announce.erl,

View File

@ -63,7 +63,11 @@
mod_annouce,
mod_caps,
mod_configure,
mod_configure2,
mod_disco,
mod_echo,
mod_offline,
mod_offline_odbc,
mod_roster,
mod_vcard
]).

View File

@ -33,30 +33,36 @@
stop/1,
process_local_iq/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include_lib("exmpp/include/exmpp.hrl").
-define(NS_ECONFIGURE, "http://ejabberd.jabberstudio.org/protocol/configure").
-include("ejabberd.hrl").
% XXX The namespace used in this module isn't known by Exmpp: if the
% known list isn't updated by Ejabberd, some element names will be
% represented with strings.
% XXX This module currently supposed that they'll be atoms.
-define(NS_ECONFIGURE, 'http://ejabberd.jabberstudio.org/protocol/configure').
-define(NS_ECONFIGURE_s, "http://ejabberd.jabberstudio.org/protocol/configure").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_ECONFIGURE,
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_ECONFIGURE_s,
?MODULE, process_local_iq, IQDisc),
ok.
stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_ECONFIGURE).
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_ECONFIGURE_s).
process_local_iq(From, To, #iq{type = Type, lang = _Lang, sub_el = SubEl} = IQ) ->
case acl:match_rule(To#jid.lserver, configure, From) of
process_local_iq(From, To, IQ) ->
case acl:match_rule(To#jid.ldomain, configure, From) of
deny ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
exmpp_iq:error(IQ, 'not-allowed');
allow ->
case Type of
case exmpp_iq:get_type(IQ) of
set ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
exmpp_iq:error(IQ, 'feature-not-implemented');
%%case xml:get_tag_attr_s("type", SubEl) of
%% "cancel" ->
%% IQ#iq{type = result,
@ -90,56 +96,57 @@ process_local_iq(From, To, #iq{type = Type, lang = _Lang, sub_el = SubEl} = IQ)
%% sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
%%end;
get ->
case process_get(SubEl) of
case process_get(IQ#xmlel.children) of
{result, Res} ->
IQ#iq{type = result, sub_el = [Res]};
exmpp_iq:result(IQ, Res);
{error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]}
exmpp_iq:error(IQ, Error)
end
end
end.
process_get({xmlelement, "info", _Attrs, _SubEls}) ->
process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'info'}) ->
S2SConns = ejabberd_s2s:dirty_get_connections(),
TConns = lists:usort([element(2, C) || C <- S2SConns]),
Attrs = [{"registered-users",
Attrs = [#xmlattr{name = 'registered-users', value =
integer_to_list(mnesia:table_info(passwd, size))},
{"online-users",
#xmlattr{name = 'online-users', value =
integer_to_list(mnesia:table_info(presence, size))},
{"running-nodes",
#xmlattr{name = 'running-nodes', value =
integer_to_list(length(mnesia:system_info(running_db_nodes)))},
{"stopped-nodes",
#xmlattr{name = 'stopped-nodes', value =
integer_to_list(
length(lists:usort(mnesia:system_info(db_nodes) ++
mnesia:system_info(extra_db_nodes)) --
mnesia:system_info(running_db_nodes)))},
{"outgoing-s2s-servers", integer_to_list(length(TConns))}],
{result, {xmlelement, "info",
[{"xmlns", ?NS_ECONFIGURE} | Attrs], []}};
process_get({xmlelement, "welcome-message", Attrs, _SubEls}) ->
#xmlattr{name = 'outgoing-s2s-servers', value =
integer_to_list(length(TConns))}],
{result, #xmlel{ns = ?NS_ECONFIGURE, name = 'info', attrs = Attrs}};
process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'welcome-message', attrs = Attrs}) ->
{Subj, Body} = case ejabberd_config:get_local_option(welcome_message) of
{_Subj, _Body} = SB -> SB;
_ -> {"", ""}
end,
{result, {xmlelement, "welcome-message", Attrs,
[{xmlelement, "subject", [], [{xmlcdata, Subj}]},
{xmlelement, "body", [], [{xmlcdata, Body}]}]}};
process_get({xmlelement, "registration-watchers", Attrs, _SubEls}) ->
{result, #xmlel{ns = ?NS_ECONFIGURE, name = 'welcome-message',
attrs = Attrs, children =
[#xmlel{ns = ?NS_ECONFIGURE, name = 'subject', children = [#xmlcdata{cdata = list_to_binary(Subj)}]},
#xmlel{ns = ?NS_ECONFIGURE, name = 'body', children = [#xmlcdata{cdata = list_to_binary(Body)}]}]}};
process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'registration-watchers', attrs = Attrs}) ->
SubEls =
case ejabberd_config:get_local_option(registration_watchers) of
JIDs when is_list(JIDs) ->
lists:map(fun(JID) ->
{xmlelement, "jid", [], [{xmlcdata, JID}]}
#xmlel{ns = ?NS_ECONFIGURE, name = 'jid', children = [#xmlcdata{cdata = list_to_binary(JID)}]}
end, JIDs);
_ ->
[]
end,
{result, {xmlelement, "registration_watchers", Attrs, SubEls}};
process_get({xmlelement, "acls", Attrs, _SubEls}) ->
{result, #xmlel{ns = ?NS_ECONFIGURE, name = 'registration_watchers', attrs = Attrs, children = SubEls}};
process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'acls', attrs = Attrs}) ->
Str = lists:flatten(io_lib:format("~p.", [ets:tab2list(acl)])),
{result, {xmlelement, "acls", Attrs, [{xmlcdata, Str}]}};
process_get({xmlelement, "access", Attrs, _SubEls}) ->
{result, #xmlel{ns = ?NS_ECONFIGURE, name = 'acls', attrs = Attrs, children = [#xmlcdata{cdata = list_to_binary(Str)}]}};
process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'access', attrs = Attrs}) ->
Str =
lists:flatten(
io_lib:format(
@ -149,22 +156,22 @@ process_get({xmlelement, "access", Attrs, _SubEls}) ->
[],
[{{access, '$1', '$2'}}]}])
])),
{result, {xmlelement, "access", Attrs, [{xmlcdata, Str}]}};
process_get({xmlelement, "last", Attrs, _SubEls}) ->
{result, #xmlel{ns = ?NS_ECONFIGURE, name = 'access', attrs = Attrs, children = [#xmlcdata{cdata = list_to_binary(Str)}]}};
process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'last', attrs = Attrs}) ->
case catch mnesia:dirty_select(
last_activity, [{{last_activity, '_', '$1', '_'}, [], ['$1']}]) of
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
{error, 'internal-server-error'};
Vals ->
{MegaSecs, Secs, _MicroSecs} = now(),
TimeStamp = MegaSecs * 1000000 + Secs,
Str = lists:flatten(
lists:append(
[[integer_to_list(TimeStamp - V), " "] || V <- Vals])),
{result, {xmlelement, "last", Attrs, [{xmlcdata, Str}]}}
{result, #xmlel{ns = ?NS_ECONFIGURE, name = 'last', attrs = Attrs, children = [#xmlcdata{cdata = list_to_binary(Str)}]}}
end;
%%process_get({xmlelement, Name, Attrs, SubEls}) ->
%% {result, };
process_get(_) ->
{error, ?ERR_BAD_REQUEST}.
{error, 'bad-request'}.

View File

@ -37,8 +37,9 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {host}).
@ -117,8 +118,8 @@ handle_cast(_Msg, State) ->
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
Packet2 = case From#jid.user of
"" -> jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST);
Packet2 = case From#jid.node of
<<>> -> exmpp_stanza:reply_with_error(Packet, 'bad-request');
_ -> Packet
end,
do_client_version(disabled, To, From), % Put 'enabled' to enable it
@ -171,16 +172,16 @@ code_change(_OldVsn, State, _Extra) ->
do_client_version(disabled, _From, _To) ->
ok;
do_client_version(enabled, From, To) ->
ToS = jlib:jid_to_string(To),
%% It is important to identify this process and packet
Random_resource = integer_to_list(random:uniform(100000)),
From2 = From#jid{resource = Random_resource,
lresource = Random_resource},
%% Build an iq:query request
Packet = {xmlelement, "iq",
[{"to", ToS}, {"type", "get"}],
[{xmlelement, "query", [{"xmlns", ?NS_VERSION}], []}]},
Request = #xmlel{ns = ?NS_SOFT_VERSION, name = 'query'},
Packet = exmpp_stanza:set_recipient(
exmpp_iq:get(?NS_JABBER_CLIENT, Request),
To),
%% Send the request
ejabberd_router:route(From2, To, Packet),
@ -189,15 +190,15 @@ do_client_version(enabled, From, To) ->
%% It is very important to only accept a packet which is the
%% response to the request that he sent
Els = receive {route, To, From2, IQ} ->
{xmlelement, "query", _, List} = xml:get_subtag(IQ, "query"),
#xmlel{ns = ?NS_SOFT_VERSION, name = 'query', children = List} = exmpp_iq:get_payload(IQ),
List
after 5000 -> % Timeout in miliseconds: 5 seconds
[]
end,
Values = [{Name, Value} || {xmlelement,Name,[],[{xmlcdata,Value}]} <- Els],
Values = [{Name, binary_to_list(Value)} || #xmlel{name = Name, children = [#xmlcdata{cdata = Value}]} <- Els],
%% Print in log
Values_string1 = [io_lib:format("~n~s: ~p", [N, V]) || {N, V} <- Values],
Values_string2 = lists:concat(Values_string1),
?INFO_MSG("Information of the client: ~s~s", [ToS, Values_string2]).
?INFO_MSG("Information of the client: ~s~s", [exmpp_jid:jid_to_string(To), Values_string2]).

View File

@ -38,8 +38,9 @@
get_last_info/2,
remove_user/2]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_privacy.hrl").
-record(last_activity, {us, timestamp, status}).
@ -51,9 +52,9 @@ start(Host, Opts) ->
[{disc_copies, [node()]},
{attributes, record_info(fields, last_activity)}]),
update_table(),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST_ACTIVITY_s,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST_ACTIVITY_s,
?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50),
@ -65,30 +66,28 @@ stop(Host) ->
?MODULE, remove_user, 50),
ejabberd_hooks:delete(unset_presence_hook, Host,
?MODULE, on_presence_update, 50),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST).
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST_ACTIVITY_s),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST_ACTIVITY_s).
process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
process_local_iq(_From, _To, IQ) ->
case exmpp_iq:get_type(IQ) of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
exmpp_iq:error(IQ, 'not-allowed');
get ->
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_LAST},
{"seconds", integer_to_list(Sec)}],
[]}]}
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
[#xmlattr{name = 'seconds', value = integer_to_list(Sec)}]},
exmpp_iq:result(IQ, Response)
end.
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
process_sm_iq(From, To, IQ) ->
case exmpp_iq:get_type(IQ) of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
exmpp_iq:error(IQ, 'not-allowed');
get ->
User = To#jid.luser,
Server = To#jid.lserver,
User = To#jid.lnode,
Server = To#jid.ldomain,
{Subscription, _Groups} =
ejabberd_hooks:run_fold(
roster_get_jid_info, Server,
@ -104,36 +103,33 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
allow,
[User, Server, UserListRecord,
{From, To,
{xmlelement, "presence", [], []}},
exmpp_presence:available()},
out]) of
allow ->
get_last(IQ, SubEl, User, Server);
get_last(IQ, User, Server);
deny ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
exmpp_iq:error(IQ, 'not-allowed')
end;
true ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
exmpp_iq:error(IQ, 'not-allowed')
end
end.
%% TODO: This function could use get_last_info/2
get_last(IQ, SubEl, LUser, LServer) ->
get_last(IQ, LUser, LServer) ->
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
{'EXIT', _Reason} ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
exmpp_iq:error(IQ, 'internal-server-error');
[] ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
exmpp_iq:error(IQ, 'service-unavailable');
[#last_activity{timestamp = TimeStamp, status = Status}] ->
{MegaSecs, Secs, _MicroSecs} = now(),
TimeStamp2 = MegaSecs * 1000000 + Secs,
Sec = TimeStamp2 - TimeStamp,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_LAST},
{"seconds", integer_to_list(Sec)}],
[{xmlcdata, Status}]}]}
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
attrs = [#xmlattr{name = 'seconds', value = integer_to_list(Sec)}],
children = [#xmlcdata{cdata = list_to_binary(Status)}]},
exmpp_iq:result(IQ, Response)
end.
@ -144,8 +140,8 @@ on_presence_update(User, Server, _Resource, Status) ->
store_last_info(User, Server, TimeStamp, Status).
store_last_info(User, Server, TimeStamp, Status) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
mnesia:write(#last_activity{us = US,
@ -166,8 +162,8 @@ get_last_info(LUser, LServer) ->
end.
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
mnesia:delete({last_activity, US})

View File

@ -38,15 +38,16 @@
get_last_info/2,
remove_user/2]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_privacy.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST_ACTIVITY_s,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST_ACTIVITY_s,
?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50),
@ -58,29 +59,27 @@ stop(Host) ->
?MODULE, remove_user, 50),
ejabberd_hooks:delete(unset_presence_hook, Host,
?MODULE, on_presence_update, 50),
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST).
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST_ACTIVITY_s),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST_ACTIVITY_s).
process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
process_local_iq(_From, _To, IQ) ->
case exmpp_iq:get_type(IQ) of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
exmpp_iq:error(IQ, 'not-allowed');
get ->
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_LAST},
{"seconds", integer_to_list(Sec)}],
[]}]}
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
[#xmlattr{name = 'seconds', value = integer_to_list(Sec)}]},
exmpp_iq:result(IQ, Response)
end.
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
process_sm_iq(From, To, IQ) ->
case exmpp_iq:get_type(IQ) of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
exmpp_iq:error(IQ, 'not-allowed');
get ->
User = To#jid.luser,
Server = To#jid.lserver,
User = To#jid.lnode,
Server = To#jid.ldomain,
{Subscription, _Groups} =
ejabberd_hooks:run_fold(
roster_get_jid_info, Server,
@ -96,42 +95,38 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
allow,
[User, Server, UserListRecord,
{From, To,
{xmlelement, "presence", [], []}},
exmpp_presence:available()},
out]) of
allow ->
get_last(IQ, SubEl, User, Server);
get_last(IQ, User, Server);
deny ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
exmpp_iq:error(IQ, 'not-allowed')
end;
true ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
exmpp_iq:error(IQ, 'not-allowed')
end
end.
%% TODO: This function could use get_last_info/2
get_last(IQ, SubEl, LUser, LServer) ->
get_last(IQ, LUser, LServer) ->
Username = ejabberd_odbc:escape(LUser),
case catch odbc_queries:get_last(LServer, Username) of
{'EXIT', _Reason} ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
exmpp_iq:error(IQ, 'internal-server-error');
{selected, ["seconds","state"], []} ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
exmpp_iq:error(IQ, 'service-unavailable');
{selected, ["seconds","state"], [{STimeStamp, Status}]} ->
case catch list_to_integer(STimeStamp) of
TimeStamp when is_integer(TimeStamp) ->
{MegaSecs, Secs, _MicroSecs} = now(),
TimeStamp2 = MegaSecs * 1000000 + Secs,
Sec = TimeStamp2 - TimeStamp,
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_LAST},
{"seconds", integer_to_list(Sec)}],
[{xmlcdata, Status}]}]};
Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
attrs = [#xmlattr{name = 'seconds', value = integer_to_list(Sec)}],
children = [#xmlcdata{cdata = list_to_binary(Status)}]},
exmpp_iq:result(IQ, Response);
_ ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
exmpp_iq:error(IQ, 'internal-server-error')
end
end.
@ -141,8 +136,8 @@ on_presence_update(User, Server, _Resource, Status) ->
store_last_info(User, Server, TimeStamp, Status).
store_last_info(User, Server, TimeStamp, Status) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
Username = ejabberd_odbc:escape(LUser),
Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
State = ejabberd_odbc:escape(Status),
@ -166,7 +161,7 @@ get_last_info(LUser, LServer) ->
end.
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
Username = ejabberd_odbc:escape(LUser),
odbc_queries:del_last(LServer, Username).

View File

@ -41,8 +41,9 @@
webadmin_page/3,
webadmin_user/4]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("web/ejabberd_http.hrl").
-include("web/ejabberd_web_admin.hrl").
@ -51,6 +52,11 @@
-define(PROCNAME, ejabberd_offline).
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
% These are the namespace already declared by the stream opening. This is
% used at serialization time.
-define(DEFAULT_NS, ?NS_JABBER_CLIENT).
-define(PREFIXED_NS, [{?NS_XMPP, ?NS_XMPP_pfx}]).
start(Host, Opts) ->
mnesia:create_table(offline_msg,
[{disc_only_copies, [node()]},
@ -142,23 +148,25 @@ stop(Host) ->
{wait, Proc}.
store_packet(From, To, Packet) ->
Type = xml:get_tag_attr_s("type", Packet),
Type = exmpp_stanza:get_type(Packet),
if
(Type /= "error") and (Type /= "groupchat") and
(Type /= "headline") ->
case check_event(From, To, Packet) of
true ->
#jid{luser = LUser, lserver = LServer} = To,
#jid{lnode = LUser, ldomain = LServer} = To,
TimeStamp = now(),
{xmlelement, _Name, _Attrs, Els} = Packet,
Expire = find_x_expire(TimeStamp, Els),
gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
Expire = find_x_expire(TimeStamp, Packet#xmlel.children),
% XXX OLD FORMAT: Packet is stored in the old format.
PacketOld = exmpp_xml:xmlel_to_xmlelement(Packet,
[?DEFAULT_NS], ?PREFIXED_NS),
gen_mod:get_module_proc(To#jid.ldomain, ?PROCNAME) !
#offline_msg{us = {LUser, LServer},
timestamp = TimeStamp,
expire = Expire,
from = From,
to = To,
packet = Packet},
packet = PacketOld},
stop;
_ ->
ok
@ -168,31 +176,28 @@ store_packet(From, To, Packet) ->
end.
check_event(From, To, Packet) ->
{xmlelement, Name, Attrs, Els} = Packet,
case find_x_event(Els) of
case find_x_event(Packet#xmlel.children) of
false ->
true;
El ->
case xml:get_subtag(El, "id") of
false ->
case xml:get_subtag(El, "offline") of
false ->
case exmpp_xml:get_element(El, 'id') of
undefined ->
case exmpp_xml:get_element(El, 'offline') of
undefined ->
true;
_ ->
ID = case xml:get_tag_attr_s("id", Packet) of
"" ->
{xmlelement, "id", [], []};
ID = case exmpp_stanza:get_id(Packet) of
undefined ->
#xmlel{ns = ?NS_MESSAGE_EVENT, name = 'id'};
S ->
{xmlelement, "id", [],
[{xmlcdata, S}]}
#xmlel{ns = ?NS_MESSAGE_EVENT, name = 'id',
children = [#xmlcdata{cdata =
list_to_binary(S)}]}
end,
X = #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'x', children =
[ID, #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'offline'}]},
ejabberd_router:route(
To, From, {xmlelement, Name, Attrs,
[{xmlelement, "x",
[{"xmlns", ?NS_EVENT}],
[ID,
{xmlelement, "offline", [], []}]}]
}),
To, From, exmpp_xml:set_children(Packet, [X])),
true
end;
_ ->
@ -202,44 +207,34 @@ check_event(From, To, Packet) ->
find_x_event([]) ->
false;
find_x_event([{xmlcdata, _} | Els]) ->
find_x_event(Els);
find_x_event([El | Els]) ->
case xml:get_tag_attr_s("xmlns", El) of
?NS_EVENT ->
El;
_ ->
find_x_event(Els)
end.
find_x_event([#xmlel{ns = ?NS_MESSAGE_EVENT} = El | _Els]) ->
El;
find_x_event([_ | Els]) ->
find_x_event(Els).
find_x_expire(_, []) ->
never;
find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
find_x_expire(TimeStamp, Els);
find_x_expire(TimeStamp, [El | Els]) ->
case xml:get_tag_attr_s("xmlns", El) of
?NS_EXPIRE ->
Val = xml:get_tag_attr_s("seconds", El),
case catch list_to_integer(Val) of
{'EXIT', _} ->
never;
Int when Int > 0 ->
{MegaSecs, Secs, MicroSecs} = TimeStamp,
S = MegaSecs * 1000000 + Secs + Int,
MegaSecs1 = S div 1000000,
Secs1 = S rem 1000000,
{MegaSecs1, Secs1, MicroSecs};
_ ->
never
end;
find_x_expire(TimeStamp, [#xmlel{ns = ?NS_MESSAGE_EXPIRE} = El | _Els]) ->
Val = exmpp_xml:get_attribute(El, 'seconds', ""),
case catch list_to_integer(Val) of
{'EXIT', _} ->
never;
Int when Int > 0 ->
{MegaSecs, Secs, MicroSecs} = TimeStamp,
S = MegaSecs * 1000000 + Secs + Int,
MegaSecs1 = S div 1000000,
Secs1 = S rem 1000000,
{MegaSecs1, Secs1, MicroSecs};
_ ->
find_x_expire(TimeStamp, Els)
end.
never
end;
find_x_expire(TimeStamp, [_ | Els]) ->
find_x_expire(TimeStamp, Els).
resend_offline_messages(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
Rs = mnesia:wread({offline_msg, US}),
@ -250,16 +245,22 @@ resend_offline_messages(User, Server) ->
{atomic, Rs} ->
lists:foreach(
fun(R) ->
{xmlelement, Name, Attrs, Els} = R#offline_msg.packet,
Packet = case R#offline_msg.packet of
#xmlelement{} = P ->
exmpp_xml:xmlelement_to_xmlel(P,
[?DEFAULT_NS], ?PREFIXED_NS);
#xmlel{} = P ->
P
end,
% XXX OLD FORMAT: Convert From & To.
ejabberd_sm !
{route,
R#offline_msg.from,
R#offline_msg.to,
{xmlelement, Name, Attrs,
Els ++
[jlib:timestamp_to_xml(
jlib:from_old_jid(R#offline_msg.from),
jlib:from_old_jid(R#offline_msg.to),
exmpp_xml:append_child(Packet,
jlib:timestamp_to_xml(
calendar:now_to_universal_time(
R#offline_msg.timestamp))]}}
R#offline_msg.timestamp)))}
end,
lists:keysort(#offline_msg.timestamp, Rs));
_ ->
@ -267,8 +268,8 @@ resend_offline_messages(User, Server) ->
end.
pop_offline_messages(Ls, User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
Rs = mnesia:wread({offline_msg, US}),
@ -280,15 +281,21 @@ pop_offline_messages(Ls, User, Server) ->
TS = now(),
Ls ++ lists:map(
fun(R) ->
{xmlelement, Name, Attrs, Els} = R#offline_msg.packet,
Packet = case R#offline_msg.packet of
#xmlelement{} = P ->
exmpp_xml:xmlelement_to_xmlel(P,
[?DEFAULT_NS], ?PREFIXED_NS);
#xmlel{} = P ->
P
end,
% XXX OLD FORMAT: Convert From & To.
{route,
R#offline_msg.from,
R#offline_msg.to,
{xmlelement, Name, Attrs,
Els ++
[jlib:timestamp_to_xml(
jlib:from_old_jid(R#offline_msg.from),
jlib:from_old_jid(R#offline_msg.to),
exmpp_xml:append_child(Packet,
jlib:timestamp_to_xml(
calendar:now_to_universal_time(
R#offline_msg.timestamp))]}}
R#offline_msg.timestamp)))}
end,
lists:filter(
fun(R) ->
@ -343,8 +350,8 @@ remove_old_messages(Days) ->
mnesia:transaction(F).
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
F = fun() ->
mnesia:delete({offline_msg, US})
@ -371,10 +378,15 @@ update_table() ->
F1 = fun() ->
mnesia:write_lock_table(mod_offline_tmp_table),
mnesia:foldl(
fun(#offline_msg{us = U} = R, _) ->
fun(#offline_msg{us = U, packet = P} = R, _) ->
New_R = R#offline_msg{
us = {U, Host},
packet = exmpp_xml:xmlelement_to_xmlel(P,
[?DEFAULT_NS], ?PREFIXED_NS)
},
mnesia:dirty_write(
mod_offline_tmp_table,
R#offline_msg{us = {U, Host}})
New_R)
end, ok, offline_msg)
end,
mnesia:transaction(F1),
@ -402,8 +414,7 @@ update_table() ->
mnesia:transform_table(
offline_msg,
fun({_, U, TS, F, T, P}) ->
{xmlelement, _Name, _Attrs, Els} = P,
Expire = find_x_expire(TS, Els),
Expire = find_x_expire(TS, P#xmlelement.children),
#offline_msg{us = U,
timestamp = TS,
expire = Expire,
@ -442,14 +453,21 @@ update_table() ->
%% Warn senders that their messages have been discarded:
discard_warn_sender(Msgs) ->
lists:foreach(
fun(#offline_msg{from=From, to=To, packet=Packet}) ->
fun(#offline_msg{from=From, to=To, packet=Packet0}) ->
Packet = case Packet0 of
#xmlelement{} = P ->
exmpp_xml:xmlelement_to_xmlel(P,
[?DEFAULT_NS], ?PREFIXED_NS);
#xmlel{} = P ->
P
end,
ErrText = "Your contact offline message queue is full. The message has been discarded.",
Lang = xml:get_tag_attr_s("xml:lang", Packet),
Err = jlib:make_error_reply(
Packet, ?ERRT_RESOURCE_CONSTRAINT(Lang, ErrText)),
Error = exmpp_stanza:error('resource-constraint',
{"en", ErrText}),
Err = exmpp_stanza:reply_with_error(Packet, Error),
ejabberd_router:route(
To,
From, Err)
jlib:from_old_jid(To),
jlib:from_old_jid(From), Err)
end, Msgs).
@ -464,14 +482,14 @@ webadmin_page(_, Host,
webadmin_page(Acc, _, _) -> Acc.
user_queue(User, Server, Query, Lang) ->
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
US = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
Res = user_queue_parse_query(US, Query),
Msgs = lists:keysort(#offline_msg.timestamp,
mnesia:dirty_read({offline_msg, US})),
FMsgs =
lists:map(
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
packet = Packet} = Msg) ->
ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
{{Year, Month, Day}, {Hour, Minute, Second}} =
calendar:now_to_local_time(TimeStamp),
@ -479,11 +497,14 @@ user_queue(User, Server, Query, Lang) ->
io_lib:format(
"~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
[Year, Month, Day, Hour, Minute, Second])),
SFrom = jlib:jid_to_string(From),
STo = jlib:jid_to_string(To),
Attrs2 = jlib:replace_from_to_attrs(SFrom, STo, Attrs),
Packet = {xmlelement, Name, Attrs2, Els},
FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
SFrom = exmpp_jid:jid_to_string(jlib:from_old_jid(From)),
STo = exmpp_jid:jid_to_string(jlib:from_old_jid(To)),
Packet0 = exmpp_xml:xmlelement_to_xmlel(Packet,
[?DEFAULT_NS], ?PREFIXED_NS),
Packet1 = exmpp_stanza:set_jids(Packet0, SFrom, STo),
FPacket = exmpp_xml:node_to_list(
exmpp_xml:indent_document(Packet1, <<" ">>),
[?DEFAULT_NS], ?PREFIXED_NS),
?XE("tr",
[?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
?XAC("td", [{"class", "valign"}], Time),
@ -547,10 +568,10 @@ user_queue_parse_query(US, Query) ->
end.
us_to_list({User, Server}) ->
jlib:jid_to_string({User, Server, ""}).
exmpp_jid:jid_to_string(User, Server).
webadmin_user(Acc, User, Server, Lang) ->
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
US = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
QueueLen = length(mnesia:dirty_read({offline_msg, US})),
FQueueLen = [?AC("queue/",
integer_to_list(QueueLen))],

View File

@ -40,8 +40,9 @@
webadmin_page/3,
webadmin_user/4]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("web/ejabberd_http.hrl").
-include("web/ejabberd_web_admin.hrl").
@ -50,6 +51,11 @@
-define(PROCNAME, ejabberd_offline).
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
% These are the namespace already declared by the stream opening. This is
% used at serialization time.
-define(DEFAULT_NS, ?NS_JABBER_CLIENT).
-define(PREFIXED_NS, [{?NS_XMPP, ?NS_XMPP_pfx}]).
start(Host, Opts) ->
ejabberd_hooks:add(offline_message_hook, Host,
?MODULE, store_packet, 50),
@ -93,24 +99,20 @@ loop(Host, MaxOfflineMsgs) ->
fun(M) ->
Username =
ejabberd_odbc:escape(
(M#offline_msg.to)#jid.luser),
(M#offline_msg.to)#jid.lnode),
From = M#offline_msg.from,
To = M#offline_msg.to,
{xmlelement, Name, Attrs, Els} =
M#offline_msg.packet,
Attrs2 = jlib:replace_from_to_attrs(
jlib:jid_to_string(From),
jlib:jid_to_string(To),
Attrs),
Packet = {xmlelement, Name, Attrs2,
Els ++
[jlib:timestamp_to_xml(
Packet0 = exmpp_stanza:set_jids(
From,
To,
M#offline_msg.packet),
Packet1 = exmpp_xml:append_child(Packet0,
jlib:timestamp_to_xml(
calendar:now_to_universal_time(
M#offline_msg.timestamp))]},
M#offline_msg.timestamp))),
XML =
ejabberd_odbc:escape(
lists:flatten(
xml:element_to_string(Packet))),
exmpp_xml:document_to_string(Packet1)),
odbc_queries:add_spool_sql(Username, XML)
end, Msgs),
case catch odbc_queries:add_spool(Host, Query) of
@ -152,17 +154,16 @@ stop(Host) ->
ok.
store_packet(From, To, Packet) ->
Type = xml:get_tag_attr_s("type", Packet),
Type = exmpp_stanza_:get_type(Packet, 'type'),
if
(Type /= "error") and (Type /= "groupchat") and
(Type /= "headline") ->
case check_event(From, To, Packet) of
true ->
#jid{luser = LUser} = To,
#jid{lnode = LUser} = To,
TimeStamp = now(),
{xmlelement, _Name, _Attrs, Els} = Packet,
Expire = find_x_expire(TimeStamp, Els),
gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
Expire = find_x_expire(TimeStamp, Packet#xmlel.children),
gen_mod:get_module_proc(To#jid.ldomain, ?PROCNAME) !
#offline_msg{user = LUser,
timestamp = TimeStamp,
expire = Expire,
@ -178,31 +179,27 @@ store_packet(From, To, Packet) ->
end.
check_event(From, To, Packet) ->
{xmlelement, Name, Attrs, Els} = Packet,
case find_x_event(Els) of
case find_x_event(Packet#xmlel.children) of
false ->
true;
El ->
case xml:get_subtag(El, "id") of
false ->
case xml:get_subtag(El, "offline") of
false ->
case exmpp_xml:get_element(El, 'id') of
undefined ->
case exmpp_xml:get_element(El, 'offline') of
undefined ->
true;
_ ->
ID = case xml:get_tag_attr_s("id", Packet) of
ID = case exmpp_xml:get_attribute(Packet, 'id', "") of
"" ->
{xmlelement, "id", [], []};
#xmlel{ns = ?NS_MESSAGE_EVENT, name = 'id'};
S ->
{xmlelement, "id", [],
[{xmlcdata, S}]}
#xmlel{ns = ?NS_MESSAGE_EVENT, name = 'id',
children = [#xmlcdata{cdata = list_to_binary(S)}]}
end,
X = #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'x', children =
[ID, #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'offline'}]},
ejabberd_router:route(
To, From, {xmlelement, Name, Attrs,
[{xmlelement, "x",
[{"xmlns", ?NS_EVENT}],
[ID,
{xmlelement, "offline", [], []}]}]
}),
To, From, exmpp_xml:set_children(Packet, [X])),
true
end;
_ ->
@ -212,64 +209,49 @@ check_event(From, To, Packet) ->
find_x_event([]) ->
false;
find_x_event([{xmlcdata, _} | Els]) ->
find_x_event(Els);
find_x_event([El | Els]) ->
case xml:get_tag_attr_s("xmlns", El) of
?NS_EVENT ->
El;
_ ->
find_x_event(Els)
end.
find_x_event([#xmlel{ns = ?NS_MESSAGE_EVENT} = El | _Els]) ->
El;
find_x_event([_ | Els]) ->
find_x_event(Els).
find_x_expire(_, []) ->
never;
find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
find_x_expire(TimeStamp, Els);
find_x_expire(TimeStamp, [El | Els]) ->
case xml:get_tag_attr_s("xmlns", El) of
?NS_EXPIRE ->
Val = xml:get_tag_attr_s("seconds", El),
case catch list_to_integer(Val) of
{'EXIT', _} ->
never;
Int when Int > 0 ->
{MegaSecs, Secs, MicroSecs} = TimeStamp,
S = MegaSecs * 1000000 + Secs + Int,
MegaSecs1 = S div 1000000,
Secs1 = S rem 1000000,
{MegaSecs1, Secs1, MicroSecs};
_ ->
never
end;
find_x_expire(TimeStamp, [#xmlel{ns = ?NS_MESSAGE_EXPIRE} = El | _Els]) ->
Val = exmpp_xml:get_attribute(El, 'seconds', ""),
case catch list_to_integer(Val) of
{'EXIT', _} ->
never;
Int when Int > 0 ->
{MegaSecs, Secs, MicroSecs} = TimeStamp,
S = MegaSecs * 1000000 + Secs + Int,
MegaSecs1 = S div 1000000,
Secs1 = S rem 1000000,
{MegaSecs1, Secs1, MicroSecs};
_ ->
find_x_expire(TimeStamp, Els)
end.
never
end;
find_x_expire(TimeStamp, [_ | Els]) ->
find_x_expire(TimeStamp, Els).
pop_offline_messages(Ls, User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
EUser = ejabberd_odbc:escape(LUser),
case odbc_queries:get_and_del_spool_msg_t(LServer, EUser) of
{atomic, {selected, ["username","xml"], Rs}} ->
Ls ++ lists:flatmap(
fun({_, XML}) ->
case xml_stream:parse_element(XML) of
{error, _Reason} ->
[];
El ->
To = jlib:string_to_jid(
xml:get_tag_attr_s("to", El)),
From = jlib:string_to_jid(
xml:get_tag_attr_s("from", El)),
if
(To /= error) and
(From /= error) ->
[{route, From, To, El}];
true ->
[]
end
try
[El] = exmpp_xml:parse_document(XML, [namespace, name_as_atom]),
To = exmpp_jid:list_to_jid(
exmpp_stanza:get_recipient(El)),
From = exmpp_jid:list_to_jid(
exmpp_stanza:get_sender(El)),
[{route, From, To, El}]
catch
_ ->
[]
end
end, Rs);
_ ->
@ -278,8 +260,8 @@ pop_offline_messages(Ls, User, Server) ->
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
Username = ejabberd_odbc:escape(LUser),
odbc_queries:del_spool_msg(LServer, Username).
@ -294,9 +276,9 @@ discard_warn_sender(Msgs) ->
lists:foreach(
fun(#offline_msg{from=From, to=To, packet=Packet}) ->
ErrText = "Your contact offline message queue is full. The message has been discarded.",
Lang = xml:get_tag_attr_s("xml:lang", Packet),
Err = jlib:make_error_reply(
Packet, ?ERRT_RESOURCE_CONSTRAINT(Lang, ErrText)),
Error = exmpp_stanza:error('resource-constraint',
{"en", ErrText}),
Err = exmpp_stanza:reply_with_error(Packet, Error),
ejabberd_router:route(
To,
From, Err)
@ -314,8 +296,8 @@ webadmin_page(_, Host,
webadmin_page(Acc, _, _) -> Acc.
user_queue(User, Server, Query, Lang) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
Username = ejabberd_odbc:escape(LUser),
US = {LUser, LServer},
Res = user_queue_parse_query(Username, LServer, Query),
@ -327,11 +309,12 @@ user_queue(User, Server, Query, Lang) ->
{selected, ["username", "xml"], Rs} ->
lists:flatmap(
fun({_, XML}) ->
case xml_stream:parse_element(XML) of
{error, _Reason} ->
[];
El ->
try exmpp_xml:parse_document(XML, [namespace, name_as_atom]) of
[El] ->
[El]
catch
_ ->
[]
end
end, Rs);
_ ->
@ -339,10 +322,12 @@ user_queue(User, Server, Query, Lang) ->
end,
FMsgs =
lists:map(
fun({xmlelement, _Name, _Attrs, _Els} = Msg) ->
fun(#xmlel{} = Msg) ->
ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
Packet = Msg,
FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
FPacket = exmpp_xml:node_to_list(
exmpp_xml:indent_document(Packet, <<" ">>),
[?DEFAULT_NS], ?PREFIXED_NS),
?XE("tr",
[?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
@ -386,11 +371,12 @@ user_queue_parse_query(Username, LServer, Query) ->
{selected, ["xml", "seq"], Rs} ->
lists:flatmap(
fun({XML, Seq}) ->
case xml_stream:parse_element(XML) of
{error, _Reason} ->
[];
El ->
try exmpp_xml:parse_document(XML, [namespace, name_as_atom]) of
[El] ->
[{El, Seq}]
catch
_ ->
[]
end
end, Rs);
_ ->
@ -421,11 +407,11 @@ user_queue_parse_query(Username, LServer, Query) ->
end.
us_to_list({User, Server}) ->
jlib:jid_to_string({User, Server, ""}).
exmpp_jid:jid_to_list(User, Server).
webadmin_user(Acc, User, Server, Lang) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
Username = ejabberd_odbc:escape(LUser),
QueueLen = case catch ejabberd_odbc:sql_query(
LServer,