Rewrite mod_offline to use XML generator

This commit is contained in:
Evgeniy Khramtsov 2016-07-19 07:56:14 +03:00
parent 9a8e197d7e
commit a4a9dd7f03
5 changed files with 538 additions and 215 deletions

View File

@ -11,7 +11,8 @@
-record(csi, {type :: active | inactive}).
-type csi() :: #csi{}.
-record(hint, {type :: 'no-copy' | 'no-store' | 'store' | 'no-permanent-store'}).
-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' |
'store' | 'no-permanent-store'}).
-type hint() :: #hint{}.
-record(feature_register, {}).
@ -50,6 +51,10 @@
-record(carbons_private, {}).
-type carbons_private() :: #carbons_private{}.
-record(expire, {seconds :: non_neg_integer(),
stored :: non_neg_integer()}).
-type expire() :: #expire{}.
-record(pubsub_unsubscribe, {node :: binary(),
jid :: any(),
subid :: binary()}).
@ -304,6 +309,13 @@
-record(sasl_abort, {}).
-type sasl_abort() :: #sasl_abort{}.
-record(xevent, {offline = false :: boolean(),
delivered = false :: boolean(),
displayed = false :: boolean(),
composing = false :: boolean(),
id :: binary()}).
-type xevent() :: #xevent{}.
-record(vcard_email, {home = false :: boolean(),
work = false :: boolean(),
internet = false :: boolean(),
@ -717,6 +729,7 @@
starttls_proceed() |
sm_resumed() |
forwarded() |
xevent() |
privacy_list() |
text() |
vcard_org() |
@ -735,12 +748,12 @@
pubsub_options() |
compress() |
bytestreams() |
muc_history() |
identity() |
feature_csi() |
muc_user_destroy() |
privacy_query() |
delay() |
muc_history() |
vcard_tel() |
vcard_logo() |
disco_info() |
@ -780,6 +793,7 @@
vcard_name() |
sm_resume() |
carbons_enable() |
expire() |
pubsub_unsubscribe() |
muc_decline() |
chatstate() |

View File

@ -49,7 +49,7 @@
get_sm_identity/5,
get_sm_items/5,
get_info/5,
handle_offline_query/3,
handle_offline_query/1,
remove_expired_messages/1,
remove_old_messages/2,
remove_user/2,
@ -73,7 +73,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
-include("xmpp.hrl").
-include("ejabberd_http.hrl").
@ -250,7 +250,7 @@ receive_all(US, Msgs, DBType) ->
end
end.
get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
get_sm_features(Acc, _From, _To, undefined, _Lang) ->
Feats = case Acc of
{result, I} -> I;
_ -> []
@ -268,12 +268,10 @@ get_sm_features(_Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
Acc.
get_sm_identity(_Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S},
get_sm_identity(Acc, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S},
?NS_FLEX_OFFLINE, _Lang) ->
Identity = #xmlel{name = <<"identity">>,
attrs = [{<<"category">>, <<"automation">>},
{<<"type">>, <<"message-list">>}]},
[Identity];
[#identity{category = <<"automation">>,
type = <<"message-list">>}|Acc];
get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
@ -282,15 +280,16 @@ get_sm_items(_Acc, #jid{luser = U, lserver = S, lresource = R} = JID,
?NS_FLEX_OFFLINE, _Lang) ->
case ejabberd_sm:get_session_pid(U, S, R) of
Pid when is_pid(Pid) ->
Hdrs = read_message_headers(U, S),
BareJID = jid:to_string(jid:remove_resource(JID)),
Mod = gen_mod:db_mod(S, ?MODULE),
Hdrs = Mod:read_message_headers(U, S),
BareJID = jid:remove_resource(JID),
Pid ! dont_ask_offline,
{result, lists:map(
fun({Node, From, _To, _El}) ->
#xmlel{name = <<"item">>,
attrs = [{<<"jid">>, BareJID},
{<<"node">>, Node},
{<<"name">>, jid:to_string(From)}]}
fun({Seq, From, _To, _El}) ->
Node = integer_to_binary(Seq),
#disco_item{jid = BareJID,
node = Node,
name = jid:to_string(From)}
end, Hdrs)};
none ->
{result, []}
@ -298,6 +297,8 @@ get_sm_items(_Acc, #jid{luser = U, lserver = S, lresource = R} = JID,
get_sm_items(Acc, _From, _To, _Node, _Lang) ->
Acc.
-spec get_info([xdata()], jid(), jid(),
undefined | binary(), undefined | binary()) -> [xdata()].
get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
#jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) ->
N = jlib:integer_to_binary(count_offline_messages(U, S)),
@ -307,50 +308,42 @@ get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
none ->
ok
end,
[#xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA},
{<<"type">>, <<"result">>}],
children = [#xmlel{name = <<"field">>,
attrs = [{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}],
children = [#xmlel{name = <<"value">>,
children = [{xmlcdata,
?NS_FLEX_OFFLINE}]}]},
#xmlel{name = <<"field">>,
attrs = [{<<"var">>, <<"number_of_messages">>}],
children = [#xmlel{name = <<"value">>,
children = [{xmlcdata, N}]}]}]}];
[#xdata{type = result,
fields = [#xdata_field{var = <<"FORM_TYPE">>,
type = hidden,
values = [?NS_FLEX_OFFLINE]},
#xdata_field{var = <<"number_of_messages">>,
values = [N]}]}];
get_info(Acc, _From, _To, _Node, _Lang) ->
Acc.
handle_offline_query(#jid{luser = U, lserver = S} = From,
#jid{luser = U, lserver = S} = _To,
#iq{type = Type, sub_el = SubEl} = IQ) ->
-spec handle_offline_query(iq()) -> iq().
handle_offline_query(#iq{from = #jid{luser = U, lserver = S} = From,
to = #jid{luser = U, lserver = S} = _To,
type = Type,
sub_els = [#offline{purge = Purge,
items = Items,
fetch = Fetch}]} = IQ) ->
case Type of
get ->
case fxml:get_subtag(SubEl, <<"fetch">>) of
#xmlel{} ->
handle_offline_fetch(From);
false ->
handle_offline_items_view(From, SubEl)
if Fetch -> handle_offline_fetch(From);
true -> handle_offline_items_view(From, Items)
end;
set ->
case fxml:get_subtag(SubEl, <<"purge">>) of
#xmlel{} ->
delete_all_msgs(U, S);
false ->
handle_offline_items_remove(From, SubEl)
if Purge -> delete_all_msgs(U, S);
true -> handle_offline_items_remove(From, Items)
end
end,
IQ#iq{type = result, sub_el = []};
handle_offline_query(_From, _To, #iq{sub_el = SubEl, lang = Lang} = IQ) ->
xmpp:make_iq_result(IQ);
handle_offline_query(#iq{lang = Lang} = IQ) ->
Txt = <<"Query to another users is forbidden">>,
IQ#iq{type = error, sub_el = [SubEl, ?ERRT_FORBIDDEN(Lang, Txt)]}.
xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)).
handle_offline_items_view(JID, #xmlel{children = Items}) ->
-spec handle_offline_items_view(jid(), [offline_item()]) -> ok.
handle_offline_items_view(JID, Items) ->
{U, S, R} = jid:tolower(JID),
lists:foreach(
fun(Node) ->
fun(#offline_item{node = Node, action = view}) ->
case fetch_msg_by_node(JID, Node) of
{ok, OfflineMsg} ->
case offline_msg_to_route(S, OfflineMsg) of
@ -367,40 +360,25 @@ handle_offline_items_view(JID, #xmlel{children = Items}) ->
end;
error ->
ok
end
end, get_nodes_from_items(Items, <<"view">>)).
handle_offline_items_remove(JID, #xmlel{children = Items}) ->
lists:foreach(
fun(Node) ->
remove_msg_by_node(JID, Node)
end, get_nodes_from_items(Items, <<"remove">>)).
get_nodes_from_items(Items, Action) ->
lists:flatmap(
fun(#xmlel{name = <<"item">>, attrs = Attrs}) ->
case fxml:get_attr_s(<<"action">>, Attrs) of
Action ->
case fxml:get_attr_s(<<"node">>, Attrs) of
<<"">> ->
[];
TS ->
[TS]
end;
_ ->
[]
end;
(_) ->
[]
ok
end, Items).
set_offline_tag(#xmlel{children = Els} = El, Node) ->
OfflineEl = #xmlel{name = <<"offline">>,
attrs = [{<<"xmlns">>, ?NS_FLEX_OFFLINE}],
children = [#xmlel{name = <<"item">>,
attrs = [{<<"node">>, Node}]}]},
El#xmlel{children = [OfflineEl|Els]}.
-spec handle_offline_items_remove(jid(), [offline_item()]) -> ok.
handle_offline_items_remove(JID, Items) ->
lists:foreach(
fun(#offline_item{node = Node, action = remove}) ->
remove_msg_by_node(JID, Node);
(_) ->
ok
end, Items).
-spec set_offline_tag(message(), binary()) -> message().
set_offline_tag(Msg, Node) ->
xmpp:set_subtag(Msg, #offline{items = [#offline_item{node = Node}]}).
-spec handle_offline_fetch(jid()) -> ok.
handle_offline_fetch(#jid{luser = U, lserver = S, lresource = R}) ->
case ejabberd_sm:get_session_pid(U, S, R) of
none ->
@ -414,6 +392,7 @@ handle_offline_fetch(#jid{luser = U, lserver = S, lresource = R}) ->
end, read_message_headers(U, S))
end.
-spec fetch_msg_by_node(jid(), binary()) -> error | {ok, #offline_msg{}}.
fetch_msg_by_node(To, Seq) ->
case catch binary_to_integer(Seq) of
I when is_integer(I), I >= 0 ->
@ -425,6 +404,7 @@ fetch_msg_by_node(To, Seq) ->
error
end.
-spec remove_msg_by_node(jid(), binary()) -> ok.
remove_msg_by_node(To, Seq) ->
case catch binary_to_integer(Seq) of
I when is_integer(I), I>= 0 ->
@ -436,39 +416,38 @@ remove_msg_by_node(To, Seq) ->
ok
end.
-spec need_to_store(binary(), message()) -> boolean().
need_to_store(_LServer, #message{type = error}) -> false;
need_to_store(_LServer, #message{type = groupchat}) -> false;
need_to_store(_LServer, #message{type = headline}) -> false;
need_to_store(LServer, Packet) ->
Type = fxml:get_tag_attr_s(<<"type">>, Packet),
if (Type /= <<"error">>) and (Type /= <<"groupchat">>)
and (Type /= <<"headline">>) ->
case has_offline_tag(Packet) of
false ->
case check_store_hint(Packet) of
store ->
true;
no_store ->
false;
none ->
case gen_mod:get_module_opt(
LServer, ?MODULE, store_empty_body,
fun(V) when is_boolean(V) -> V;
(unless_chat_state) -> unless_chat_state
end,
unless_chat_state) of
false ->
fxml:get_subtag(Packet, <<"body">>) /= false;
unless_chat_state ->
not jlib:is_standalone_chat_state(Packet);
true ->
true
end
end;
true ->
false
case xmpp:has_subtag(Packet, #offline{}) of
false ->
case check_store_hint(Packet) of
store ->
true;
no_store ->
false;
none ->
case gen_mod:get_module_opt(
LServer, ?MODULE, store_empty_body,
fun(V) when is_boolean(V) -> V;
(unless_chat_state) -> unless_chat_state
end,
unless_chat_state) of
false ->
Packet#message.body /= [];
unless_chat_state ->
not xmpp_util:is_standalone_chat_state(Packet);
true ->
true
end
end;
true ->
true ->
false
end.
-spec store_packet(jid(), jid(), message()) -> ok | stop.
store_packet(From, To, Packet) ->
case need_to_store(To#jid.lserver, Packet) of
true ->
@ -476,18 +455,19 @@ store_packet(From, To, Packet) ->
true ->
#jid{luser = LUser, lserver = LServer} = To,
TimeStamp = p1_time_compat:timestamp(),
#xmlel{children = Els} = Packet,
Expire = find_x_expire(TimeStamp, Els),
Expire = find_x_expire(TimeStamp, Packet),
El = xmpp:encode(Packet),
gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
#offline_msg{us = {LUser, LServer},
timestamp = TimeStamp, expire = Expire,
from = From, to = To, packet = Packet},
from = From, to = To, packet = El},
stop;
_ -> ok
end;
false -> ok
end.
-spec check_store_hint(message()) -> store | no_store | none.
check_store_hint(Packet) ->
case has_store_hint(Packet) of
true ->
@ -501,89 +481,43 @@ check_store_hint(Packet) ->
end
end.
-spec has_store_hint(message()) -> boolean().
has_store_hint(Packet) ->
fxml:get_subtag_with_xmlns(Packet, <<"store">>, ?NS_HINTS) =/= false.
xmpp:has_subtag(Packet, #hint{type = 'store'}).
-spec has_no_store_hint(message()) -> boolean().
has_no_store_hint(Packet) ->
fxml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS) =/= false
orelse
fxml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS) =/= false.
has_offline_tag(Packet) ->
fxml:get_subtag_with_xmlns(Packet, <<"offline">>, ?NS_FLEX_OFFLINE) =/= false.
xmpp:has_subtag(Packet, #hint{type = 'no-store'})
orelse
xmpp:has_subtag(Packet, #hint{type = 'no-storage'}).
%% Check if the packet has any content about XEP-0022
check_event(From, To, Packet) ->
#xmlel{name = Name, attrs = Attrs, children = Els} =
Packet,
case find_x_event(Els) of
false -> true;
El ->
case fxml:get_subtag(El, <<"id">>) of
false ->
case fxml:get_subtag(El, <<"offline">>) of
false -> true;
_ ->
ID = case fxml:get_tag_attr_s(<<"id">>, Packet) of
<<"">> ->
#xmlel{name = <<"id">>, attrs = [],
children = []};
S ->
#xmlel{name = <<"id">>, attrs = [],
children = [{xmlcdata, S}]}
end,
ejabberd_router:route(To, From,
#xmlel{name = Name, attrs = Attrs,
children =
[#xmlel{name = <<"x">>,
attrs =
[{<<"xmlns">>,
?NS_EVENT}],
children =
[ID,
#xmlel{name
=
<<"offline">>,
attrs
=
[],
children
=
[]}]}]}),
true
end;
_ -> false
end
-spec check_event(jid(), jid(), message()) -> boolean().
check_event(From, To, #message{id = ID} = Msg) ->
case xmpp:get_subtag(Msg, #xevent{}) of
false ->
true;
#xevent{id = undefined, offline = false} ->
true;
#xevent{id = undefined, offline = true} ->
NewMsg = Msg#message{sub_els = [#xevent{id = ID, offline = true}]},
ejabberd_router:route(To, From, xmpp:set_from_to(NewMsg, To, From)),
true;
_ ->
false
end.
%% Check if the packet has subelements about XEP-0022
find_x_event([]) -> false;
find_x_event([{xmlcdata, _} | Els]) ->
find_x_event(Els);
find_x_event([El | Els]) ->
case fxml:get_tag_attr_s(<<"xmlns">>, El) of
?NS_EVENT -> El;
_ -> find_x_event(Els)
end.
find_x_expire(_, []) -> never;
find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
find_x_expire(TimeStamp, Els);
find_x_expire(TimeStamp, [El | Els]) ->
case fxml:get_tag_attr_s(<<"xmlns">>, El) of
?NS_EXPIRE ->
Val = fxml:get_tag_attr_s(<<"seconds">>, El),
case catch jlib:binary_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, Els)
-spec find_x_expire(erlang:timestamp(), message()) -> erlang:timestamp() | never.
find_x_expire(TimeStamp, Msg) ->
case xmpp:get_subtag(Msg, #expire{}) of
#expire{seconds = Int} ->
{MegaSecs, Secs, MicroSecs} = TimeStamp,
S = MegaSecs * 1000000 + Secs + Int,
MegaSecs1 = S div 1000000,
Secs1 = S rem 1000000,
{MegaSecs1, Secs1, MicroSecs};
false ->
never
end.
resend_offline_messages(User, Server) ->
@ -612,10 +546,9 @@ pop_offline_messages(Ls, User, Server) ->
end,
lists:filter(
fun(#offline_msg{packet = Pkt} = R) ->
#xmlel{children = Els} = Pkt,
Expire = case R#offline_msg.expire of
undefined ->
find_x_expire(TS, Els);
find_x_expire(TS, Pkt);
Exp ->
Exp
end,
@ -648,17 +581,15 @@ remove_user(User, Server) ->
%% Warn senders that their messages have been discarded:
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 = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
Err = jlib:make_error_reply(Packet,
?ERRT_RESOURCE_CONSTRAINT(Lang,
ErrText)),
ejabberd_router:route(To, From, Err)
end,
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 = xmpp:get_lang(Packet),
Err = xmpp:make_error(
Packet, xmpp:err_resource_constraint(ErrText, Lang)),
ejabberd_router:route(To, From, Err)
end, Msgs).
webadmin_page(_, Host,
#request{us = _US, path = [<<"user">>, U, <<"queue">>],
@ -668,29 +599,30 @@ webadmin_page(_, Host,
webadmin_page(Acc, _, _) -> Acc.
get_offline_els(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Hdrs = Mod:read_message_headers(LUser, LServer),
Hdrs = read_message_headers(LUser, LServer),
lists:map(
fun({_Seq, From, To, Packet}) ->
jlib:replace_from_to(From, To, Packet)
xmpp:set_from_to(Packet, From, To)
end, Hdrs).
offline_msg_to_route(LServer, #offline_msg{} = R) ->
El = case R#offline_msg.timestamp of
undefined ->
R#offline_msg.packet;
TS ->
jlib:add_delay_info(R#offline_msg.packet, LServer, TS,
<<"Offline Storage">>)
end,
{route, R#offline_msg.from, R#offline_msg.to, El}.
Pkt = xmpp:decode(R#offline_msg.packet, [ignore_els]),
Pkt1 = case R#offline_msg.timestamp of
undefined ->
Pkt;
TS ->
xmpp_util:add_delay_info(Pkt, LServer, TS,
<<"Offline Storage">>)
end,
{route, R#offline_msg.from, R#offline_msg.to, Pkt1}.
read_message_headers(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
lists:map(
fun({Seq, From, To, El}) ->
Node = integer_to_binary(Seq),
{Node, From, To, El}
Packet = xmpp:decode(El, [ignore_els]),
{Node, From, To, Packet}
end, Mod:read_message_headers(LUser, LServer)).
format_user_queue(Hdrs) ->
@ -826,6 +758,7 @@ webadmin_user(Acc, User, Server, Lang) ->
?INPUTT(<<"submit">>, <<"removealloffline">>,
<<"Remove All Offline Messages">>)].
-spec delete_all_msgs(binary(), binary()) -> {atomic, any()}.
delete_all_msgs(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
@ -849,6 +782,7 @@ webadmin_user_parse_query(Acc, _Action, _User, _Server,
Acc.
%% Returns as integer the number of offline messages for a given user
-spec count_offline_messages(binary(), binary()) -> non_neg_integer().
count_offline_messages(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),

View File

@ -156,10 +156,12 @@ get_error(#iq{error = E}) -> E;
get_error(#message{error = E}) -> E;
get_error(#presence{error = E}) -> E.
-spec get_els(iq() | message() | presence()) -> [xmpp_element() | xmlel()].
-spec get_els(iq() | message() | presence()) -> [xmpp_element() | xmlel()];
(xmlel()) -> [xmlel()].
get_els(#iq{sub_els = Els}) -> Els;
get_els(#message{sub_els = Els}) -> Els;
get_els(#presence{sub_els = Els}) -> Els.
get_els(#presence{sub_els = Els}) -> Els;
get_els(#xmlel{children = Els}) -> [El || El = #xmlel{} <- Els].
-spec set_id(iq(), binary()) -> iq();
(message(), binary()) -> message();

View File

@ -15,6 +15,24 @@ decode(_el) -> decode(_el, []).
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls = proplists:get_bool(ignore_els, Opts),
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"x">>, <<"jabber:x:expire">>} ->
decode_expire(<<"jabber:x:expire">>, IgnoreEls, _el);
{<<"x">>, <<"jabber:x:event">>} ->
decode_xevent(<<"jabber:x:event">>, IgnoreEls, _el);
{<<"id">>, <<"jabber:x:event">>} ->
decode_xevent_id(<<"jabber:x:event">>, IgnoreEls, _el);
{<<"composing">>, <<"jabber:x:event">>} ->
decode_xevent_composing(<<"jabber:x:event">>, IgnoreEls,
_el);
{<<"displayed">>, <<"jabber:x:event">>} ->
decode_xevent_displayed(<<"jabber:x:event">>, IgnoreEls,
_el);
{<<"delivered">>, <<"jabber:x:event">>} ->
decode_xevent_delivered(<<"jabber:x:event">>, IgnoreEls,
_el);
{<<"offline">>, <<"jabber:x:event">>} ->
decode_xevent_offline(<<"jabber:x:event">>, IgnoreEls,
_el);
{<<"query">>, <<"jabber:iq:search">>} ->
decode_search(<<"jabber:iq:search">>, IgnoreEls, _el);
{<<"item">>, <<"jabber:iq:search">>} ->
@ -40,6 +58,9 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls, _el);
{<<"store">>, <<"urn:xmpp:hints">>} ->
decode_hint_store(<<"urn:xmpp:hints">>, IgnoreEls, _el);
{<<"no-storage">>, <<"urn:xmpp:hints">>} ->
decode_hint_no_storage(<<"urn:xmpp:hints">>, IgnoreEls,
_el);
{<<"no-store">>, <<"urn:xmpp:hints">>} ->
decode_hint_no_store(<<"urn:xmpp:hints">>, IgnoreEls,
_el);
@ -1162,6 +1183,13 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"x">>, <<"jabber:x:expire">>} -> true;
{<<"x">>, <<"jabber:x:event">>} -> true;
{<<"id">>, <<"jabber:x:event">>} -> true;
{<<"composing">>, <<"jabber:x:event">>} -> true;
{<<"displayed">>, <<"jabber:x:event">>} -> true;
{<<"delivered">>, <<"jabber:x:event">>} -> true;
{<<"offline">>, <<"jabber:x:event">>} -> true;
{<<"query">>, <<"jabber:iq:search">>} -> true;
{<<"item">>, <<"jabber:iq:search">>} -> true;
{<<"email">>, <<"jabber:iq:search">>} -> true;
@ -1172,6 +1200,7 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
{<<"no-permanent-store">>, <<"urn:xmpp:hints">>} ->
true;
{<<"store">>, <<"urn:xmpp:hints">>} -> true;
{<<"no-storage">>, <<"urn:xmpp:hints">>} -> true;
{<<"no-store">>, <<"urn:xmpp:hints">>} -> true;
{<<"no-copy">>, <<"urn:xmpp:hints">>} -> true;
{<<"participant">>, <<"urn:xmpp:mix:0">>} -> true;
@ -2289,6 +2318,9 @@ encode({hint, 'no-copy'} = No_copy) ->
encode({hint, 'no-store'} = No_store) ->
encode_hint_no_store(No_store,
[{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
encode({hint, 'no-storage'} = No_storage) ->
encode_hint_no_storage(No_storage,
[{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
encode({hint, store} = Store) ->
encode_hint_store(Store,
[{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
@ -2301,7 +2333,12 @@ encode({search_item, _, _, _, _, _} = Item) ->
[{<<"xmlns">>, <<"jabber:iq:search">>}]);
encode({search, _, _, _, _, _, _, _} = Query) ->
encode_search(Query,
[{<<"xmlns">>, <<"jabber:iq:search">>}]).
[{<<"xmlns">>, <<"jabber:iq:search">>}]);
encode({xevent, _, _, _, _, _} = X) ->
encode_xevent(X, [{<<"xmlns">>, <<"jabber:x:event">>}]);
encode({expire, _, _} = X) ->
encode_expire(X,
[{<<"xmlns">>, <<"jabber:x:expire">>}]).
get_name({last, _, _}) -> <<"query">>;
get_name({version, _, _, _}) -> <<"query">>;
@ -2460,11 +2497,14 @@ get_name({mix_leave}) -> <<"leave">>;
get_name({mix_participant, _, _}) -> <<"participant">>;
get_name({hint, 'no-copy'}) -> <<"no-copy">>;
get_name({hint, 'no-store'}) -> <<"no-store">>;
get_name({hint, 'no-storage'}) -> <<"no-storage">>;
get_name({hint, store}) -> <<"store">>;
get_name({hint, 'no-permanent-store'}) ->
<<"no-permanent-store">>;
get_name({search_item, _, _, _, _, _}) -> <<"item">>;
get_name({search, _, _, _, _, _, _, _}) -> <<"query">>.
get_name({search, _, _, _, _, _, _, _}) -> <<"query">>;
get_name({xevent, _, _, _, _, _}) -> <<"x">>;
get_name({expire, _, _}) -> <<"x">>.
get_ns({last, _, _}) -> <<"jabber:iq:last">>;
get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
@ -2685,13 +2725,16 @@ get_ns({mix_leave}) -> <<"urn:xmpp:mix:0">>;
get_ns({mix_participant, _, _}) -> <<"urn:xmpp:mix:0">>;
get_ns({hint, 'no-copy'}) -> <<"urn:xmpp:hints">>;
get_ns({hint, 'no-store'}) -> <<"urn:xmpp:hints">>;
get_ns({hint, 'no-storage'}) -> <<"urn:xmpp:hints">>;
get_ns({hint, store}) -> <<"urn:xmpp:hints">>;
get_ns({hint, 'no-permanent-store'}) ->
<<"urn:xmpp:hints">>;
get_ns({search_item, _, _, _, _, _}) ->
<<"jabber:iq:search">>;
get_ns({search, _, _, _, _, _, _, _}) ->
<<"jabber:iq:search">>.
<<"jabber:iq:search">>;
get_ns({xevent, _, _, _, _, _}) -> <<"jabber:x:event">>;
get_ns({expire, _, _}) -> <<"jabber:x:expire">>.
dec_int(Val) -> dec_int(Val, infinity, infinity).
@ -2908,6 +2951,9 @@ pp(hint, 1) -> [type];
pp(search_item, 5) -> [jid, first, last, nick, email];
pp(search, 7) ->
[instructions, first, last, nick, email, items, xdata];
pp(xevent, 5) ->
[offline, delivered, displayed, composing, id];
pp(expire, 2) -> [seconds, stored];
pp(_, _) -> no.
join([], _Sep) -> <<>>;
@ -2954,6 +3000,267 @@ dec_tzo(Val) ->
M = jlib:binary_to_integer(M1),
if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
decode_expire(__TopXMLNS, __IgnoreEls,
{xmlel, <<"x">>, _attrs, _els}) ->
{Seconds, Stored} = decode_expire_attrs(__TopXMLNS,
_attrs, undefined, undefined),
{expire, Seconds, Stored}.
decode_expire_attrs(__TopXMLNS,
[{<<"seconds">>, _val} | _attrs], _Seconds, Stored) ->
decode_expire_attrs(__TopXMLNS, _attrs, _val, Stored);
decode_expire_attrs(__TopXMLNS,
[{<<"stored">>, _val} | _attrs], Seconds, _Stored) ->
decode_expire_attrs(__TopXMLNS, _attrs, Seconds, _val);
decode_expire_attrs(__TopXMLNS, [_ | _attrs], Seconds,
Stored) ->
decode_expire_attrs(__TopXMLNS, _attrs, Seconds,
Stored);
decode_expire_attrs(__TopXMLNS, [], Seconds, Stored) ->
{decode_expire_attr_seconds(__TopXMLNS, Seconds),
decode_expire_attr_stored(__TopXMLNS, Stored)}.
encode_expire({expire, Seconds, Stored},
_xmlns_attrs) ->
_els = [],
_attrs = encode_expire_attr_stored(Stored,
encode_expire_attr_seconds(Seconds,
_xmlns_attrs)),
{xmlel, <<"x">>, _attrs, _els}.
decode_expire_attr_seconds(__TopXMLNS, undefined) ->
erlang:error({xmpp_codec,
{missing_attr, <<"seconds">>, <<"x">>, __TopXMLNS}});
decode_expire_attr_seconds(__TopXMLNS, _val) ->
case catch dec_int(_val, 0, infinity) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_attr_value, <<"seconds">>, <<"x">>, __TopXMLNS}});
_res -> _res
end.
encode_expire_attr_seconds(_val, _acc) ->
[{<<"seconds">>, enc_int(_val)} | _acc].
decode_expire_attr_stored(__TopXMLNS, undefined) ->
undefined;
decode_expire_attr_stored(__TopXMLNS, _val) ->
case catch dec_int(_val, 0, infinity) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_attr_value, <<"stored">>, <<"x">>, __TopXMLNS}});
_res -> _res
end.
encode_expire_attr_stored(undefined, _acc) -> _acc;
encode_expire_attr_stored(_val, _acc) ->
[{<<"stored">>, enc_int(_val)} | _acc].
decode_xevent(__TopXMLNS, __IgnoreEls,
{xmlel, <<"x">>, _attrs, _els}) ->
{Id, Displayed, Delivered, Offline, Composing} =
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els,
undefined, false, false, false, false),
{xevent, Offline, Delivered, Displayed, Composing, Id}.
decode_xevent_els(__TopXMLNS, __IgnoreEls, [], Id,
Displayed, Delivered, Offline, Composing) ->
{Id, Displayed, Delivered, Offline, Composing};
decode_xevent_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"offline">>, _attrs, _} = _el | _els], Id,
Displayed, Delivered, Offline, Composing) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered,
decode_xevent_offline(__TopXMLNS, __IgnoreEls, _el),
Composing);
<<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered,
decode_xevent_offline(<<"jabber:x:event">>,
__IgnoreEls, _el),
Composing);
_ ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline, Composing)
end;
decode_xevent_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"delivered">>, _attrs, _} = _el | _els], Id,
Displayed, Delivered, Offline, Composing) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed,
decode_xevent_delivered(__TopXMLNS, __IgnoreEls,
_el),
Offline, Composing);
<<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed,
decode_xevent_delivered(<<"jabber:x:event">>,
__IgnoreEls, _el),
Offline, Composing);
_ ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline, Composing)
end;
decode_xevent_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"displayed">>, _attrs, _} = _el | _els], Id,
Displayed, Delivered, Offline, Composing) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
decode_xevent_displayed(__TopXMLNS, __IgnoreEls,
_el),
Delivered, Offline, Composing);
<<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
decode_xevent_displayed(<<"jabber:x:event">>,
__IgnoreEls, _el),
Delivered, Offline, Composing);
_ ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline, Composing)
end;
decode_xevent_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"composing">>, _attrs, _} = _el | _els], Id,
Displayed, Delivered, Offline, Composing) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline,
decode_xevent_composing(__TopXMLNS, __IgnoreEls,
_el));
<<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline,
decode_xevent_composing(<<"jabber:x:event">>,
__IgnoreEls, _el));
_ ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline, Composing)
end;
decode_xevent_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"id">>, _attrs, _} = _el | _els], Id,
Displayed, Delivered, Offline, Composing) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els,
decode_xevent_id(__TopXMLNS, __IgnoreEls, _el),
Displayed, Delivered, Offline, Composing);
<<"jabber:x:event">> ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els,
decode_xevent_id(<<"jabber:x:event">>, __IgnoreEls,
_el),
Displayed, Delivered, Offline, Composing);
_ ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline, Composing)
end;
decode_xevent_els(__TopXMLNS, __IgnoreEls, [_ | _els],
Id, Displayed, Delivered, Offline, Composing) ->
decode_xevent_els(__TopXMLNS, __IgnoreEls, _els, Id,
Displayed, Delivered, Offline, Composing).
encode_xevent({xevent, Offline, Delivered, Displayed,
Composing, Id},
_xmlns_attrs) ->
_els = lists:reverse('encode_xevent_$id'(Id,
'encode_xevent_$displayed'(Displayed,
'encode_xevent_$delivered'(Delivered,
'encode_xevent_$offline'(Offline,
'encode_xevent_$composing'(Composing,
[])))))),
_attrs = _xmlns_attrs,
{xmlel, <<"x">>, _attrs, _els}.
'encode_xevent_$id'(undefined, _acc) -> _acc;
'encode_xevent_$id'(Id, _acc) ->
[encode_xevent_id(Id, []) | _acc].
'encode_xevent_$displayed'(false, _acc) -> _acc;
'encode_xevent_$displayed'(Displayed, _acc) ->
[encode_xevent_displayed(Displayed, []) | _acc].
'encode_xevent_$delivered'(false, _acc) -> _acc;
'encode_xevent_$delivered'(Delivered, _acc) ->
[encode_xevent_delivered(Delivered, []) | _acc].
'encode_xevent_$offline'(false, _acc) -> _acc;
'encode_xevent_$offline'(Offline, _acc) ->
[encode_xevent_offline(Offline, []) | _acc].
'encode_xevent_$composing'(false, _acc) -> _acc;
'encode_xevent_$composing'(Composing, _acc) ->
[encode_xevent_composing(Composing, []) | _acc].
decode_xevent_id(__TopXMLNS, __IgnoreEls,
{xmlel, <<"id">>, _attrs, _els}) ->
Cdata = decode_xevent_id_els(__TopXMLNS, __IgnoreEls,
_els, <<>>),
Cdata.
decode_xevent_id_els(__TopXMLNS, __IgnoreEls, [],
Cdata) ->
decode_xevent_id_cdata(__TopXMLNS, Cdata);
decode_xevent_id_els(__TopXMLNS, __IgnoreEls,
[{xmlcdata, _data} | _els], Cdata) ->
decode_xevent_id_els(__TopXMLNS, __IgnoreEls, _els,
<<Cdata/binary, _data/binary>>);
decode_xevent_id_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Cdata) ->
decode_xevent_id_els(__TopXMLNS, __IgnoreEls, _els,
Cdata).
encode_xevent_id(Cdata, _xmlns_attrs) ->
_els = encode_xevent_id_cdata(Cdata, []),
_attrs = _xmlns_attrs,
{xmlel, <<"id">>, _attrs, _els}.
decode_xevent_id_cdata(__TopXMLNS, <<>>) -> undefined;
decode_xevent_id_cdata(__TopXMLNS, _val) -> _val.
encode_xevent_id_cdata(undefined, _acc) -> _acc;
encode_xevent_id_cdata(_val, _acc) ->
[{xmlcdata, _val} | _acc].
decode_xevent_composing(__TopXMLNS, __IgnoreEls,
{xmlel, <<"composing">>, _attrs, _els}) ->
true.
encode_xevent_composing(true, _xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"composing">>, _attrs, _els}.
decode_xevent_displayed(__TopXMLNS, __IgnoreEls,
{xmlel, <<"displayed">>, _attrs, _els}) ->
true.
encode_xevent_displayed(true, _xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"displayed">>, _attrs, _els}.
decode_xevent_delivered(__TopXMLNS, __IgnoreEls,
{xmlel, <<"delivered">>, _attrs, _els}) ->
true.
encode_xevent_delivered(true, _xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"delivered">>, _attrs, _els}.
decode_xevent_offline(__TopXMLNS, __IgnoreEls,
{xmlel, <<"offline">>, _attrs, _els}) ->
true.
encode_xevent_offline(true, _xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"offline">>, _attrs, _els}.
decode_search(__TopXMLNS, __IgnoreEls,
{xmlel, <<"query">>, _attrs, _els}) ->
{Xdata, Items, Instructions, Last, First, Nick, Email} =
@ -3459,6 +3766,16 @@ encode_hint_store({hint, store}, _xmlns_attrs) ->
_attrs = _xmlns_attrs,
{xmlel, <<"store">>, _attrs, _els}.
decode_hint_no_storage(__TopXMLNS, __IgnoreEls,
{xmlel, <<"no-storage">>, _attrs, _els}) ->
{hint, 'no-storage'}.
encode_hint_no_storage({hint, 'no-storage'},
_xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"no-storage">>, _attrs, _els}.
decode_hint_no_store(__TopXMLNS, __IgnoreEls,
{xmlel, <<"no-store">>, _attrs, _els}) ->
{hint, 'no-store'}.

