25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-22 16:20:52 +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> 2008-07-25 Jean-Sébastien Pédron <js.pedron@meetic-corp.com>
* src/adhoc.erl, src/mod_configure.erl, src/mod_announce.erl, * src/adhoc.erl, src/mod_configure.erl, src/mod_announce.erl,

View File

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

View File

@ -33,30 +33,36 @@
stop/1, stop/1,
process_local_iq/3]). process_local_iq/3]).
-include("ejabberd.hrl"). -include_lib("exmpp/include/exmpp.hrl").
-include("jlib.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) -> start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), 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), ?MODULE, process_local_iq, IQDisc),
ok. ok.
stop(Host) -> 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) -> process_local_iq(From, To, IQ) ->
case acl:match_rule(To#jid.lserver, configure, From) of case acl:match_rule(To#jid.ldomain, configure, From) of
deny -> deny ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; exmpp_iq:error(IQ, 'not-allowed');
allow -> allow ->
case Type of case exmpp_iq:get_type(IQ) of
set -> set ->
IQ#iq{type = error, exmpp_iq:error(IQ, 'feature-not-implemented');
sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]};
%%case xml:get_tag_attr_s("type", SubEl) of %%case xml:get_tag_attr_s("type", SubEl) of
%% "cancel" -> %% "cancel" ->
%% IQ#iq{type = result, %% 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]} %% sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
%%end; %%end;
get -> get ->
case process_get(SubEl) of case process_get(IQ#xmlel.children) of
{result, Res} -> {result, Res} ->
IQ#iq{type = result, sub_el = [Res]}; exmpp_iq:result(IQ, Res);
{error, Error} -> {error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]} exmpp_iq:error(IQ, Error)
end end
end end
end. end.
process_get({xmlelement, "info", _Attrs, _SubEls}) -> process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'info'}) ->
S2SConns = ejabberd_s2s:dirty_get_connections(), S2SConns = ejabberd_s2s:dirty_get_connections(),
TConns = lists:usort([element(2, C) || C <- S2SConns]), 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))}, integer_to_list(mnesia:table_info(passwd, size))},
{"online-users", #xmlattr{name = 'online-users', value =
integer_to_list(mnesia:table_info(presence, size))}, 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)))}, integer_to_list(length(mnesia:system_info(running_db_nodes)))},
{"stopped-nodes", #xmlattr{name = 'stopped-nodes', value =
integer_to_list( integer_to_list(
length(lists:usort(mnesia:system_info(db_nodes) ++ length(lists:usort(mnesia:system_info(db_nodes) ++
mnesia:system_info(extra_db_nodes)) -- mnesia:system_info(extra_db_nodes)) --
mnesia:system_info(running_db_nodes)))}, mnesia:system_info(running_db_nodes)))},
{"outgoing-s2s-servers", integer_to_list(length(TConns))}], #xmlattr{name = 'outgoing-s2s-servers', value =
{result, {xmlelement, "info", integer_to_list(length(TConns))}],
[{"xmlns", ?NS_ECONFIGURE} | Attrs], []}}; {result, #xmlel{ns = ?NS_ECONFIGURE, name = 'info', attrs = Attrs}};
process_get({xmlelement, "welcome-message", Attrs, _SubEls}) -> process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'welcome-message', attrs = Attrs}) ->
{Subj, Body} = case ejabberd_config:get_local_option(welcome_message) of {Subj, Body} = case ejabberd_config:get_local_option(welcome_message) of
{_Subj, _Body} = SB -> SB; {_Subj, _Body} = SB -> SB;
_ -> {"", ""} _ -> {"", ""}
end, end,
{result, {xmlelement, "welcome-message", Attrs, {result, #xmlel{ns = ?NS_ECONFIGURE, name = 'welcome-message',
[{xmlelement, "subject", [], [{xmlcdata, Subj}]}, attrs = Attrs, children =
{xmlelement, "body", [], [{xmlcdata, Body}]}]}}; [#xmlel{ns = ?NS_ECONFIGURE, name = 'subject', children = [#xmlcdata{cdata = list_to_binary(Subj)}]},
process_get({xmlelement, "registration-watchers", Attrs, _SubEls}) -> #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 = SubEls =
case ejabberd_config:get_local_option(registration_watchers) of case ejabberd_config:get_local_option(registration_watchers) of
JIDs when is_list(JIDs) -> JIDs when is_list(JIDs) ->
lists:map(fun(JID) -> lists:map(fun(JID) ->
{xmlelement, "jid", [], [{xmlcdata, JID}]} #xmlel{ns = ?NS_ECONFIGURE, name = 'jid', children = [#xmlcdata{cdata = list_to_binary(JID)}]}
end, JIDs); end, JIDs);
_ -> _ ->
[] []
end, end,
{result, {xmlelement, "registration_watchers", Attrs, SubEls}}; {result, #xmlel{ns = ?NS_ECONFIGURE, name = 'registration_watchers', attrs = Attrs, children = SubEls}};
process_get({xmlelement, "acls", Attrs, _SubEls}) -> process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'acls', attrs = Attrs}) ->
Str = lists:flatten(io_lib:format("~p.", [ets:tab2list(acl)])), Str = lists:flatten(io_lib:format("~p.", [ets:tab2list(acl)])),
{result, {xmlelement, "acls", Attrs, [{xmlcdata, Str}]}}; {result, #xmlel{ns = ?NS_ECONFIGURE, name = 'acls', attrs = Attrs, children = [#xmlcdata{cdata = list_to_binary(Str)}]}};
process_get({xmlelement, "access", Attrs, _SubEls}) -> process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'access', attrs = Attrs}) ->
Str = Str =
lists:flatten( lists:flatten(
io_lib:format( io_lib:format(
@ -149,22 +156,22 @@ process_get({xmlelement, "access", Attrs, _SubEls}) ->
[], [],
[{{access, '$1', '$2'}}]}]) [{{access, '$1', '$2'}}]}])
])), ])),
{result, {xmlelement, "access", Attrs, [{xmlcdata, Str}]}}; {result, #xmlel{ns = ?NS_ECONFIGURE, name = 'access', attrs = Attrs, children = [#xmlcdata{cdata = list_to_binary(Str)}]}};
process_get({xmlelement, "last", Attrs, _SubEls}) -> process_get(#xmlel{ns = ?NS_ECONFIGURE, name = 'last', attrs = Attrs}) ->
case catch mnesia:dirty_select( case catch mnesia:dirty_select(
last_activity, [{{last_activity, '_', '$1', '_'}, [], ['$1']}]) of last_activity, [{{last_activity, '_', '$1', '_'}, [], ['$1']}]) of
{'EXIT', _Reason} -> {'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR}; {error, 'internal-server-error'};
Vals -> Vals ->
{MegaSecs, Secs, _MicroSecs} = now(), {MegaSecs, Secs, _MicroSecs} = now(),
TimeStamp = MegaSecs * 1000000 + Secs, TimeStamp = MegaSecs * 1000000 + Secs,
Str = lists:flatten( Str = lists:flatten(
lists:append( lists:append(
[[integer_to_list(TimeStamp - V), " "] || V <- Vals])), [[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; end;
%%process_get({xmlelement, Name, Attrs, SubEls}) -> %%process_get({xmlelement, Name, Attrs, SubEls}) ->
%% {result, }; %% {result, };
process_get(_) -> 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, -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]). terminate/2, code_change/3]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.hrl").
-record(state, {host}). -record(state, {host}).
@ -117,8 +118,8 @@ handle_cast(_Msg, State) ->
%% Description: Handling all non call/cast messages %% Description: Handling all non call/cast messages
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) -> handle_info({route, From, To, Packet}, State) ->
Packet2 = case From#jid.user of Packet2 = case From#jid.node of
"" -> jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST); <<>> -> exmpp_stanza:reply_with_error(Packet, 'bad-request');
_ -> Packet _ -> Packet
end, end,
do_client_version(disabled, To, From), % Put 'enabled' to enable it 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) -> do_client_version(disabled, _From, _To) ->
ok; ok;
do_client_version(enabled, From, To) -> do_client_version(enabled, From, To) ->
ToS = jlib:jid_to_string(To),
%% It is important to identify this process and packet %% It is important to identify this process and packet
Random_resource = integer_to_list(random:uniform(100000)), Random_resource = integer_to_list(random:uniform(100000)),
From2 = From#jid{resource = Random_resource, From2 = From#jid{resource = Random_resource,
lresource = Random_resource}, lresource = Random_resource},
%% Build an iq:query request %% Build an iq:query request
Packet = {xmlelement, "iq", Request = #xmlel{ns = ?NS_SOFT_VERSION, name = 'query'},
[{"to", ToS}, {"type", "get"}], Packet = exmpp_stanza:set_recipient(
[{xmlelement, "query", [{"xmlns", ?NS_VERSION}], []}]}, exmpp_iq:get(?NS_JABBER_CLIENT, Request),
To),
%% Send the request %% Send the request
ejabberd_router:route(From2, To, Packet), 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 %% It is very important to only accept a packet which is the
%% response to the request that he sent %% response to the request that he sent
Els = receive {route, To, From2, IQ} -> 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 List
after 5000 -> % Timeout in miliseconds: 5 seconds after 5000 -> % Timeout in miliseconds: 5 seconds
[] []
end, 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 %% Print in log
Values_string1 = [io_lib:format("~n~s: ~p", [N, V]) || {N, V} <- Values], Values_string1 = [io_lib:format("~n~s: ~p", [N, V]) || {N, V} <- Values],
Values_string2 = lists:concat(Values_string1), 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, get_last_info/2,
remove_user/2]). remove_user/2]).
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_privacy.hrl"). -include("mod_privacy.hrl").
-record(last_activity, {us, timestamp, status}). -record(last_activity, {us, timestamp, status}).
@ -51,9 +52,9 @@ start(Host, Opts) ->
[{disc_copies, [node()]}, [{disc_copies, [node()]},
{attributes, record_info(fields, last_activity)}]), {attributes, record_info(fields, last_activity)}]),
update_table(), 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), ?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), ?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(remove_user, Host, ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50), ?MODULE, remove_user, 50),
@ -65,30 +66,28 @@ stop(Host) ->
?MODULE, remove_user, 50), ?MODULE, remove_user, 50),
ejabberd_hooks:delete(unset_presence_hook, Host, ejabberd_hooks:delete(unset_presence_hook, Host,
?MODULE, on_presence_update, 50), ?MODULE, on_presence_update, 50),
gen_iq_handler:remove_iq_handler(ejabberd_local, 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). 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) -> process_local_iq(_From, _To, IQ) ->
case Type of case exmpp_iq:get_type(IQ) of
set -> set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; exmpp_iq:error(IQ, 'not-allowed');
get -> get ->
Sec = trunc(element(1, erlang:statistics(wall_clock))/1000), Sec = trunc(element(1, erlang:statistics(wall_clock))/1000),
IQ#iq{type = result, Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs =
sub_el = [{xmlelement, "query", [#xmlattr{name = 'seconds', value = integer_to_list(Sec)}]},
[{"xmlns", ?NS_LAST}, exmpp_iq:result(IQ, Response)
{"seconds", integer_to_list(Sec)}],
[]}]}
end. end.
process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) -> process_sm_iq(From, To, IQ) ->
case Type of case exmpp_iq:get_type(IQ) of
set -> set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; exmpp_iq:error(IQ, 'not-allowed');
get -> get ->
User = To#jid.luser, User = To#jid.lnode,
Server = To#jid.lserver, Server = To#jid.ldomain,
{Subscription, _Groups} = {Subscription, _Groups} =
ejabberd_hooks:run_fold( ejabberd_hooks:run_fold(
roster_get_jid_info, Server, roster_get_jid_info, Server,
@ -104,36 +103,33 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
allow, allow,
[User, Server, UserListRecord, [User, Server, UserListRecord,
{From, To, {From, To,
{xmlelement, "presence", [], []}}, exmpp_presence:available()},
out]) of out]) of
allow -> allow ->
get_last(IQ, SubEl, User, Server); get_last(IQ, User, Server);
deny -> deny ->
IQ#iq{type = error, exmpp_iq:error(IQ, 'not-allowed')
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
end; end;
true -> true ->
IQ#iq{type = error, exmpp_iq:error(IQ, 'not-allowed')
sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
end end
end. end.
%% TODO: This function could use get_last_info/2 %% 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 case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
{'EXIT', _Reason} -> {'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}] -> [#last_activity{timestamp = TimeStamp, status = Status}] ->
{MegaSecs, Secs, _MicroSecs} = now(), {MegaSecs, Secs, _MicroSecs} = now(),
TimeStamp2 = MegaSecs * 1000000 + Secs, TimeStamp2 = MegaSecs * 1000000 + Secs,
Sec = TimeStamp2 - TimeStamp, Sec = TimeStamp2 - TimeStamp,
IQ#iq{type = result, Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query',
sub_el = [{xmlelement, "query", attrs = [#xmlattr{name = 'seconds', value = integer_to_list(Sec)}],
[{"xmlns", ?NS_LAST}, children = [#xmlcdata{cdata = list_to_binary(Status)}]},
{"seconds", integer_to_list(Sec)}], exmpp_iq:result(IQ, Response)
[{xmlcdata, Status}]}]}
end. 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).
store_last_info(User, Server, TimeStamp, Status) -> store_last_info(User, Server, TimeStamp, Status) ->
LUser = jlib:nodeprep(User), LUser = exmpp_stringprep:nodeprep(User),
LServer = jlib:nameprep(Server), LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer}, US = {LUser, LServer},
F = fun() -> F = fun() ->
mnesia:write(#last_activity{us = US, mnesia:write(#last_activity{us = US,
@ -166,8 +162,8 @@ get_last_info(LUser, LServer) ->
end. end.
remove_user(User, Server) -> remove_user(User, Server) ->
LUser = jlib:nodeprep(User), LUser = exmpp_stringprep:nodeprep(User),
LServer = jlib:nameprep(Server), LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer}, US = {LUser, LServer},
F = fun() -> F = fun() ->
mnesia:delete({last_activity, US}) mnesia:delete({last_activity, US})

View File

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

View File

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

View File

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