aboutsummaryrefslogtreecommitdiff
path: root/src/jlib.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/jlib.erl')
-rw-r--r--src/jlib.erl934
1 files changed, 0 insertions, 934 deletions
diff --git a/src/jlib.erl b/src/jlib.erl
deleted file mode 100644
index 4bc9b0055..000000000
--- a/src/jlib.erl
+++ /dev/null
@@ -1,934 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : jlib.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : General XMPP library.
-%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License along
-%%% with this program; if not, write to the Free Software Foundation, Inc.,
-%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-%%%
-%%%----------------------------------------------------------------------
-
--module(jlib).
-
--author('alexey@process-one.net').
-
--protocol({xep, 59, '1.0'}).
--protocol({xep, 82, '1.1'}).
--protocol({xep, 203, '2.0'}).
-
--compile({no_auto_import, [atom_to_binary/2,
- binary_to_integer/1,
- integer_to_binary/1]}).
-
--export([make_result_iq_reply/1, make_error_reply/3,
- make_error_reply/2, make_error_element/2,
- make_correct_from_to_attrs/3, replace_from_to_attrs/3,
- replace_from_to/3, replace_from_attrs/2, replace_from/2,
- remove_attr/2, tolower/1,
- get_iq_namespace/1, iq_query_info/1,
- iq_query_or_response_info/1, is_iq_request_type/1,
- iq_to_xml/1, parse_xdata_submit/1,
- unwrap_carbon/1, is_standalone_chat_state/1,
- add_delay_info/3, add_delay_info/4,
- timestamp_to_legacy/1, timestamp_to_iso_basic/1, timestamp_to_iso/2,
- now_to_utc_string/1, now_to_local_string/1,
- datetime_string_to_timestamp/1,
- term_to_base64/1, base64_to_term/1,
- decode_base64/1, encode_base64/1, ip_to_list/1,
- rsm_encode/1, rsm_encode/2, rsm_decode/1,
- binary_to_integer/1, binary_to_integer/2,
- integer_to_binary/1, integer_to_binary/2,
- atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
- l2i/1, i2l/1, i2l/2, queue_drop_while/2,
- expr_to_term/1, term_to_expr/1]).
-
-%% The following functions are deprecated and will be removed soon
-%% Use corresponding functions from jid.erl instead
--export([make_jid/3, make_jid/1, split_jid/1, string_to_jid/1,
- jid_to_string/1, is_nodename/1, nodeprep/1,
- nameprep/1, resourceprep/1, jid_tolower/1,
- jid_remove_resource/1, jid_replace_resource/2]).
-
--deprecated([{make_jid, '_'},
- {split_jid, 1},
- {string_to_jid, 1},
- {jid_to_string, 1},
- {is_nodename, 1},
- {nodeprep, 1},
- {nameprep, 1},
- {resourceprep, 1},
- {jid_tolower, 1},
- {jid_remove_resource, 1},
- {jid_replace_resource, 2}]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-%send_iq(From, To, ID, SubTags) ->
-% ok.
-
--spec make_result_iq_reply(xmlel()) -> xmlel().
-
-make_result_iq_reply(#xmlel{name = Name, attrs = Attrs,
- children = SubTags}) ->
- NewAttrs = make_result_iq_reply_attrs(Attrs),
- #xmlel{name = Name, attrs = NewAttrs,
- children = SubTags}.
-
--spec make_result_iq_reply_attrs([attr()]) -> [attr()].
-
-make_result_iq_reply_attrs(Attrs) ->
- To = fxml:get_attr(<<"to">>, Attrs),
- From = fxml:get_attr(<<"from">>, Attrs),
- Attrs1 = lists:keydelete(<<"to">>, 1, Attrs),
- Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1),
- Attrs3 = case To of
- {value, ToVal} -> [{<<"from">>, ToVal} | Attrs2];
- _ -> Attrs2
- end,
- Attrs4 = case From of
- {value, FromVal} -> [{<<"to">>, FromVal} | Attrs3];
- _ -> Attrs3
- end,
- Attrs5 = lists:keydelete(<<"type">>, 1, Attrs4),
- Attrs6 = [{<<"type">>, <<"result">>} | Attrs5],
- Attrs6.
-
--spec make_error_reply(xmlel(), binary(), binary()) -> xmlel().
-
-make_error_reply(#xmlel{name = Name, attrs = Attrs,
- children = SubTags},
- Code, Desc) ->
- NewAttrs = make_error_reply_attrs(Attrs),
- #xmlel{name = Name, attrs = NewAttrs,
- children =
- SubTags ++
- [#xmlel{name = <<"error">>,
- attrs = [{<<"code">>, Code}],
- children = [{xmlcdata, Desc}]}]}.
-
--spec make_error_reply(xmlel(), xmlel()) -> xmlel().
-
-make_error_reply(#xmlel{name = Name, attrs = Attrs,
- children = SubTags},
- Error) ->
- NewAttrs = make_error_reply_attrs(Attrs),
- #xmlel{name = Name, attrs = NewAttrs,
- children = SubTags ++ [Error]}.
-
--spec make_error_reply_attrs([attr()]) -> [attr()].
-
-make_error_reply_attrs(Attrs) ->
- To = fxml:get_attr(<<"to">>, Attrs),
- From = fxml:get_attr(<<"from">>, Attrs),
- Attrs1 = lists:keydelete(<<"to">>, 1, Attrs),
- Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1),
- Attrs3 = case To of
- {value, ToVal} -> [{<<"from">>, ToVal} | Attrs2];
- _ -> Attrs2
- end,
- Attrs4 = case From of
- {value, FromVal} -> [{<<"to">>, FromVal} | Attrs3];
- _ -> Attrs3
- end,
- Attrs5 = lists:keydelete(<<"type">>, 1, Attrs4),
- Attrs6 = [{<<"type">>, <<"error">>} | Attrs5],
- Attrs6.
-
--spec make_error_element(binary(), binary()) -> xmlel().
-
-make_error_element(Code, Desc) ->
- #xmlel{name = <<"error">>, attrs = [{<<"code">>, Code}],
- children = [{xmlcdata, Desc}]}.
-
--spec make_correct_from_to_attrs(binary(), binary(), [attr()]) -> [attr()].
-
-make_correct_from_to_attrs(From, To, Attrs) ->
- Attrs1 = lists:keydelete(<<"from">>, 1, Attrs),
- Attrs2 = case fxml:get_attr(<<"to">>, Attrs) of
- {value, _} -> Attrs1;
- _ -> [{<<"to">>, To} | Attrs1]
- end,
- Attrs3 = [{<<"from">>, From} | Attrs2],
- Attrs3.
-
--spec replace_from_to_attrs(binary(), binary(), [attr()]) -> [attr()].
-
-replace_from_to_attrs(From, To, Attrs) ->
- Attrs1 = lists:keydelete(<<"to">>, 1, Attrs),
- Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1),
- Attrs3 = [{<<"to">>, To} | Attrs2],
- Attrs4 = [{<<"from">>, From} | Attrs3],
- Attrs4.
-
--spec replace_from_to(jid(), jid(), xmlel()) -> xmlel().
-
-replace_from_to(From, To,
- #xmlel{name = Name, attrs = Attrs, children = Els}) ->
- NewAttrs =
- replace_from_to_attrs(jid:to_string(From),
- jid:to_string(To), Attrs),
- #xmlel{name = Name, attrs = NewAttrs, children = Els}.
-
--spec replace_from_attrs(binary(), [attr()]) -> [attr()].
-
-replace_from_attrs(From, Attrs) ->
- Attrs1 = lists:keydelete(<<"from">>, 1, Attrs),
- [{<<"from">>, From} | Attrs1].
-
--spec replace_from(jid(), xmlel()) -> xmlel().
-
-replace_from(From,
- #xmlel{name = Name, attrs = Attrs, children = Els}) ->
- NewAttrs = replace_from_attrs(jid:to_string(From),
- Attrs),
- #xmlel{name = Name, attrs = NewAttrs, children = Els}.
-
--spec remove_attr(binary(), xmlel()) -> xmlel().
-
-remove_attr(Attr,
- #xmlel{name = Name, attrs = Attrs, children = Els}) ->
- NewAttrs = lists:keydelete(Attr, 1, Attrs),
- #xmlel{name = Name, attrs = NewAttrs, children = Els}.
-
--spec make_jid(binary(), binary(), binary()) -> jid() | error.
-
-make_jid(User, Server, Resource) ->
- jid:make(User, Server, Resource).
-
--spec make_jid({binary(), binary(), binary()}) -> jid() | error.
-
-make_jid({User, Server, Resource}) ->
- jid:make({User, Server, Resource}).
-
-%% This is the reverse of make_jid/1
--spec split_jid(jid()) -> {binary(), binary(), binary()} | error.
-split_jid(J) ->
- jid:split(J).
-
--spec string_to_jid(binary()) -> jid() | error.
-
-string_to_jid(S) ->
- jid:from_string(S).
-
--spec jid_to_string(jid() | ljid()) -> binary().
-
-jid_to_string(J) ->
- jid:to_string(J).
-
--spec is_nodename(binary()) -> boolean().
-
-is_nodename(Node) ->
- jid:is_nodename(Node).
-
-%tolower_c(C) when C >= $A, C =< $Z ->
-% C + 32;
-%tolower_c(C) ->
-% C.
-
--define(LOWER(Char),
- if Char >= $A, Char =< $Z -> Char + 32;
- true -> Char
- end).
-
-%tolower(S) ->
-% lists:map(fun tolower_c/1, S).
-
-%tolower(S) ->
-% [?LOWER(Char) || Char <- S].
-
--spec tolower(binary()) -> binary().
-
-tolower(B) ->
- iolist_to_binary(tolower_s(binary_to_list(B))).
-
-tolower_s([C | Cs]) ->
- if C >= $A, C =< $Z -> [C + 32 | tolower_s(Cs)];
- true -> [C | tolower_s(Cs)]
- end;
-tolower_s([]) -> [].
-
-%tolower([C | Cs]) when C >= $A, C =< $Z ->
-% [C + 32 | tolower(Cs)];
-%tolower([C | Cs]) ->
-% [C | tolower(Cs)];
-%tolower([]) ->
-% [].
-
--spec nodeprep(binary()) -> binary() | error.
-
-nodeprep(S) -> jid:nodeprep(S).
-
--spec nameprep(binary()) -> binary() | error.
-
-nameprep(S) -> jid:nameprep(S).
-
--spec resourceprep(binary()) -> binary() | error.
-
-resourceprep(S) -> jid:resourceprep(S).
-
--spec jid_tolower(jid() | ljid()) -> error | ljid().
-
-jid_tolower(J) ->
- jid:tolower(J).
-
--spec jid_remove_resource(jid()) -> jid();
- (ljid()) -> ljid().
-
-jid_remove_resource(J) -> jid:remove_resource(J).
-
--spec jid_replace_resource(jid(), binary()) -> error | jid().
-
-jid_replace_resource(JID, Resource) ->
- jid:replace_resource(JID, Resource).
-
--spec get_iq_namespace(xmlel()) -> binary().
-
-get_iq_namespace(#xmlel{name = <<"iq">>, children = Els}) ->
- case fxml:remove_cdata(Els) of
- [#xmlel{attrs = Attrs}] -> fxml:get_attr_s(<<"xmlns">>, Attrs);
- _ -> <<"">>
- end;
-get_iq_namespace(_) -> <<"">>.
-
-%%
--spec iq_query_info(Xmlel :: xmlel()) ->
- iq_request() | 'reply' | 'invalid' | 'not_iq'.
-
-%% @spec (xmlelement()) -> iq() | reply | invalid | not_iq
-iq_query_info(El) -> iq_info_internal(El, request).
-
-%%
--spec iq_query_or_response_info(Xmlel :: xmlel()) ->
- iq_request() | iq_reply() |
- 'reply' | 'invalid' | 'not_iq'.
-
-iq_query_or_response_info(El) ->
- iq_info_internal(El, any).
-
-iq_info_internal(#xmlel{name = <<"iq">>, attrs = Attrs, children = Els}, Filter) ->
- ID = fxml:get_attr_s(<<"id">>, Attrs),
- Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
- {Type, Class} = case fxml:get_attr_s(<<"type">>, Attrs) of
- <<"set">> -> {set, request};
- <<"get">> -> {get, request};
- <<"result">> -> {result, reply};
- <<"error">> -> {error, reply};
- _ -> {invalid, invalid}
- end,
- if Type == invalid -> invalid; Class == request; Filter == any ->
- FilteredEls = fxml:remove_cdata(Els),
- {XMLNS, SubEl} = case {Class, FilteredEls} of
- {request, [#xmlel{attrs = Attrs2}]} ->
- {fxml:get_attr_s(<<"xmlns">>, Attrs2), hd(FilteredEls)};
- {reply, _} ->
- NonErrorEls = [El || #xmlel{name = SubName} = El <- FilteredEls,
- SubName /= <<"error">>],
- {case NonErrorEls of
- [NonErrorEl] -> fxml:get_tag_attr_s(<<"xmlns">>, NonErrorEl);
- _ -> <<"">>
- end,
- FilteredEls};
- _ ->
- {<<"">>, []}
- end,
- if XMLNS == <<"">>, Class == request ->
- invalid;
- true ->
- #iq{id = ID, type = Type, xmlns = XMLNS, lang = Lang, sub_el = SubEl}
- end;
- Class == reply, Filter /= any ->
- reply
- end;
-iq_info_internal(_, _) -> not_iq.
-
--spec is_iq_request_type(set | get | result | error) -> boolean().
-
-is_iq_request_type(set) -> true;
-is_iq_request_type(get) -> true;
-is_iq_request_type(_) -> false.
-
-iq_type_to_string(set) -> <<"set">>;
-iq_type_to_string(get) -> <<"get">>;
-iq_type_to_string(result) -> <<"result">>;
-iq_type_to_string(error) -> <<"error">>.
-
--spec iq_to_xml(IQ :: iq()) -> xmlel().
-
-iq_to_xml(#iq{id = ID, type = Type, sub_el = SubEl}) ->
- if ID /= <<"">> ->
- #xmlel{name = <<"iq">>,
- attrs =
- [{<<"id">>, ID}, {<<"type">>, iq_type_to_string(Type)}],
- children = SubEl};
- true ->
- #xmlel{name = <<"iq">>,
- attrs = [{<<"type">>, iq_type_to_string(Type)}],
- children = SubEl}
- end.
-
--spec parse_xdata_submit(El :: xmlel()) ->
- [{Var::binary(), Values::[binary()]}] | 'invalid'.
-
-parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) ->
- case fxml:get_attr_s(<<"type">>, Attrs) of
- <<"submit">> ->
- lists:reverse(parse_xdata_fields(Els, []));
- <<"form">> -> %% This is a workaround to accept Psi's wrong forms
- lists:reverse(parse_xdata_fields(Els, []));
- _ ->
- invalid
- end.
-
--spec parse_xdata_fields(Xmlels :: [xmlel() | cdata()],
- Res :: [{Var::binary(), Values :: [binary()]}]) ->
- [{Var::binary(), Values::[binary()]}].
-
-parse_xdata_fields([], Res) -> Res;
-parse_xdata_fields([#xmlel{name = <<"field">>, attrs = Attrs, children = SubEls}
- | Els], Res) ->
- case fxml:get_attr_s(<<"var">>, Attrs) of
- <<>> ->
- parse_xdata_fields(Els, Res);
- Var ->
- Field = {Var, lists:reverse(parse_xdata_values(SubEls, []))},
- parse_xdata_fields(Els, [Field | Res])
- end;
-parse_xdata_fields([_ | Els], Res) ->
- parse_xdata_fields(Els, Res).
-
--spec parse_xdata_values(Xmlels :: [xmlel() | cdata()],
- Res :: [binary()]) -> [binary()].
-
-parse_xdata_values([], Res) -> Res;
-parse_xdata_values([#xmlel{name = <<"value">>, children = SubEls} | Els], Res) ->
- Val = fxml:get_cdata(SubEls),
- parse_xdata_values(Els, [Val | Res]);
-parse_xdata_values([_ | Els], Res) ->
- parse_xdata_values(Els, Res).
-
--spec rsm_decode(iq() | xmlel()) -> none | rsm_in().
-
-rsm_decode(#iq{sub_el = SubEl}) -> rsm_decode(SubEl);
-rsm_decode(#xmlel{} = SubEl) ->
- case fxml:get_subtag(SubEl, <<"set">>) of
- false -> none;
- #xmlel{name = <<"set">>, children = SubEls} ->
- lists:foldl(fun rsm_parse_element/2, #rsm_in{}, SubEls)
- end.
-
-rsm_parse_element(#xmlel{name = <<"max">>, attrs = []} =
- Elem,
- RsmIn) ->
- CountStr = fxml:get_tag_cdata(Elem),
- {Count, _} = str:to_integer(CountStr),
- RsmIn#rsm_in{max = Count};
-rsm_parse_element(#xmlel{name = <<"before">>,
- attrs = []} =
- Elem,
- RsmIn) ->
- UID = fxml:get_tag_cdata(Elem),
- RsmIn#rsm_in{direction = before, id = UID};
-rsm_parse_element(#xmlel{name = <<"after">>,
- attrs = []} =
- Elem,
- RsmIn) ->
- UID = fxml:get_tag_cdata(Elem),
- RsmIn#rsm_in{direction = aft, id = UID};
-rsm_parse_element(#xmlel{name = <<"index">>,
- attrs = []} =
- Elem,
- RsmIn) ->
- IndexStr = fxml:get_tag_cdata(Elem),
- {Index, _} = str:to_integer(IndexStr),
- RsmIn#rsm_in{index = Index};
-rsm_parse_element(_, RsmIn) -> RsmIn.
-
--spec rsm_encode(iq(), rsm_out()) -> iq().
-
-rsm_encode(#iq{sub_el = SubEl} = IQ, RsmOut) ->
- Set = #xmlel{name = <<"set">>,
- attrs = [{<<"xmlns">>, ?NS_RSM}],
- children = lists:reverse(rsm_encode_out(RsmOut))},
- #xmlel{name = Name, attrs = Attrs, children = SubEls} =
- SubEl,
- New = #xmlel{name = Name, attrs = Attrs,
- children = [Set | SubEls]},
- IQ#iq{sub_el = New}.
-
--spec rsm_encode(none | rsm_out()) -> [xmlel()].
-
-rsm_encode(none) -> [];
-rsm_encode(RsmOut) ->
- [#xmlel{name = <<"set">>,
- attrs = [{<<"xmlns">>, ?NS_RSM}],
- children = lists:reverse(rsm_encode_out(RsmOut))}].
-
-rsm_encode_out(#rsm_out{count = Count, index = Index,
- first = First, last = Last}) ->
- El = rsm_encode_first(First, Index, []),
- El2 = rsm_encode_last(Last, El),
- rsm_encode_count(Count, El2).
-
-rsm_encode_first(undefined, undefined, Arr) -> Arr;
-rsm_encode_first(First, undefined, Arr) ->
- [#xmlel{name = <<"first">>, attrs = [],
- children = [{xmlcdata, First}]}
- | Arr];
-rsm_encode_first(First, Index, Arr) ->
- [#xmlel{name = <<"first">>,
- attrs = [{<<"index">>, i2l(Index)}],
- children = [{xmlcdata, First}]}
- | Arr].
-
-rsm_encode_last(undefined, Arr) -> Arr;
-rsm_encode_last(Last, Arr) ->
- [#xmlel{name = <<"last">>, attrs = [],
- children = [{xmlcdata, Last}]}
- | Arr].
-
-rsm_encode_count(undefined, Arr) -> Arr;
-rsm_encode_count(Count, Arr) ->
- [#xmlel{name = <<"count">>, attrs = [],
- children = [{xmlcdata, i2l(Count)}]}
- | Arr].
-
--spec unwrap_carbon(xmlel()) -> xmlel().
-
-unwrap_carbon(#xmlel{name = <<"message">>} = Stanza) ->
- case unwrap_carbon(Stanza, <<"sent">>) of
- #xmlel{} = Payload ->
- Payload;
- false ->
- case unwrap_carbon(Stanza, <<"received">>) of
- #xmlel{} = Payload ->
- Payload;
- false ->
- Stanza
- end
- end;
-unwrap_carbon(Stanza) -> Stanza.
-
--spec unwrap_carbon(xmlel(), binary()) -> xmlel() | false.
-
-unwrap_carbon(Stanza, Direction) ->
- case fxml:get_subtag(Stanza, Direction) of
- #xmlel{name = Direction, attrs = Attrs} = El ->
- case fxml:get_attr_s(<<"xmlns">>, Attrs) of
- NS when NS == ?NS_CARBONS_2;
- NS == ?NS_CARBONS_1 ->
- case fxml:get_subtag_with_xmlns(El, <<"forwarded">>,
- ?NS_FORWARD) of
- #xmlel{children = Els} ->
- case fxml:remove_cdata(Els) of
- [#xmlel{} = Payload] ->
- Payload;
- _ ->
- false
- end;
- false ->
- false
- end;
- _NS ->
- false
- end;
- false ->
- false
- end.
-
--spec is_standalone_chat_state(xmlel()) -> boolean().
-
-is_standalone_chat_state(Stanza) ->
- case unwrap_carbon(Stanza) of
- #xmlel{name = <<"message">>, children = Els} ->
- IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY],
- Stripped = [El || #xmlel{name = Name, attrs = Attrs} = El <- Els,
- not lists:member(fxml:get_attr_s(<<"xmlns">>,
- Attrs),
- IgnoreNS),
- Name /= <<"thread">>],
- Stripped == [];
- #xmlel{} ->
- false
- end.
-
--spec add_delay_info(xmlel(), jid() | ljid() | binary(), erlang:timestamp())
- -> xmlel().
-
-add_delay_info(El, From, Time) ->
- add_delay_info(El, From, Time, <<"">>).
-
--spec add_delay_info(xmlel(), jid() | ljid() | binary(), erlang:timestamp(),
- binary()) -> xmlel().
-
-add_delay_info(El, From, Time, Desc) ->
- DelayTag = create_delay_tag(Time, From, Desc),
- fxml:append_subtags(El, [DelayTag]).
-
--spec create_delay_tag(erlang:timestamp(), jid() | ljid() | binary(), binary())
- -> xmlel() | error.
-
-create_delay_tag(TimeStamp, FromJID, Desc) when is_tuple(FromJID) ->
- From = jid:to_string(FromJID),
- Stamp = now_to_utc_string(TimeStamp, 3),
- Children = case Desc of
- <<"">> -> [];
- _ -> [{xmlcdata, Desc}]
- end,
- #xmlel{name = <<"delay">>,
- attrs =
- [{<<"xmlns">>, ?NS_DELAY}, {<<"from">>, From},
- {<<"stamp">>, Stamp}],
- children = Children};
-create_delay_tag(DateTime, Host, Desc) when is_binary(Host) ->
- FromJID = jid:make(<<"">>, Host, <<"">>),
- create_delay_tag(DateTime, FromJID, Desc).
-
--type tz() :: {binary(), {integer(), integer()}} | {integer(), integer()} | utc.
-
-%% Timezone = utc | {Sign::string(), {Hours, Minutes}} | {Hours, Minutes}
-%% Hours = integer()
-%% Minutes = integer()
--spec timestamp_to_iso(calendar:datetime(), tz()) -> {binary(), binary()}.
-
-%% This is the XEP-0082 date and time format
-%% http://xmpp.org/extensions/xep-0082.html
-
-timestamp_to_iso({{Year, Month, Day},
- {Hour, Minute, Second}},
- Timezone) ->
- Timestamp_string =
- lists:flatten(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",
- [Year, Month, Day, Hour, Minute, Second])),
- Timezone_string = case Timezone of
- utc -> "Z";
- {Sign, {TZh, TZm}} ->
- io_lib:format("~s~2..0B:~2..0B", [Sign, TZh, TZm]);
- {TZh, TZm} ->
- Sign = case TZh >= 0 of
- true -> "+";
- false -> "-"
- end,
- io_lib:format("~s~2..0B:~2..0B",
- [Sign, abs(TZh), TZm])
- end,
- {iolist_to_binary(Timestamp_string), iolist_to_binary(Timezone_string)}.
-
-
--spec timestamp_to_legacy(calendar:datetime()) -> binary().
-%% This is the jabber legacy format
-%% http://xmpp.org/extensions/xep-0091.html#time
-timestamp_to_legacy({{Year, Month, Day},
- {Hour, Minute, Second}}) ->
- iolist_to_binary(io_lib:format("~4..0B~2..0B~2..0BT~2..0B:~2..0B:~2..0B",
- [Year, Month, Day, Hour, Minute, Second])).
-
--spec timestamp_to_iso_basic(calendar:datetime()) -> binary().
-%% This is the ISO 8601 basic bormat
-timestamp_to_iso_basic({{Year, Month, Day},
- {Hour, Minute, Second}}) ->
- iolist_to_binary(io_lib:format("~4..0B~2..0B~2..0BT~2..0B~2..0B~2..0B",
- [Year, Month, Day, Hour, Minute, Second])).
-
--spec now_to_utc_string(erlang:timestamp()) -> binary().
-
-now_to_utc_string({MegaSecs, Secs, MicroSecs}) ->
- now_to_utc_string({MegaSecs, Secs, MicroSecs}, 6).
-
--spec now_to_utc_string(erlang:timestamp(), 1..6) -> binary().
-
-now_to_utc_string({MegaSecs, Secs, MicroSecs}, Precision) ->
- {{Year, Month, Day}, {Hour, Minute, Second}} =
- calendar:now_to_universal_time({MegaSecs, Secs,
- MicroSecs}),
- Max = round(math:pow(10, Precision)),
- case round(MicroSecs / math:pow(10, 6 - Precision)) of
- Max ->
- now_to_utc_string({MegaSecs, Secs + 1, 0}, Precision);
- FracOfSec ->
- list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT"
- "~2..0B:~2..0B:~2..0B.~*..0BZ",
- [Year, Month, Day, Hour, Minute, Second,
- Precision, FracOfSec]))
- end.
-
--spec now_to_local_string(erlang:timestamp()) -> binary().
-
-now_to_local_string({MegaSecs, Secs, MicroSecs}) ->
- LocalTime = calendar:now_to_local_time({MegaSecs, Secs,
- MicroSecs}),
- UTCTime = calendar:now_to_universal_time({MegaSecs,
- Secs, MicroSecs}),
- Seconds =
- calendar:datetime_to_gregorian_seconds(LocalTime) -
- calendar:datetime_to_gregorian_seconds(UTCTime),
- {{H, M, _}, Sign} = if Seconds < 0 ->
- {calendar:seconds_to_time(-Seconds), "-"};
- true -> {calendar:seconds_to_time(Seconds), "+"}
- end,
- {{Year, Month, Day}, {Hour, Minute, Second}} =
- LocalTime,
- list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B.~6."
- ".0B~s~2..0B:~2..0B",
- [Year, Month, Day, Hour, Minute, Second,
- MicroSecs, Sign, H, M])).
-
--spec datetime_string_to_timestamp(binary()) -> undefined | erlang:timestamp().
-
-datetime_string_to_timestamp(TimeStr) ->
- case catch parse_datetime(TimeStr) of
- {'EXIT', _Err} -> undefined;
- TimeStamp -> TimeStamp
- end.
-
-parse_datetime(TimeStr) ->
- [Date, Time] = str:tokens(TimeStr, <<"T">>),
- D = parse_date(Date),
- {T, MS, TZH, TZM} = parse_time(Time),
- S = calendar:datetime_to_gregorian_seconds({D, T}),
- S1 = calendar:datetime_to_gregorian_seconds({{1970, 1,
- 1},
- {0, 0, 0}}),
- Seconds = S - S1 - TZH * 60 * 60 - TZM * 60,
- {Seconds div 1000000, Seconds rem 1000000, MS}.
-
-% yyyy-mm-dd
-parse_date(Date) ->
- [Y, M, D] = str:tokens(Date, <<"-">>),
- Date1 = {binary_to_integer(Y), binary_to_integer(M),
- binary_to_integer(D)},
- case calendar:valid_date(Date1) of
- true -> Date1;
- _ -> false
- end.
-
-% hh:mm:ss[.sss]TZD
-parse_time(Time) ->
- case str:str(Time, <<"Z">>) of
- 0 -> parse_time_with_timezone(Time);
- _ ->
- [T | _] = str:tokens(Time, <<"Z">>),
- {TT, MS} = parse_time1(T),
- {TT, MS, 0, 0}
- end.
-
-parse_time_with_timezone(Time) ->
- case str:str(Time, <<"+">>) of
- 0 ->
- case str:str(Time, <<"-">>) of
- 0 -> false;
- _ -> parse_time_with_timezone(Time, <<"-">>)
- end;
- _ -> parse_time_with_timezone(Time, <<"+">>)
- end.
-
-parse_time_with_timezone(Time, Delim) ->
- [T, TZ] = str:tokens(Time, Delim),
- {TZH, TZM} = parse_timezone(TZ),
- {TT, MS} = parse_time1(T),
- case Delim of
- <<"-">> -> {TT, MS, -TZH, -TZM};
- <<"+">> -> {TT, MS, TZH, TZM}
- end.
-
-parse_timezone(TZ) ->
- [H, M] = str:tokens(TZ, <<":">>),
- {[H1, M1], true} = check_list([{H, 12}, {M, 60}]),
- {H1, M1}.
-
-parse_time1(Time) ->
- [HMS | T] = str:tokens(Time, <<".">>),
- MS = case T of
- [] -> 0;
- [Val] -> binary_to_integer(str:left(Val, 6, $0))
- end,
- [H, M, S] = str:tokens(HMS, <<":">>),
- {[H1, M1, S1], true} = check_list([{H, 24}, {M, 60},
- {S, 60}]),
- {{H1, M1, S1}, MS}.
-
-check_list(List) ->
- lists:mapfoldl(fun ({L, N}, B) ->
- V = binary_to_integer(L),
- if (V >= 0) and (V =< N) -> {V, B};
- true -> {false, false}
- end
- end,
- true, List).
-
-%
-% Base64 stuff (based on httpd_util.erl)
-%
-
--spec term_to_base64(term()) -> binary().
-
-term_to_base64(Term) ->
- encode_base64(term_to_binary(Term)).
-
--spec base64_to_term(binary()) -> {term, term()} | error.
-
-base64_to_term(Base64) ->
- case catch binary_to_term(decode_base64(Base64), [safe]) of
- {'EXIT', _} ->
- error;
- Term ->
- {term, Term}
- end.
-
--spec decode_base64(binary()) -> binary().
-
-decode_base64(S) ->
- case catch binary:last(S) of
- C when C == $\n; C == $\s ->
- decode_base64(binary:part(S, 0, byte_size(S) - 1));
- _ ->
- decode_base64_bin(S, <<>>)
- end.
-
-take_without_spaces(Bin, Count) ->
- take_without_spaces(Bin, Count, <<>>).
-
-take_without_spaces(Bin, 0, Acc) ->
- {Acc, Bin};
-take_without_spaces(<<>>, _, Acc) ->
- {Acc, <<>>};
-take_without_spaces(<<$\s, Tail/binary>>, Count, Acc) ->
- take_without_spaces(Tail, Count, Acc);
-take_without_spaces(<<$\t, Tail/binary>>, Count, Acc) ->
- take_without_spaces(Tail, Count, Acc);
-take_without_spaces(<<$\n, Tail/binary>>, Count, Acc) ->
- take_without_spaces(Tail, Count, Acc);
-take_without_spaces(<<$\r, Tail/binary>>, Count, Acc) ->
- take_without_spaces(Tail, Count, Acc);
-take_without_spaces(<<Char:8, Tail/binary>>, Count, Acc) ->
- take_without_spaces(Tail, Count-1, <<Acc/binary, Char:8>>).
-
-decode_base64_bin(<<>>, Acc) ->
- Acc;
-decode_base64_bin(Bin, Acc) ->
- case take_without_spaces(Bin, 4) of
- {<<A, B, $=, $=>>, _} ->
- <<Acc/binary, (d(A)):6, (d(B) bsr 4):2>>;
- {<<A, B, C, $=>>, _} ->
- <<Acc/binary, (d(A)):6, (d(B)):6, (d(C) bsr 2):4>>;
- {<<A, B, C, D>>, Tail} ->
- Acc2 = <<Acc/binary, (d(A)):6, (d(B)):6, (d(C)):6, (d(D)):6>>,
- decode_base64_bin(Tail, Acc2);
- _ ->
- <<"">>
- end.
-
-d(X) when X >= $A, X =< $Z -> X - 65;
-d(X) when X >= $a, X =< $z -> X - 71;
-d(X) when X >= $0, X =< $9 -> X + 4;
-d($+) -> 62;
-d($/) -> 63;
-d(_) -> 63.
-
-
-%% Convert Erlang inet IP to list
--spec encode_base64(binary()) -> binary().
-
-encode_base64(Data) ->
- encode_base64_bin(Data, <<>>).
-
-encode_base64_bin(<<A:6, B:6, C:6, D:6, Tail/binary>>, Acc) ->
- encode_base64_bin(Tail, <<Acc/binary, (e(A)):8, (e(B)):8, (e(C)):8, (e(D)):8>>);
-encode_base64_bin(<<A:6, B:6, C:4>>, Acc) ->
- <<Acc/binary, (e(A)):8, (e(B)):8, (e(C bsl 2)):8, $=>>;
-encode_base64_bin(<<A:6, B:2>>, Acc) ->
- <<Acc/binary, (e(A)):8, (e(B bsl 4)):8, $=, $=>>;
-encode_base64_bin(<<>>, Acc) ->
- Acc.
-
-e(X) when X >= 0, X < 26 -> X + 65;
-e(X) when X > 25, X < 52 -> X + 71;
-e(X) when X > 51, X < 62 -> X - 4;
-e(62) -> $+;
-e(63) -> $/;
-e(X) -> exit({bad_encode_base64_token, X}).
-
--spec ip_to_list(inet:ip_address() | undefined |
- {inet:ip_address(), inet:port_number()}) -> binary().
-
-ip_to_list({IP, _Port}) ->
- ip_to_list(IP);
-%% This function clause could use inet_parse too:
-ip_to_list(undefined) ->
- <<"unknown">>;
-ip_to_list(IP) ->
- list_to_binary(inet_parse:ntoa(IP)).
-
-binary_to_atom(Bin) ->
- erlang:binary_to_atom(Bin, utf8).
-
-binary_to_integer(Bin) ->
- erlang:binary_to_integer(Bin).
-
-binary_to_integer(Bin, Base) ->
- erlang:binary_to_integer(Bin, Base).
-
-integer_to_binary(I) ->
- erlang:integer_to_binary(I).
-
-integer_to_binary(I, Base) ->
- erlang:integer_to_binary(I, Base).
-
-tuple_to_binary(T) ->
- iolist_to_binary(tuple_to_list(T)).
-
-atom_to_binary(A) ->
- erlang:atom_to_binary(A, utf8).
-
-expr_to_term(Expr) ->
- Str = binary_to_list(<<Expr/binary, ".">>),
- {ok, Tokens, _} = erl_scan:string(Str),
- {ok, Term} = erl_parse:parse_term(Tokens),
- Term.
-
-term_to_expr(Term) ->
- list_to_binary(io_lib:print(Term)).
-
-l2i(I) when is_integer(I) -> I;
-l2i(L) when is_binary(L) -> binary_to_integer(L).
-
-i2l(I) when is_integer(I) -> integer_to_binary(I);
-i2l(L) when is_binary(L) -> L.
-
-i2l(I, N) when is_integer(I) -> i2l(i2l(I), N);
-i2l(L, N) when is_binary(L) ->
- case str:len(L) of
- N -> L;
- C when C > N -> L;
- _ -> i2l(<<$0, L/binary>>, N)
- end.
-
--spec queue_drop_while(fun((term()) -> boolean()), ?TQUEUE) -> ?TQUEUE.
-
-queue_drop_while(F, Q) ->
- case queue:peek(Q) of
- {value, Item} ->
- case F(Item) of
- true ->
- queue_drop_while(F, queue:drop(Q));
- _ ->
- Q
- end;
- empty ->
- Q
- end.