View File

@ -2536,7 +2536,8 @@
#attr{name = <<"nick">>,
label = '$nick'}]}).
-record(hint, {type :: 'no-copy' | 'no-store' | 'store' | 'no-permanent-store'}).
-record(hint, {type :: 'no-copy' | 'no-store' | 'no-storage' |
'store' | 'no-permanent-store'}).
-type hint() :: #hint{}.
-xml(hint_no_copy,
@ -2549,6 +2550,11 @@
xmlns = <<"urn:xmpp:hints">>,
result = {hint, 'no-store'}}).
-xml(hint_no_storage,
#elem{name = <<"no-storage">>,
xmlns = <<"urn:xmpp:hints">>,
result = {hint, 'no-storage'}}).
-xml(hint_store,
#elem{name = <<"store">>,
xmlns = <<"urn:xmpp:hints">>,
@ -2621,6 +2627,56 @@
#ref{name = xdata, min = 0, max = 1,
label = '$xdata'}]}).
-xml(xevent_offline,
#elem{name = <<"offline">>,
xmlns = <<"jabber:x:event">>,
result = true}).
-xml(xevent_delivered,
#elem{name = <<"delivered">>,
xmlns = <<"jabber:x:event">>,
result = true}).
-xml(xevent_displayed,
#elem{name = <<"displayed">>,
xmlns = <<"jabber:x:event">>,
result = true}).
-xml(xevent_composing,
#elem{name = <<"composing">>,
xmlns = <<"jabber:x:event">>,
result = true}).
-xml(xevent_id,
#elem{name = <<"id">>,
xmlns = <<"jabber:x:event">>,
cdata = #cdata{},
result = '$cdata'}).
-xml(xevent,
#elem{name = <<"x">>,
xmlns = <<"jabber:x:event">>,
result = {xevent, '$offline', '$delivered', '$displayed',
'$composing', '$id'},
refs = [#ref{name = xevent_offline, min = 0, max = 1,
label = '$offline', default = false},
#ref{name = xevent_delivered, min = 0, max = 1,
label = '$delivered', default = false},
#ref{name = xevent_displayed, min = 0, max = 1,
label = '$displayed', default = false},
#ref{name = xevent_composing, min = 0, max = 1,
label = '$composing', default = false},
#ref{name = xevent_id, min = 0, max = 1,
label = '$id'}]}).
-xml(expire,
#elem{name = <<"x">>,
xmlns = <<"jabber:x:expire">>,
result = {expire, '$seconds', '$stored'},
attrs = [#attr{name = <<"seconds">>,
required = true,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"stored">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}}]}).
dec_tzo(Val) ->
[H1, M1] = str:tokens(Val, <<":">>),
H = jlib:binary_to_integer(H1),