-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, {}).
-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()}).
-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(),
starttls_proceed() |
sm_resumed() |
forwarded() |
+ xevent() |
privacy_list() |
text() |
vcard_org() |
pubsub_options() |
compress() |
bytestreams() |
+ muc_history() |
identity() |
feature_csi() |
muc_user_destroy() |
privacy_query() |
delay() |
- muc_history() |
vcard_tel() |
vcard_logo() |
disco_info() |
vcard_name() |
sm_resume() |
carbons_enable() |
+ expire() |
pubsub_unsubscribe() |
muc_decline() |
chatstate() |
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,
-include("ejabberd.hrl").
-include("logger.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("ejabberd_http.hrl").
end
end.
-get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
+get_sm_features(Acc, _From, _To, undefined, _Lang) ->
Feats = case Acc of
{result, I} -> I;
_ -> []
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.
?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, []}
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)),
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
end;
error ->
ok
- end
- end, get_nodes_from_items(Items, <<"view">>)).
+ end;
+ (_) ->
+ ok
+ end, Items).
-handle_offline_items_remove(JID, #xmlel{children = Items}) ->
+-spec handle_offline_items_remove(jid(), [offline_item()]) -> ok.
+handle_offline_items_remove(JID, 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;
+ fun(#offline_item{node = Node, action = remove}) ->
+ remove_msg_by_node(JID, Node);
(_) ->
- []
+ 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 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 ->
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 ->
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 ->
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 ->
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 ->
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
- 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)
+-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.
-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) ->
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,
%% 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">>],
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) ->
?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),
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),
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();
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">>} ->
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);
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;
{<<"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;
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">>}]);
[{<<"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">>;
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">>;
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).
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) -> <<>>;
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} =
_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'}.
#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,
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">>,
#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),