Rewrite mod_offline to use XML generator
This commit is contained in:
parent
9a8e197d7e
commit
a4a9dd7f03
|
@ -11,7 +11,8 @@
|
||||||
-record(csi, {type :: active | inactive}).
|
-record(csi, {type :: active | inactive}).
|
||||||
-type csi() :: #csi{}.
|
-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{}.
|
-type hint() :: #hint{}.
|
||||||
|
|
||||||
-record(feature_register, {}).
|
-record(feature_register, {}).
|
||||||
|
@ -50,6 +51,10 @@
|
||||||
-record(carbons_private, {}).
|
-record(carbons_private, {}).
|
||||||
-type carbons_private() :: #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(),
|
-record(pubsub_unsubscribe, {node :: binary(),
|
||||||
jid :: any(),
|
jid :: any(),
|
||||||
subid :: binary()}).
|
subid :: binary()}).
|
||||||
|
@ -304,6 +309,13 @@
|
||||||
-record(sasl_abort, {}).
|
-record(sasl_abort, {}).
|
||||||
-type sasl_abort() :: #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(),
|
-record(vcard_email, {home = false :: boolean(),
|
||||||
work = false :: boolean(),
|
work = false :: boolean(),
|
||||||
internet = false :: boolean(),
|
internet = false :: boolean(),
|
||||||
|
@ -717,6 +729,7 @@
|
||||||
starttls_proceed() |
|
starttls_proceed() |
|
||||||
sm_resumed() |
|
sm_resumed() |
|
||||||
forwarded() |
|
forwarded() |
|
||||||
|
xevent() |
|
||||||
privacy_list() |
|
privacy_list() |
|
||||||
text() |
|
text() |
|
||||||
vcard_org() |
|
vcard_org() |
|
||||||
|
@ -735,12 +748,12 @@
|
||||||
pubsub_options() |
|
pubsub_options() |
|
||||||
compress() |
|
compress() |
|
||||||
bytestreams() |
|
bytestreams() |
|
||||||
|
muc_history() |
|
||||||
identity() |
|
identity() |
|
||||||
feature_csi() |
|
feature_csi() |
|
||||||
muc_user_destroy() |
|
muc_user_destroy() |
|
||||||
privacy_query() |
|
privacy_query() |
|
||||||
delay() |
|
delay() |
|
||||||
muc_history() |
|
|
||||||
vcard_tel() |
|
vcard_tel() |
|
||||||
vcard_logo() |
|
vcard_logo() |
|
||||||
disco_info() |
|
disco_info() |
|
||||||
|
@ -780,6 +793,7 @@
|
||||||
vcard_name() |
|
vcard_name() |
|
||||||
sm_resume() |
|
sm_resume() |
|
||||||
carbons_enable() |
|
carbons_enable() |
|
||||||
|
expire() |
|
||||||
pubsub_unsubscribe() |
|
pubsub_unsubscribe() |
|
||||||
muc_decline() |
|
muc_decline() |
|
||||||
chatstate() |
|
chatstate() |
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
get_sm_identity/5,
|
get_sm_identity/5,
|
||||||
get_sm_items/5,
|
get_sm_items/5,
|
||||||
get_info/5,
|
get_info/5,
|
||||||
handle_offline_query/3,
|
handle_offline_query/1,
|
||||||
remove_expired_messages/1,
|
remove_expired_messages/1,
|
||||||
remove_old_messages/2,
|
remove_old_messages/2,
|
||||||
remove_user/2,
|
remove_user/2,
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-include("ejabberd_http.hrl").
|
-include("ejabberd_http.hrl").
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ receive_all(US, Msgs, DBType) ->
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
|
get_sm_features(Acc, _From, _To, undefined, _Lang) ->
|
||||||
Feats = case Acc of
|
Feats = case Acc of
|
||||||
{result, I} -> I;
|
{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) ->
|
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
||||||
Acc.
|
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) ->
|
?NS_FLEX_OFFLINE, _Lang) ->
|
||||||
Identity = #xmlel{name = <<"identity">>,
|
[#identity{category = <<"automation">>,
|
||||||
attrs = [{<<"category">>, <<"automation">>},
|
type = <<"message-list">>}|Acc];
|
||||||
{<<"type">>, <<"message-list">>}]},
|
|
||||||
[Identity];
|
|
||||||
get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
|
get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
|
@ -282,15 +280,16 @@ get_sm_items(_Acc, #jid{luser = U, lserver = S, lresource = R} = JID,
|
||||||
?NS_FLEX_OFFLINE, _Lang) ->
|
?NS_FLEX_OFFLINE, _Lang) ->
|
||||||
case ejabberd_sm:get_session_pid(U, S, R) of
|
case ejabberd_sm:get_session_pid(U, S, R) of
|
||||||
Pid when is_pid(Pid) ->
|
Pid when is_pid(Pid) ->
|
||||||
Hdrs = read_message_headers(U, S),
|
Mod = gen_mod:db_mod(S, ?MODULE),
|
||||||
BareJID = jid:to_string(jid:remove_resource(JID)),
|
Hdrs = Mod:read_message_headers(U, S),
|
||||||
|
BareJID = jid:remove_resource(JID),
|
||||||
Pid ! dont_ask_offline,
|
Pid ! dont_ask_offline,
|
||||||
{result, lists:map(
|
{result, lists:map(
|
||||||
fun({Node, From, _To, _El}) ->
|
fun({Seq, From, _To, _El}) ->
|
||||||
#xmlel{name = <<"item">>,
|
Node = integer_to_binary(Seq),
|
||||||
attrs = [{<<"jid">>, BareJID},
|
#disco_item{jid = BareJID,
|
||||||
{<<"node">>, Node},
|
node = Node,
|
||||||
{<<"name">>, jid:to_string(From)}]}
|
name = jid:to_string(From)}
|
||||||
end, Hdrs)};
|
end, Hdrs)};
|
||||||
none ->
|
none ->
|
||||||
{result, []}
|
{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) ->
|
get_sm_items(Acc, _From, _To, _Node, _Lang) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
|
-spec get_info([xdata()], jid(), jid(),
|
||||||
|
undefined | binary(), undefined | binary()) -> [xdata()].
|
||||||
get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
|
get_info(_Acc, #jid{luser = U, lserver = S, lresource = R},
|
||||||
#jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) ->
|
#jid{luser = U, lserver = S}, ?NS_FLEX_OFFLINE, _Lang) ->
|
||||||
N = jlib:integer_to_binary(count_offline_messages(U, S)),
|
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 ->
|
none ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
[#xmlel{name = <<"x">>,
|
[#xdata{type = result,
|
||||||
attrs = [{<<"xmlns">>, ?NS_XDATA},
|
fields = [#xdata_field{var = <<"FORM_TYPE">>,
|
||||||
{<<"type">>, <<"result">>}],
|
type = hidden,
|
||||||
children = [#xmlel{name = <<"field">>,
|
values = [?NS_FLEX_OFFLINE]},
|
||||||
attrs = [{<<"var">>, <<"FORM_TYPE">>},
|
#xdata_field{var = <<"number_of_messages">>,
|
||||||
{<<"type">>, <<"hidden">>}],
|
values = [N]}]}];
|
||||||
children = [#xmlel{name = <<"value">>,
|
|
||||||
children = [{xmlcdata,
|
|
||||||
?NS_FLEX_OFFLINE}]}]},
|
|
||||||
#xmlel{name = <<"field">>,
|
|
||||||
attrs = [{<<"var">>, <<"number_of_messages">>}],
|
|
||||||
children = [#xmlel{name = <<"value">>,
|
|
||||||
children = [{xmlcdata, N}]}]}]}];
|
|
||||||
get_info(Acc, _From, _To, _Node, _Lang) ->
|
get_info(Acc, _From, _To, _Node, _Lang) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
handle_offline_query(#jid{luser = U, lserver = S} = From,
|
-spec handle_offline_query(iq()) -> iq().
|
||||||
#jid{luser = U, lserver = S} = _To,
|
handle_offline_query(#iq{from = #jid{luser = U, lserver = S} = From,
|
||||||
#iq{type = Type, sub_el = SubEl} = IQ) ->
|
to = #jid{luser = U, lserver = S} = _To,
|
||||||
|
type = Type,
|
||||||
|
sub_els = [#offline{purge = Purge,
|
||||||
|
items = Items,
|
||||||
|
fetch = Fetch}]} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
get ->
|
get ->
|
||||||
case fxml:get_subtag(SubEl, <<"fetch">>) of
|
if Fetch -> handle_offline_fetch(From);
|
||||||
#xmlel{} ->
|
true -> handle_offline_items_view(From, Items)
|
||||||
handle_offline_fetch(From);
|
|
||||||
false ->
|
|
||||||
handle_offline_items_view(From, SubEl)
|
|
||||||
end;
|
end;
|
||||||
set ->
|
set ->
|
||||||
case fxml:get_subtag(SubEl, <<"purge">>) of
|
if Purge -> delete_all_msgs(U, S);
|
||||||
#xmlel{} ->
|
true -> handle_offline_items_remove(From, Items)
|
||||||
delete_all_msgs(U, S);
|
|
||||||
false ->
|
|
||||||
handle_offline_items_remove(From, SubEl)
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
IQ#iq{type = result, sub_el = []};
|
xmpp:make_iq_result(IQ);
|
||||||
handle_offline_query(_From, _To, #iq{sub_el = SubEl, lang = Lang} = IQ) ->
|
handle_offline_query(#iq{lang = Lang} = IQ) ->
|
||||||
Txt = <<"Query to another users is forbidden">>,
|
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),
|
{U, S, R} = jid:tolower(JID),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Node) ->
|
fun(#offline_item{node = Node, action = view}) ->
|
||||||
case fetch_msg_by_node(JID, Node) of
|
case fetch_msg_by_node(JID, Node) of
|
||||||
{ok, OfflineMsg} ->
|
{ok, OfflineMsg} ->
|
||||||
case offline_msg_to_route(S, OfflineMsg) of
|
case offline_msg_to_route(S, OfflineMsg) of
|
||||||
|
@ -367,40 +360,25 @@ handle_offline_items_view(JID, #xmlel{children = Items}) ->
|
||||||
end;
|
end;
|
||||||
error ->
|
error ->
|
||||||
ok
|
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;
|
end;
|
||||||
(_) ->
|
(_) ->
|
||||||
[]
|
ok
|
||||||
end, Items).
|
end, Items).
|
||||||
|
|
||||||
set_offline_tag(#xmlel{children = Els} = El, Node) ->
|
-spec handle_offline_items_remove(jid(), [offline_item()]) -> ok.
|
||||||
OfflineEl = #xmlel{name = <<"offline">>,
|
handle_offline_items_remove(JID, Items) ->
|
||||||
attrs = [{<<"xmlns">>, ?NS_FLEX_OFFLINE}],
|
lists:foreach(
|
||||||
children = [#xmlel{name = <<"item">>,
|
fun(#offline_item{node = Node, action = remove}) ->
|
||||||
attrs = [{<<"node">>, Node}]}]},
|
remove_msg_by_node(JID, Node);
|
||||||
El#xmlel{children = [OfflineEl|Els]}.
|
(_) ->
|
||||||
|
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}) ->
|
handle_offline_fetch(#jid{luser = U, lserver = S, lresource = R}) ->
|
||||||
case ejabberd_sm:get_session_pid(U, S, R) of
|
case ejabberd_sm:get_session_pid(U, S, R) of
|
||||||
none ->
|
none ->
|
||||||
|
@ -414,6 +392,7 @@ handle_offline_fetch(#jid{luser = U, lserver = S, lresource = R}) ->
|
||||||
end, read_message_headers(U, S))
|
end, read_message_headers(U, S))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec fetch_msg_by_node(jid(), binary()) -> error | {ok, #offline_msg{}}.
|
||||||
fetch_msg_by_node(To, Seq) ->
|
fetch_msg_by_node(To, Seq) ->
|
||||||
case catch binary_to_integer(Seq) of
|
case catch binary_to_integer(Seq) of
|
||||||
I when is_integer(I), I >= 0 ->
|
I when is_integer(I), I >= 0 ->
|
||||||
|
@ -425,6 +404,7 @@ fetch_msg_by_node(To, Seq) ->
|
||||||
error
|
error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec remove_msg_by_node(jid(), binary()) -> ok.
|
||||||
remove_msg_by_node(To, Seq) ->
|
remove_msg_by_node(To, Seq) ->
|
||||||
case catch binary_to_integer(Seq) of
|
case catch binary_to_integer(Seq) of
|
||||||
I when is_integer(I), I>= 0 ->
|
I when is_integer(I), I>= 0 ->
|
||||||
|
@ -436,39 +416,38 @@ remove_msg_by_node(To, Seq) ->
|
||||||
ok
|
ok
|
||||||
end.
|
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) ->
|
need_to_store(LServer, Packet) ->
|
||||||
Type = fxml:get_tag_attr_s(<<"type">>, Packet),
|
case xmpp:has_subtag(Packet, #offline{}) of
|
||||||
if (Type /= <<"error">>) and (Type /= <<"groupchat">>)
|
false ->
|
||||||
and (Type /= <<"headline">>) ->
|
case check_store_hint(Packet) of
|
||||||
case has_offline_tag(Packet) of
|
store ->
|
||||||
false ->
|
true;
|
||||||
case check_store_hint(Packet) of
|
no_store ->
|
||||||
store ->
|
false;
|
||||||
true;
|
none ->
|
||||||
no_store ->
|
case gen_mod:get_module_opt(
|
||||||
false;
|
LServer, ?MODULE, store_empty_body,
|
||||||
none ->
|
fun(V) when is_boolean(V) -> V;
|
||||||
case gen_mod:get_module_opt(
|
(unless_chat_state) -> unless_chat_state
|
||||||
LServer, ?MODULE, store_empty_body,
|
end,
|
||||||
fun(V) when is_boolean(V) -> V;
|
unless_chat_state) of
|
||||||
(unless_chat_state) -> unless_chat_state
|
false ->
|
||||||
end,
|
Packet#message.body /= [];
|
||||||
unless_chat_state) of
|
unless_chat_state ->
|
||||||
false ->
|
not xmpp_util:is_standalone_chat_state(Packet);
|
||||||
fxml:get_subtag(Packet, <<"body">>) /= false;
|
true ->
|
||||||
unless_chat_state ->
|
true
|
||||||
not jlib:is_standalone_chat_state(Packet);
|
end
|
||||||
true ->
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
true ->
|
|
||||||
false
|
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec store_packet(jid(), jid(), message()) -> ok | stop.
|
||||||
store_packet(From, To, Packet) ->
|
store_packet(From, To, Packet) ->
|
||||||
case need_to_store(To#jid.lserver, Packet) of
|
case need_to_store(To#jid.lserver, Packet) of
|
||||||
true ->
|
true ->
|
||||||
|
@ -476,18 +455,19 @@ store_packet(From, To, Packet) ->
|
||||||
true ->
|
true ->
|
||||||
#jid{luser = LUser, lserver = LServer} = To,
|
#jid{luser = LUser, lserver = LServer} = To,
|
||||||
TimeStamp = p1_time_compat:timestamp(),
|
TimeStamp = p1_time_compat:timestamp(),
|
||||||
#xmlel{children = Els} = Packet,
|
Expire = find_x_expire(TimeStamp, Packet),
|
||||||
Expire = find_x_expire(TimeStamp, Els),
|
El = xmpp:encode(Packet),
|
||||||
gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
|
gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
|
||||||
#offline_msg{us = {LUser, LServer},
|
#offline_msg{us = {LUser, LServer},
|
||||||
timestamp = TimeStamp, expire = Expire,
|
timestamp = TimeStamp, expire = Expire,
|
||||||
from = From, to = To, packet = Packet},
|
from = From, to = To, packet = El},
|
||||||
stop;
|
stop;
|
||||||
_ -> ok
|
_ -> ok
|
||||||
end;
|
end;
|
||||||
false -> ok
|
false -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec check_store_hint(message()) -> store | no_store | none.
|
||||||
check_store_hint(Packet) ->
|
check_store_hint(Packet) ->
|
||||||
case has_store_hint(Packet) of
|
case has_store_hint(Packet) of
|
||||||
true ->
|
true ->
|
||||||
|
@ -501,89 +481,43 @@ check_store_hint(Packet) ->
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec has_store_hint(message()) -> boolean().
|
||||||
has_store_hint(Packet) ->
|
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) ->
|
has_no_store_hint(Packet) ->
|
||||||
fxml:get_subtag_with_xmlns(Packet, <<"no-store">>, ?NS_HINTS) =/= false
|
xmpp:has_subtag(Packet, #hint{type = 'no-store'})
|
||||||
orelse
|
orelse
|
||||||
fxml:get_subtag_with_xmlns(Packet, <<"no-storage">>, ?NS_HINTS) =/= false.
|
xmpp:has_subtag(Packet, #hint{type = 'no-storage'}).
|
||||||
|
|
||||||
has_offline_tag(Packet) ->
|
|
||||||
fxml:get_subtag_with_xmlns(Packet, <<"offline">>, ?NS_FLEX_OFFLINE) =/= false.
|
|
||||||
|
|
||||||
%% Check if the packet has any content about XEP-0022
|
%% Check if the packet has any content about XEP-0022
|
||||||
check_event(From, To, Packet) ->
|
-spec check_event(jid(), jid(), message()) -> boolean().
|
||||||
#xmlel{name = Name, attrs = Attrs, children = Els} =
|
check_event(From, To, #message{id = ID} = Msg) ->
|
||||||
Packet,
|
case xmpp:get_subtag(Msg, #xevent{}) of
|
||||||
case find_x_event(Els) of
|
false ->
|
||||||
false -> true;
|
true;
|
||||||
El ->
|
#xevent{id = undefined, offline = false} ->
|
||||||
case fxml:get_subtag(El, <<"id">>) of
|
true;
|
||||||
false ->
|
#xevent{id = undefined, offline = true} ->
|
||||||
case fxml:get_subtag(El, <<"offline">>) of
|
NewMsg = Msg#message{sub_els = [#xevent{id = ID, offline = true}]},
|
||||||
false -> true;
|
ejabberd_router:route(To, From, xmpp:set_from_to(NewMsg, To, From)),
|
||||||
_ ->
|
true;
|
||||||
ID = case fxml:get_tag_attr_s(<<"id">>, Packet) of
|
_ ->
|
||||||
<<"">> ->
|
false
|
||||||
#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.
|
end.
|
||||||
|
|
||||||
%% Check if the packet has subelements about XEP-0022
|
-spec find_x_expire(erlang:timestamp(), message()) -> erlang:timestamp() | never.
|
||||||
find_x_event([]) -> false;
|
find_x_expire(TimeStamp, Msg) ->
|
||||||
find_x_event([{xmlcdata, _} | Els]) ->
|
case xmpp:get_subtag(Msg, #expire{}) of
|
||||||
find_x_event(Els);
|
#expire{seconds = Int} ->
|
||||||
find_x_event([El | Els]) ->
|
{MegaSecs, Secs, MicroSecs} = TimeStamp,
|
||||||
case fxml:get_tag_attr_s(<<"xmlns">>, El) of
|
S = MegaSecs * 1000000 + Secs + Int,
|
||||||
?NS_EVENT -> El;
|
MegaSecs1 = S div 1000000,
|
||||||
_ -> find_x_event(Els)
|
Secs1 = S rem 1000000,
|
||||||
end.
|
{MegaSecs1, Secs1, MicroSecs};
|
||||||
|
false ->
|
||||||
find_x_expire(_, []) -> never;
|
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)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
resend_offline_messages(User, Server) ->
|
resend_offline_messages(User, Server) ->
|
||||||
|
@ -612,10 +546,9 @@ pop_offline_messages(Ls, User, Server) ->
|
||||||
end,
|
end,
|
||||||
lists:filter(
|
lists:filter(
|
||||||
fun(#offline_msg{packet = Pkt} = R) ->
|
fun(#offline_msg{packet = Pkt} = R) ->
|
||||||
#xmlel{children = Els} = Pkt,
|
|
||||||
Expire = case R#offline_msg.expire of
|
Expire = case R#offline_msg.expire of
|
||||||
undefined ->
|
undefined ->
|
||||||
find_x_expire(TS, Els);
|
find_x_expire(TS, Pkt);
|
||||||
Exp ->
|
Exp ->
|
||||||
Exp
|
Exp
|
||||||
end,
|
end,
|
||||||
|
@ -648,17 +581,15 @@ remove_user(User, Server) ->
|
||||||
|
|
||||||
%% 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(fun (#offline_msg{from = From, to = To,
|
lists:foreach(
|
||||||
packet = Packet}) ->
|
fun(#offline_msg{from = From, to = To, packet = Packet}) ->
|
||||||
ErrText = <<"Your contact offline message queue is "
|
ErrText = <<"Your contact offline message queue is "
|
||||||
"full. The message has been discarded.">>,
|
"full. The message has been discarded.">>,
|
||||||
Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
|
Lang = xmpp:get_lang(Packet),
|
||||||
Err = jlib:make_error_reply(Packet,
|
Err = xmpp:make_error(
|
||||||
?ERRT_RESOURCE_CONSTRAINT(Lang,
|
Packet, xmpp:err_resource_constraint(ErrText, Lang)),
|
||||||
ErrText)),
|
ejabberd_router:route(To, From, Err)
|
||||||
ejabberd_router:route(To, From, Err)
|
end, Msgs).
|
||||||
end,
|
|
||||||
Msgs).
|
|
||||||
|
|
||||||
webadmin_page(_, Host,
|
webadmin_page(_, Host,
|
||||||
#request{us = _US, path = [<<"user">>, U, <<"queue">>],
|
#request{us = _US, path = [<<"user">>, U, <<"queue">>],
|
||||||
|
@ -668,29 +599,30 @@ webadmin_page(_, Host,
|
||||||
webadmin_page(Acc, _, _) -> Acc.
|
webadmin_page(Acc, _, _) -> Acc.
|
||||||
|
|
||||||
get_offline_els(LUser, LServer) ->
|
get_offline_els(LUser, LServer) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Hdrs = read_message_headers(LUser, LServer),
|
||||||
Hdrs = Mod:read_message_headers(LUser, LServer),
|
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({_Seq, From, To, Packet}) ->
|
fun({_Seq, From, To, Packet}) ->
|
||||||
jlib:replace_from_to(From, To, Packet)
|
xmpp:set_from_to(Packet, From, To)
|
||||||
end, Hdrs).
|
end, Hdrs).
|
||||||
|
|
||||||
offline_msg_to_route(LServer, #offline_msg{} = R) ->
|
offline_msg_to_route(LServer, #offline_msg{} = R) ->
|
||||||
El = case R#offline_msg.timestamp of
|
Pkt = xmpp:decode(R#offline_msg.packet, [ignore_els]),
|
||||||
undefined ->
|
Pkt1 = case R#offline_msg.timestamp of
|
||||||
R#offline_msg.packet;
|
undefined ->
|
||||||
TS ->
|
Pkt;
|
||||||
jlib:add_delay_info(R#offline_msg.packet, LServer, TS,
|
TS ->
|
||||||
<<"Offline Storage">>)
|
xmpp_util:add_delay_info(Pkt, LServer, TS,
|
||||||
end,
|
<<"Offline Storage">>)
|
||||||
{route, R#offline_msg.from, R#offline_msg.to, El}.
|
end,
|
||||||
|
{route, R#offline_msg.from, R#offline_msg.to, Pkt1}.
|
||||||
|
|
||||||
read_message_headers(LUser, LServer) ->
|
read_message_headers(LUser, LServer) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({Seq, From, To, El}) ->
|
fun({Seq, From, To, El}) ->
|
||||||
Node = integer_to_binary(Seq),
|
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)).
|
end, Mod:read_message_headers(LUser, LServer)).
|
||||||
|
|
||||||
format_user_queue(Hdrs) ->
|
format_user_queue(Hdrs) ->
|
||||||
|
@ -826,6 +758,7 @@ webadmin_user(Acc, User, Server, Lang) ->
|
||||||
?INPUTT(<<"submit">>, <<"removealloffline">>,
|
?INPUTT(<<"submit">>, <<"removealloffline">>,
|
||||||
<<"Remove All Offline Messages">>)].
|
<<"Remove All Offline Messages">>)].
|
||||||
|
|
||||||
|
-spec delete_all_msgs(binary(), binary()) -> {atomic, any()}.
|
||||||
delete_all_msgs(User, Server) ->
|
delete_all_msgs(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
|
@ -849,6 +782,7 @@ webadmin_user_parse_query(Acc, _Action, _User, _Server,
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
%% Returns as integer the number of offline messages for a given user
|
%% 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) ->
|
count_offline_messages(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
|
|
|
@ -156,10 +156,12 @@ get_error(#iq{error = E}) -> E;
|
||||||
get_error(#message{error = E}) -> E;
|
get_error(#message{error = E}) -> E;
|
||||||
get_error(#presence{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(#iq{sub_els = Els}) -> Els;
|
||||||
get_els(#message{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();
|
-spec set_id(iq(), binary()) -> iq();
|
||||||
(message(), binary()) -> message();
|
(message(), binary()) -> message();
|
||||||
|
|
|
@ -15,6 +15,24 @@ decode(_el) -> decode(_el, []).
|
||||||
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
|
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
|
||||||
IgnoreEls = proplists:get_bool(ignore_els, Opts),
|
IgnoreEls = proplists:get_bool(ignore_els, Opts),
|
||||||
case {_name, get_attr(<<"xmlns">>, _attrs)} of
|
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">>} ->
|
{<<"query">>, <<"jabber:iq:search">>} ->
|
||||||
decode_search(<<"jabber:iq:search">>, IgnoreEls, _el);
|
decode_search(<<"jabber:iq:search">>, IgnoreEls, _el);
|
||||||
{<<"item">>, <<"jabber:iq:search">>} ->
|
{<<"item">>, <<"jabber:iq:search">>} ->
|
||||||
|
@ -40,6 +58,9 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
|
||||||
IgnoreEls, _el);
|
IgnoreEls, _el);
|
||||||
{<<"store">>, <<"urn:xmpp:hints">>} ->
|
{<<"store">>, <<"urn:xmpp:hints">>} ->
|
||||||
decode_hint_store(<<"urn:xmpp:hints">>, IgnoreEls, _el);
|
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">>} ->
|
{<<"no-store">>, <<"urn:xmpp:hints">>} ->
|
||||||
decode_hint_no_store(<<"urn:xmpp:hints">>, IgnoreEls,
|
decode_hint_no_store(<<"urn:xmpp:hints">>, IgnoreEls,
|
||||||
_el);
|
_el);
|
||||||
|
@ -1162,6 +1183,13 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
|
||||||
|
|
||||||
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
|
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
|
||||||
case {_name, get_attr(<<"xmlns">>, _attrs)} of
|
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;
|
{<<"query">>, <<"jabber:iq:search">>} -> true;
|
||||||
{<<"item">>, <<"jabber:iq:search">>} -> true;
|
{<<"item">>, <<"jabber:iq:search">>} -> true;
|
||||||
{<<"email">>, <<"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">>} ->
|
{<<"no-permanent-store">>, <<"urn:xmpp:hints">>} ->
|
||||||
true;
|
true;
|
||||||
{<<"store">>, <<"urn:xmpp:hints">>} -> true;
|
{<<"store">>, <<"urn:xmpp:hints">>} -> true;
|
||||||
|
{<<"no-storage">>, <<"urn:xmpp:hints">>} -> true;
|
||||||
{<<"no-store">>, <<"urn:xmpp:hints">>} -> true;
|
{<<"no-store">>, <<"urn:xmpp:hints">>} -> true;
|
||||||
{<<"no-copy">>, <<"urn:xmpp:hints">>} -> true;
|
{<<"no-copy">>, <<"urn:xmpp:hints">>} -> true;
|
||||||
{<<"participant">>, <<"urn:xmpp:mix:0">>} -> 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) ->
|
||||||
encode_hint_no_store(No_store,
|
encode_hint_no_store(No_store,
|
||||||
[{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
|
[{<<"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) ->
|
||||||
encode_hint_store(Store,
|
encode_hint_store(Store,
|
||||||
[{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
|
[{<<"xmlns">>, <<"urn:xmpp:hints">>}]);
|
||||||
|
@ -2301,7 +2333,12 @@ encode({search_item, _, _, _, _, _} = Item) ->
|
||||||
[{<<"xmlns">>, <<"jabber:iq:search">>}]);
|
[{<<"xmlns">>, <<"jabber:iq:search">>}]);
|
||||||
encode({search, _, _, _, _, _, _, _} = Query) ->
|
encode({search, _, _, _, _, _, _, _} = Query) ->
|
||||||
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({last, _, _}) -> <<"query">>;
|
||||||
get_name({version, _, _, _}) -> <<"query">>;
|
get_name({version, _, _, _}) -> <<"query">>;
|
||||||
|
@ -2460,11 +2497,14 @@ get_name({mix_leave}) -> <<"leave">>;
|
||||||
get_name({mix_participant, _, _}) -> <<"participant">>;
|
get_name({mix_participant, _, _}) -> <<"participant">>;
|
||||||
get_name({hint, 'no-copy'}) -> <<"no-copy">>;
|
get_name({hint, 'no-copy'}) -> <<"no-copy">>;
|
||||||
get_name({hint, 'no-store'}) -> <<"no-store">>;
|
get_name({hint, 'no-store'}) -> <<"no-store">>;
|
||||||
|
get_name({hint, 'no-storage'}) -> <<"no-storage">>;
|
||||||
get_name({hint, store}) -> <<"store">>;
|
get_name({hint, store}) -> <<"store">>;
|
||||||
get_name({hint, 'no-permanent-store'}) ->
|
get_name({hint, 'no-permanent-store'}) ->
|
||||||
<<"no-permanent-store">>;
|
<<"no-permanent-store">>;
|
||||||
get_name({search_item, _, _, _, _, _}) -> <<"item">>;
|
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({last, _, _}) -> <<"jabber:iq:last">>;
|
||||||
get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
|
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({mix_participant, _, _}) -> <<"urn:xmpp:mix:0">>;
|
||||||
get_ns({hint, 'no-copy'}) -> <<"urn:xmpp:hints">>;
|
get_ns({hint, 'no-copy'}) -> <<"urn:xmpp:hints">>;
|
||||||
get_ns({hint, 'no-store'}) -> <<"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, store}) -> <<"urn:xmpp:hints">>;
|
||||||
get_ns({hint, 'no-permanent-store'}) ->
|
get_ns({hint, 'no-permanent-store'}) ->
|
||||||
<<"urn:xmpp:hints">>;
|
<<"urn:xmpp:hints">>;
|
||||||
get_ns({search_item, _, _, _, _, _}) ->
|
get_ns({search_item, _, _, _, _, _}) ->
|
||||||
<<"jabber:iq:search">>;
|
<<"jabber:iq:search">>;
|
||||||
get_ns({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).
|
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_item, 5) -> [jid, first, last, nick, email];
|
||||||
pp(search, 7) ->
|
pp(search, 7) ->
|
||||||
[instructions, first, last, nick, email, items, xdata];
|
[instructions, first, last, nick, email, items, xdata];
|
||||||
|
pp(xevent, 5) ->
|
||||||
|
[offline, delivered, displayed, composing, id];
|
||||||
|
pp(expire, 2) -> [seconds, stored];
|
||||||
pp(_, _) -> no.
|
pp(_, _) -> no.
|
||||||
|
|
||||||
join([], _Sep) -> <<>>;
|
join([], _Sep) -> <<>>;
|
||||||
|
@ -2954,6 +3000,267 @@ dec_tzo(Val) ->
|
||||||
M = jlib:binary_to_integer(M1),
|
M = jlib:binary_to_integer(M1),
|
||||||
if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
|
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,
|
decode_search(__TopXMLNS, __IgnoreEls,
|
||||||
{xmlel, <<"query">>, _attrs, _els}) ->
|
{xmlel, <<"query">>, _attrs, _els}) ->
|
||||||
{Xdata, Items, Instructions, Last, First, Nick, Email} =
|
{Xdata, Items, Instructions, Last, First, Nick, Email} =
|
||||||
|
@ -3459,6 +3766,16 @@ encode_hint_store({hint, store}, _xmlns_attrs) ->
|
||||||
_attrs = _xmlns_attrs,
|
_attrs = _xmlns_attrs,
|
||||||
{xmlel, <<"store">>, _attrs, _els}.
|
{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,
|
decode_hint_no_store(__TopXMLNS, __IgnoreEls,
|
||||||
{xmlel, <<"no-store">>, _attrs, _els}) ->
|
{xmlel, <<"no-store">>, _attrs, _els}) ->
|
||||||
{hint, 'no-store'}.
|
{hint, 'no-store'}.
|
||||||
|
|
|
@ -2536,7 +2536,8 @@
|
||||||
#attr{name = <<"nick">>,
|
#attr{name = <<"nick">>,
|
||||||
label = '$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{}.
|
-type hint() :: #hint{}.
|
||||||
|
|
||||||
-xml(hint_no_copy,
|
-xml(hint_no_copy,
|
||||||
|
@ -2549,6 +2550,11 @@
|
||||||
xmlns = <<"urn:xmpp:hints">>,
|
xmlns = <<"urn:xmpp:hints">>,
|
||||||
result = {hint, 'no-store'}}).
|
result = {hint, 'no-store'}}).
|
||||||
|
|
||||||
|
-xml(hint_no_storage,
|
||||||
|
#elem{name = <<"no-storage">>,
|
||||||
|
xmlns = <<"urn:xmpp:hints">>,
|
||||||
|
result = {hint, 'no-storage'}}).
|
||||||
|
|
||||||
-xml(hint_store,
|
-xml(hint_store,
|
||||||
#elem{name = <<"store">>,
|
#elem{name = <<"store">>,
|
||||||
xmlns = <<"urn:xmpp:hints">>,
|
xmlns = <<"urn:xmpp:hints">>,
|
||||||
|
@ -2621,6 +2627,56 @@
|
||||||
#ref{name = xdata, min = 0, max = 1,
|
#ref{name = xdata, min = 0, max = 1,
|
||||||
label = '$xdata'}]}).
|
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) ->
|
dec_tzo(Val) ->
|
||||||
[H1, M1] = str:tokens(Val, <<":">>),
|
[H1, M1] = str:tokens(Val, <<":">>),
|
||||||
H = jlib:binary_to_integer(H1),
|
H = jlib:binary_to_integer(H1),
|
||||||
|
|
Loading…
Reference in New Issue