aboutsummaryrefslogtreecommitdiff
path: root/src/xmpp_util.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpp_util.erl')
-rw-r--r--src/xmpp_util.erl169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/xmpp_util.erl b/src/xmpp_util.erl
new file mode 100644
index 000000000..7b3e0e892
--- /dev/null
+++ b/src/xmpp_util.erl
@@ -0,0 +1,169 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 12 Jul 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-module(xmpp_util).
+
+%% API
+-export([add_delay_info/3, add_delay_info/4, unwrap_carbon/1,
+ is_standalone_chat_state/1, get_xdata_values/2,
+ has_xdata_var/2, make_adhoc_response/1, make_adhoc_response/2,
+ decode_timestamp/1, encode_timestamp/1]).
+
+-include("xmpp.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+-spec add_delay_info(stanza(), jid(), erlang:timestamp()) -> stanza().
+add_delay_info(Stz, From, Time) ->
+ add_delay_info(Stz, From, Time, <<"">>).
+
+-spec add_delay_info(stanza(), jid(),
+ erlang:timestamp(), binary()) -> stanza().
+
+add_delay_info(Stz, From, Time, Desc) ->
+ case xmpp:get_subtag(Stz, #delay{}) of
+ #delay{from = OldFrom, desc = OldDesc} = Delay ->
+ case jid:tolower(From) == jid:tolower(OldFrom) of
+ true when Desc == <<"">> ->
+ Stz;
+ true when OldDesc == <<"">> ->
+ xmpp:set_subtag(Stz, Delay#delay{desc = Desc});
+ true ->
+ case binary:match(OldDesc, Desc) of
+ nomatch ->
+ NewDesc = <<OldDesc/binary, ", ", Desc/binary>>,
+ xmpp:set_subtag(Stz, Delay#delay{desc = NewDesc});
+ _ ->
+ Stz
+ end;
+ false ->
+ NewDelay = #delay{stamp = Time, from = From, desc = Desc},
+ xmpp:set_subtag(Stz, NewDelay)
+ end;
+ false ->
+ Delay = #delay{stamp = Time, from = From, desc = Desc},
+ xmpp:set_subtag(Stz, Delay)
+ end.
+
+-spec unwrap_carbon(stanza()) -> xmpp_element().
+unwrap_carbon(#message{} = Msg) ->
+ case xmpp:get_subtag(Msg, #carbons_sent{}) of
+ #carbons_sent{forwarded = #forwarded{sub_els = [El]}} ->
+ El;
+ _ ->
+ case xmpp:get_subtag(Msg, #carbons_received{}) of
+ #carbons_received{forwarded = #forwarded{sub_els = [El]}} ->
+ El;
+ _ ->
+ Msg
+ end
+ end;
+unwrap_carbon(Stanza) -> Stanza.
+
+-spec is_standalone_chat_state(stanza()) -> boolean().
+is_standalone_chat_state(Stanza) ->
+ case unwrap_carbon(Stanza) of
+ #message{body = [], subject = [], sub_els = Els} ->
+ IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY, ?NS_EVENT],
+ Stripped = [El || El <- Els,
+ not lists:member(xmpp:get_ns(El), IgnoreNS)],
+ Stripped == [];
+ _ ->
+ false
+ end.
+
+-spec get_xdata_values(binary(), xdata()) -> [binary()].
+get_xdata_values(Var, #xdata{fields = Fields}) ->
+ case lists:keyfind(Var, #xdata_field.var, Fields) of
+ #xdata_field{values = Vals} -> Vals;
+ false -> []
+ end.
+
+-spec has_xdata_var(binary(), xdata()) -> boolean().
+has_xdata_var(Var, #xdata{fields = Fields}) ->
+ lists:keymember(Var, #xdata_field.var, Fields).
+
+-spec make_adhoc_response(adhoc_command(), adhoc_command()) -> adhoc_command().
+make_adhoc_response(#adhoc_command{lang = Lang, node = Node, sid = SID},
+ Command) ->
+ make_adhoc_response(
+ Command#adhoc_command{lang = Lang, node = Node, sid = SID}).
+
+-spec make_adhoc_response(adhoc_command()) -> adhoc_command().
+make_adhoc_response(#adhoc_command{sid = <<"">>,
+ status = Status,
+ actions = Actions} = Command) ->
+ SID = encode_timestamp(p1_time_compat:timestamp()),
+ NewActions = if Actions == undefined, Status /= completed ->
+ #adhoc_actions{execute = complete, complete = true};
+ true ->
+ undefined
+ end,
+ Command#adhoc_command{sid = SID, actions = NewActions};
+make_adhoc_response(Command) ->
+ Command.
+
+-spec decode_timestamp(binary()) -> erlang:timestamp().
+decode_timestamp(S) ->
+ try try_decode_timestamp(S)
+ catch _:_ -> erlang:error({bad_timestamp, S})
+ end.
+
+-spec encode_timestamp(erlang:timestamp()) -> binary().
+encode_timestamp({MegaSecs, Secs, MicroSecs}) ->
+ {{Year, Month, Day}, {Hour, Minute, Second}} =
+ calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}),
+ Fraction = if MicroSecs > 0 ->
+ io_lib:format(".~6..0B", [MicroSecs]);
+ true ->
+ ""
+ end,
+ list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT"
+ "~2..0B:~2..0B:~2..0B~sZ",
+ [Year, Month, Day, Hour, Minute, Second,
+ Fraction])).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+try_decode_timestamp(<<Y:4/binary, $-, Mo:2/binary, $-, D:2/binary, $T,
+ H:2/binary, $:, Mi:2/binary, $:, S:2/binary, T/binary>>) ->
+ Date = {to_integer(Y, 1970, 9999), to_integer(Mo, 1, 12), to_integer(D, 1, 31)},
+ Time = {to_integer(H, 0, 23), to_integer(Mi, 0, 59), to_integer(S, 0, 59)},
+ {MS, {TZH, TZM}} = try_decode_fraction(T),
+ Seconds = calendar:datetime_to_gregorian_seconds({Date, Time}) -
+ calendar:datetime_to_gregorian_seconds({{1970,1,1}, {0,0,0}}) -
+ TZH * 60 * 60 - TZM * 60,
+ {Seconds div 1000000, Seconds rem 1000000, MS};
+try_decode_timestamp(<<Y:4/binary, Mo:2/binary, D:2/binary, $T,
+ H:2/binary, $:, Mi:2/binary, $:, S:2/binary>>) ->
+ try_decode_timestamp(<<Y:4/binary, $-, Mo:2/binary, $-, D:2/binary, $T,
+ H:2/binary, $:, Mi:2/binary, $:, S:2/binary, $Z>>).
+
+try_decode_fraction(<<$., T/binary>>) ->
+ {match, [V]} = re:run(T, <<"^[0-9]+">>, [{capture, [0], binary}]),
+ Size = size(V),
+ <<V:Size/binary, TZD/binary>> = T,
+ {to_integer(binary:part(V, 0, min(6, Size)), 0, 999999),
+ try_decode_tzd(TZD)};
+try_decode_fraction(TZD) ->
+ {0, try_decode_tzd(TZD)}.
+
+try_decode_tzd(<<$Z>>) ->
+ {0, 0};
+try_decode_tzd(<<$-, H:2/binary, $:, M:2/binary>>) ->
+ {-1 * to_integer(H, 0, 12), to_integer(M, 0, 59)};
+try_decode_tzd(<<$+, H:2/binary, $:, M:2/binary>>) ->
+ {to_integer(H, 0, 12), to_integer(M, 0, 59)}.
+
+to_integer(S, Min, Max) ->
+ case binary_to_integer(S) of
+ I when I >= Min, I =< Max ->
+ I
+ end.