diff options
Diffstat (limited to 'src/jlib.erl')
-rw-r--r-- | src/jlib.erl | 1156 |
1 files changed, 614 insertions, 542 deletions
diff --git a/src/jlib.erl b/src/jlib.erl index ce99f95e..bf08a476 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -25,238 +25,248 @@ %%%---------------------------------------------------------------------- -module(jlib). + -author('alexey@process-one.net'). --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, - make_jid/3, - make_jid/1, - string_to_jid/1, - jid_to_string/1, - is_nodename/1, - tolower/1, - nodeprep/1, - nameprep/1, - resourceprep/1, - jid_tolower/1, - jid_remove_resource/1, - jid_replace_resource/2, - 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, - timestamp_to_iso/1, % TODO: Remove once XEP-0091 is Obsolete - timestamp_to_iso/2, - timestamp_to_xml/4, - timestamp_to_xml/1, % TODO: Remove once XEP-0091 is Obsolete - now_to_utc_string/1, - now_to_local_string/1, - datetime_string_to_timestamp/1, - decode_base64/1, - encode_base64/1, - ip_to_list/1, - rsm_encode/1, - rsm_encode/2, - rsm_decode/1]). +-compile({no_auto_import, [{atom_to_binary, 2}]}). + +-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, make_jid/3, make_jid/1, string_to_jid/1, + jid_to_string/1, is_nodename/1, tolower/1, nodeprep/1, + nameprep/1, resourceprep/1, jid_tolower/1, + jid_remove_resource/1, jid_replace_resource/2, + 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, timestamp_to_iso/1, + timestamp_to_iso/2, timestamp_to_xml/4, + timestamp_to_xml/1, now_to_utc_string/1, + now_to_local_string/1, datetime_string_to_timestamp/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]). + +%% TODO: Remove once XEP-0091 is Obsolete +%% TODO: Remove once XEP-0091 is Obsolete -include("jlib.hrl"). +-export_type([jid/0]). + %send_iq(From, To, ID, SubTags) -> % ok. -make_result_iq_reply({xmlelement, Name, Attrs, SubTags}) -> +-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), - {xmlelement, Name, NewAttrs, SubTags}. + #xmlel{name = Name, attrs = NewAttrs, + children = SubTags}. + +-spec make_result_iq_reply_attrs([attr()]) -> [attr()]. make_result_iq_reply_attrs(Attrs) -> - To = xml:get_attr("to", Attrs), - From = xml:get_attr("from", Attrs), - Attrs1 = lists:keydelete("to", 1, Attrs), - Attrs2 = lists:keydelete("from", 1, Attrs1), + To = xml:get_attr(<<"to">>, Attrs), + From = xml: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 + {value, ToVal} -> [{<<"from">>, ToVal} | Attrs2]; + _ -> Attrs2 end, Attrs4 = case From of - {value, FromVal} -> - [{"to", FromVal} | Attrs3]; - _ -> - Attrs3 + {value, FromVal} -> [{<<"to">>, FromVal} | Attrs3]; + _ -> Attrs3 end, - Attrs5 = lists:keydelete("type", 1, Attrs4), - Attrs6 = [{"type", "result"} | Attrs5], + Attrs5 = lists:keydelete(<<"type">>, 1, Attrs4), + Attrs6 = [{<<"type">>, <<"result">>} | Attrs5], Attrs6. -make_error_reply({xmlelement, Name, Attrs, SubTags}, Code, Desc) -> - NewAttrs = make_error_reply_attrs(Attrs), - {xmlelement, Name, NewAttrs, SubTags ++ [{xmlelement, "error", - [{"code", Code}], - [{xmlcdata, Desc}]}]}. +-spec make_error_reply(xmlel(), binary(), binary()) -> xmlel(). -make_error_reply({xmlelement, Name, Attrs, SubTags}, Error) -> +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), - {xmlelement, Name, NewAttrs, SubTags ++ [Error]}. + #xmlel{name = Name, attrs = NewAttrs, + children = SubTags ++ [Error]}. + +-spec make_error_reply_attrs([attr()]) -> [attr()]. make_error_reply_attrs(Attrs) -> - To = xml:get_attr("to", Attrs), - From = xml:get_attr("from", Attrs), - Attrs1 = lists:keydelete("to", 1, Attrs), - Attrs2 = lists:keydelete("from", 1, Attrs1), + To = xml:get_attr(<<"to">>, Attrs), + From = xml: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 + {value, ToVal} -> [{<<"from">>, ToVal} | Attrs2]; + _ -> Attrs2 end, Attrs4 = case From of - {value, FromVal} -> - [{"to", FromVal} | Attrs3]; - _ -> - Attrs3 + {value, FromVal} -> [{<<"to">>, FromVal} | Attrs3]; + _ -> Attrs3 end, - Attrs5 = lists:keydelete("type", 1, Attrs4), - Attrs6 = [{"type", "error"} | Attrs5], + Attrs5 = lists:keydelete(<<"type">>, 1, Attrs4), + Attrs6 = [{<<"type">>, <<"error">>} | Attrs5], Attrs6. +-spec make_error_element(binary(), binary()) -> xmlel(). + make_error_element(Code, Desc) -> - {xmlelement, "error", - [{"code", Code}], - [{xmlcdata, 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 xml:get_attr("to", Attrs) of - {value, _} -> - Attrs1; - _ -> - [{"to", To} | Attrs1] + Attrs1 = lists:keydelete(<<"from">>, 1, Attrs), + Attrs2 = case xml:get_attr(<<"to">>, Attrs) of + {value, _} -> Attrs1; + _ -> [{<<"to">>, To} | Attrs1] end, - Attrs3 = [{"from", From} | Attrs2], + 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], + Attrs1 = lists:keydelete(<<"to">>, 1, Attrs), + Attrs2 = lists:keydelete(<<"from">>, 1, Attrs1), + Attrs3 = [{<<"to">>, To} | Attrs2], + Attrs4 = [{<<"from">>, From} | Attrs3], Attrs4. -replace_from_to(From, To, {xmlelement, Name, Attrs, Els}) -> - NewAttrs = replace_from_to_attrs(jlib:jid_to_string(From), - jlib:jid_to_string(To), - Attrs), - {xmlelement, Name, NewAttrs, Els}. +-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(jlib:jid_to_string(From), + jlib: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]. + Attrs1 = lists:keydelete(<<"from">>, 1, Attrs), + [{<<"from">>, From} | Attrs1]. + +-spec replace_from(jid(), xmlel()) -> xmlel(). -replace_from(From, {xmlelement, Name, Attrs, Els}) -> - NewAttrs = replace_from_attrs(jlib:jid_to_string(From), Attrs), - {xmlelement, Name, NewAttrs, Els}. +replace_from(From, + #xmlel{name = Name, attrs = Attrs, children = Els}) -> + NewAttrs = replace_from_attrs(jlib:jid_to_string(From), + Attrs), + #xmlel{name = Name, attrs = NewAttrs, children = Els}. -remove_attr(Attr, {xmlelement, Name, Attrs, Els}) -> +-spec remove_attr(binary(), xmlel()) -> xmlel(). + +remove_attr(Attr, + #xmlel{name = Name, attrs = Attrs, children = Els}) -> NewAttrs = lists:keydelete(Attr, 1, Attrs), - {xmlelement, Name, NewAttrs, Els}. + #xmlel{name = Name, attrs = NewAttrs, children = Els}. +-spec make_jid(binary(), binary(), binary()) -> jid() | error. make_jid(User, Server, Resource) -> case nodeprep(User) of - error -> error; - LUser -> - case nameprep(Server) of - error -> error; - LServer -> - case resourceprep(Resource) of - error -> error; - LResource -> - #jid{user = User, - server = Server, - resource = Resource, - luser = LUser, - lserver = LServer, - lresource = LResource} - end - end + error -> error; + LUser -> + case nameprep(Server) of + error -> error; + LServer -> + case resourceprep(Resource) of + error -> error; + LResource -> + #jid{user = User, server = Server, resource = Resource, + luser = LUser, lserver = LServer, + lresource = LResource} + end + end end. +-spec make_jid({binary(), binary(), binary()}) -> jid() | error. + make_jid({User, Server, Resource}) -> make_jid(User, Server, Resource). -string_to_jid(J) -> - string_to_jid1(J, ""). +-spec string_to_jid(binary()) -> jid() | error. -string_to_jid1([$@ | _J], "") -> - error; +string_to_jid(S) -> + string_to_jid1(binary_to_list(S), ""). + +string_to_jid1([$@ | _J], "") -> error; string_to_jid1([$@ | J], N) -> string_to_jid2(J, lists:reverse(N), ""); -string_to_jid1([$/ | _J], "") -> - error; +string_to_jid1([$/ | _J], "") -> error; string_to_jid1([$/ | J], N) -> string_to_jid3(J, "", lists:reverse(N), ""); string_to_jid1([C | J], N) -> string_to_jid1(J, [C | N]); -string_to_jid1([], "") -> - error; +string_to_jid1([], "") -> error; string_to_jid1([], N) -> - make_jid("", lists:reverse(N), ""). + make_jid(<<"">>, list_to_binary(lists:reverse(N)), <<"">>). %% Only one "@" is admitted per JID -string_to_jid2([$@ | _J], _N, _S) -> - error; -string_to_jid2([$/ | _J], _N, "") -> - error; +string_to_jid2([$@ | _J], _N, _S) -> error; +string_to_jid2([$/ | _J], _N, "") -> error; string_to_jid2([$/ | J], N, S) -> string_to_jid3(J, N, lists:reverse(S), ""); string_to_jid2([C | J], N, S) -> string_to_jid2(J, N, [C | S]); -string_to_jid2([], _N, "") -> - error; +string_to_jid2([], _N, "") -> error; string_to_jid2([], N, S) -> - make_jid(N, lists:reverse(S), ""). + make_jid(list_to_binary(N), list_to_binary(lists:reverse(S)), <<"">>). string_to_jid3([C | J], N, S, R) -> string_to_jid3(J, N, S, [C | R]); string_to_jid3([], N, S, R) -> - make_jid(N, S, lists:reverse(R)). + make_jid(list_to_binary(N), list_to_binary(S), + list_to_binary(lists:reverse(R))). + +-spec jid_to_string(jid() | ljid()) -> binary(). -jid_to_string(#jid{user = User, server = Server, resource = Resource}) -> +jid_to_string(#jid{user = User, server = Server, + resource = Resource}) -> jid_to_string({User, Server, Resource}); -jid_to_string({Node, Server, Resource}) -> +jid_to_string({N, S, R}) -> + Node = iolist_to_binary(N), + Server = iolist_to_binary(S), + Resource = iolist_to_binary(R), S1 = case Node of - "" -> - ""; - _ -> - Node ++ "@" + <<"">> -> <<"">>; + _ -> <<Node/binary, "@">> end, - S2 = S1 ++ Server, + S2 = <<S1/binary, Server/binary>>, S3 = case Resource of - "" -> - S2; - _ -> - S2 ++ "/" ++ Resource + <<"">> -> S2; + _ -> <<S2/binary, "/", Resource/binary>> end, S3. +-spec is_nodename(binary()) -> boolean(). -is_nodename([]) -> - false; -is_nodename(J) -> - nodeprep(J) /= error. - +is_nodename(Node) -> + N = nodeprep(Node), + (N /= error) and (N /= <<>>). %tolower_c(C) when C >= $A, C =< $Z -> % C + 32; @@ -264,12 +274,9 @@ is_nodename(J) -> % C. -define(LOWER(Char), - if - Char >= $A, Char =< $Z -> - Char + 32; - true -> - Char - end). + if Char >= $A, Char =< $Z -> Char + 32; + true -> Char + end). %tolower(S) -> % lists:map(fun tolower_c/1, S). @@ -277,16 +284,16 @@ is_nodename(J) -> %tolower(S) -> % [?LOWER(Char) || Char <- S]. -% Not tail-recursive but it seems works faster than variants above -tolower([C | Cs]) -> - if - C >= $A, C =< $Z -> - [C + 32 | tolower(Cs)]; - true -> - [C | tolower(Cs)] +-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([]) -> - []. +tolower_s([]) -> []. %tolower([C | Cs]) when C >= $A, C =< $Z -> % [C + 32 | tolower(Cs)]; @@ -295,514 +302,579 @@ tolower([]) -> %tolower([]) -> % []. +-spec nodeprep(binary()) -> binary() | error. -nodeprep(S) when length(S) < 1024 -> +nodeprep("") -> <<>>; +nodeprep(S) when byte_size(S) < 1024 -> R = stringprep:nodeprep(S), - if - length(R) < 1024 -> R; - true -> error + if byte_size(R) < 1024 -> R; + true -> error end; -nodeprep(_) -> - error. +nodeprep(_) -> error. + +-spec nameprep(binary()) -> binary() | error. -nameprep(S) when length(S) < 1024 -> +nameprep(S) when byte_size(S) < 1024 -> R = stringprep:nameprep(S), - if - length(R) < 1024 -> R; - true -> error + if byte_size(R) < 1024 -> R; + true -> error end; -nameprep(_) -> - error. +nameprep(_) -> error. -resourceprep(S) when length(S) < 1024 -> +-spec resourceprep(binary()) -> binary() | error. + +resourceprep(S) when byte_size(S) < 1024 -> R = stringprep:resourceprep(S), - if - length(R) < 1024 -> R; - true -> error + if byte_size(R) < 1024 -> R; + true -> error end; -resourceprep(_) -> - error. +resourceprep(_) -> error. +-spec jid_tolower(jid() | ljid()) -> error | ljid(). -jid_tolower(#jid{luser = U, lserver = S, lresource = R}) -> +jid_tolower(#jid{luser = U, lserver = S, + lresource = R}) -> {U, S, R}; jid_tolower({U, S, R}) -> case nodeprep(U) of - error -> error; - LUser -> - case nameprep(S) of - error -> error; - LServer -> - case resourceprep(R) of - error -> error; - LResource -> - {LUser, LServer, LResource} - end - end + error -> error; + LUser -> + case nameprep(S) of + error -> error; + LServer -> + case resourceprep(R) of + error -> error; + LResource -> {LUser, LServer, LResource} + end + end end. +-spec jid_remove_resource(jid()) -> jid(); + (ljid()) -> ljid(). + jid_remove_resource(#jid{} = JID) -> - JID#jid{resource = "", lresource = ""}; -jid_remove_resource({U, S, _R}) -> - {U, S, ""}. + JID#jid{resource = <<"">>, lresource = <<"">>}; +jid_remove_resource({U, S, _R}) -> {U, S, <<"">>}. + +-spec jid_replace_resource(jid(), binary()) -> error | jid(). jid_replace_resource(JID, Resource) -> case resourceprep(Resource) of - error -> error; - LResource -> - JID#jid{resource = Resource, lresource = LResource} + error -> error; + LResource -> + JID#jid{resource = Resource, lresource = LResource} end. +-spec get_iq_namespace(xmlel()) -> binary(). -get_iq_namespace({xmlelement, Name, _Attrs, Els}) when Name == "iq" -> +get_iq_namespace(#xmlel{name = <<"iq">>, children = Els}) -> case xml:remove_cdata(Els) of - [{xmlelement, _Name2, Attrs2, _Els2}] -> - xml:get_attr_s("xmlns", Attrs2); - _ -> - "" + [#xmlel{attrs = Attrs}] -> xml:get_attr_s(<<"xmlns">>, Attrs); + _ -> <<"">> end; -get_iq_namespace(_) -> - "". +get_iq_namespace(_) -> <<"">>. + +%% +-spec(iq_query_info/1 :: +( + Xmlel :: xmlel()) + -> iq_request() | 'reply' | 'invalid' | 'not_iq' +). %% @spec (xmlelement()) -> iq() | reply | invalid | not_iq +iq_query_info(El) -> iq_info_internal(El, request). -iq_query_info(El) -> - iq_info_internal(El, request). +%% +-spec(iq_query_or_response_info/1 :: +( + Xmlel :: xmlel()) + -> iq_request() | iq_reply() | 'reply' | 'invalid' | 'not_iq' +). iq_query_or_response_info(El) -> iq_info_internal(El, any). -iq_info_internal({xmlelement, Name, Attrs, Els}, Filter) when Name == "iq" -> - %% Filter is either request or any. If it is request, any replies - %% are converted to the atom reply. - ID = xml:get_attr_s("id", Attrs), - Type = xml:get_attr_s("type", Attrs), - Lang = xml:get_attr_s("xml:lang", Attrs), - {Type1, Class} = case Type of - "set" -> {set, request}; - "get" -> {get, request}; - "result" -> {result, reply}; - "error" -> {error, reply}; - _ -> {invalid, invalid} - end, - if - Type1 == invalid -> - invalid; - Class == request; Filter == any -> - %% The iq record is a bit strange. The sub_el field is an - %% XML tuple for requests, but a list of XML tuples for - %% responses. - FilteredEls = xml:remove_cdata(Els), - {XMLNS, SubEl} = - case {Class, FilteredEls} of - {request, [{xmlelement, _Name2, Attrs2, _Els2}]} -> - {xml:get_attr_s("xmlns", Attrs2), - hd(FilteredEls)}; - {reply, _} -> - %% Find the namespace of the first non-error - %% element, if there is one. - NonErrorEls = [El || - {xmlelement, SubName, _, _} = El - <- FilteredEls, - SubName /= "error"], - {case NonErrorEls of - [NonErrorEl] -> - xml:get_tag_attr_s("xmlns", NonErrorEl); - _ -> - "" - end, - FilteredEls}; - _ -> - {"", []} - end, - if XMLNS == "", Class == request -> - invalid; - true -> - #iq{id = ID, - type = Type1, - xmlns = XMLNS, - lang = Lang, - sub_el = SubEl} - end; - Class == reply, Filter /= any -> - reply +iq_info_internal(#xmlel{name = <<"iq">>, attrs = Attrs, children = Els}, Filter) -> + ID = xml:get_attr_s(<<"id">>, Attrs), + Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), + {Type, Class} = case xml: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 = xml:remove_cdata(Els), + {XMLNS, SubEl} = case {Class, FilteredEls} of + {request, [#xmlel{attrs = Attrs2}]} -> + {xml:get_attr_s(<<"xmlns">>, Attrs2), hd(FilteredEls)}; + {reply, _} -> + NonErrorEls = [El || #xmlel{name = SubName} = El <- FilteredEls, + SubName /= <<"error">>], + {case NonErrorEls of + [NonErrorEl] -> xml: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. +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"; -iq_type_to_string(_) -> invalid. +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/1 :: +( + IQ :: iq()) + -> xmlel() +). iq_to_xml(#iq{id = ID, type = Type, sub_el = SubEl}) -> - if - ID /= "" -> - {xmlelement, "iq", - [{"id", ID}, {"type", iq_type_to_string(Type)}], SubEl}; - true -> - {xmlelement, "iq", - [{"type", iq_type_to_string(Type)}], 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. - -parse_xdata_submit(El) -> - {xmlelement, _Name, Attrs, Els} = El, - case xml: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 +-spec(parse_xdata_submit/1 :: +( + El :: xmlel()) + -> [{Var::binary(), Values::[binary()]}] + %% + | 'invalid' +). + +parse_xdata_submit(#xmlel{attrs = Attrs, children = Els}) -> + case xml: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. -parse_xdata_fields([], Res) -> - Res; -parse_xdata_fields([{xmlelement, Name, Attrs, SubEls} | Els], Res) -> - case Name of - "field" -> - case xml: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) +-spec(parse_xdata_fields/2 :: +( + 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 xml: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). -parse_xdata_values([], Res) -> - Res; -parse_xdata_values([{xmlelement, Name, _Attrs, SubEls} | Els], Res) -> - case Name of - "value" -> - Val = xml:get_cdata(SubEls), - parse_xdata_values(Els, [Val | Res]); - _ -> - parse_xdata_values(Els, Res) - end; +-spec(parse_xdata_values/2 :: +( + Xmlels :: [xmlel() | cdata()], + Res :: [binary()]) + -> [binary()] +). + +parse_xdata_values([], Res) -> Res; +parse_xdata_values([#xmlel{name = <<"value">>, children = SubEls} | Els], Res) -> + Val = xml:get_cdata(SubEls), + parse_xdata_values(Els, [Val | Res]); parse_xdata_values([_ | Els], Res) -> parse_xdata_values(Els, Res). -rsm_decode(#iq{sub_el=SubEl})-> - rsm_decode(SubEl); -rsm_decode({xmlelement, _,_,_}=SubEl)-> - case xml:get_subtag(SubEl,"set") of - false -> - none; - {xmlelement, "set", _Attrs, SubEls}-> - lists:foldl(fun rsm_parse_element/2, #rsm_in{}, SubEls) - end. - -rsm_parse_element({xmlelement, "max",[], _}=Elem, RsmIn)-> - CountStr = xml:get_tag_cdata(Elem), - {Count, _} = string:to_integer(CountStr), - RsmIn#rsm_in{max=Count}; +-spec rsm_decode(iq() | xmlel()) -> none | rsm_in(). -rsm_parse_element({xmlelement, "before", [], _}=Elem, RsmIn)-> - UID = xml:get_tag_cdata(Elem), - RsmIn#rsm_in{direction=before, id=UID}; +rsm_decode(#iq{sub_el = SubEl}) -> rsm_decode(SubEl); +rsm_decode(#xmlel{} = SubEl) -> + case xml: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({xmlelement, "after", [], _}=Elem, RsmIn)-> +rsm_parse_element(#xmlel{name = <<"max">>, attrs = []} = + Elem, + RsmIn) -> + CountStr = xml:get_tag_cdata(Elem), + {Count, _} = str:to_integer(CountStr), + RsmIn#rsm_in{max = Count}; +rsm_parse_element(#xmlel{name = <<"before">>, + attrs = []} = + Elem, + RsmIn) -> UID = xml:get_tag_cdata(Elem), - RsmIn#rsm_in{direction=aft, id=UID}; - -rsm_parse_element({xmlelement, "index",[], _}=Elem, RsmIn)-> + RsmIn#rsm_in{direction = before, id = UID}; +rsm_parse_element(#xmlel{name = <<"after">>, + attrs = []} = + Elem, + RsmIn) -> + UID = xml:get_tag_cdata(Elem), + RsmIn#rsm_in{direction = aft, id = UID}; +rsm_parse_element(#xmlel{name = <<"index">>, + attrs = []} = + Elem, + RsmIn) -> IndexStr = xml:get_tag_cdata(Elem), - {Index, _} = string:to_integer(IndexStr), - RsmIn#rsm_in{index=Index}; - - -rsm_parse_element(_, RsmIn)-> - RsmIn. - -rsm_encode(#iq{sub_el=SubEl}=IQ,RsmOut)-> - Set = {xmlelement, "set", [{"xmlns", ?NS_RSM}], - lists:reverse(rsm_encode_out(RsmOut))}, - {xmlelement, Name, Attrs, SubEls} = SubEl, - New = {xmlelement, Name, Attrs, [Set | SubEls]}, - IQ#iq{sub_el=New}. - -rsm_encode(none)-> - []; -rsm_encode(RsmOut)-> - [{xmlelement, "set", [{"xmlns", ?NS_RSM}], lists:reverse(rsm_encode_out(RsmOut))}]. -rsm_encode_out(#rsm_out{count=Count, index=Index, first=First, last=Last})-> + {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), + El2 = rsm_encode_last(Last, El), rsm_encode_count(Count, El2). -rsm_encode_first(undefined, undefined, Arr) -> - Arr; +rsm_encode_first(undefined, undefined, Arr) -> Arr; rsm_encode_first(First, undefined, Arr) -> - [{xmlelement, "first",[], [{xmlcdata, First}]}|Arr]; + [#xmlel{name = <<"first">>, attrs = [], + children = [{xmlcdata, First}]} + | Arr]; rsm_encode_first(First, Index, Arr) -> - [{xmlelement, "first",[{"index", i2l(Index)}], [{xmlcdata, First}]}|Arr]. + [#xmlel{name = <<"first">>, + attrs = [{<<"index">>, i2l(Index)}], + children = [{xmlcdata, First}]} + | Arr]. rsm_encode_last(undefined, Arr) -> Arr; rsm_encode_last(Last, Arr) -> - [{xmlelement, "last",[], [{xmlcdata, Last}]}|Arr]. + [#xmlel{name = <<"last">>, attrs = [], + children = [{xmlcdata, Last}]} + | Arr]. -rsm_encode_count(undefined, Arr)-> Arr; -rsm_encode_count(Count, Arr)-> - [{xmlelement, "count",[], [{xmlcdata, i2l(Count)}]} | Arr]. +rsm_encode_count(undefined, Arr) -> Arr; +rsm_encode_count(Count, Arr) -> + [#xmlel{name = <<"count">>, attrs = [], + children = [{xmlcdata, i2l(Count)}]} + | Arr]. -i2l(I) when is_integer(I) -> integer_to_list(I); -i2l(L) when is_list(L) -> L. +i2l(I) when is_integer(I) -> integer_to_binary(I). + +-type tz() :: {binary(), {integer(), integer()}} | {integer(), integer()} | utc. %% Timezone = utc | {Sign::string(), {Hours, Minutes}} | {Hours, Minutes} %% Hours = integer() %% Minutes = integer() -timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}, Timezone) -> +-spec timestamp_to_iso(calendar:datetime(), tz()) -> {binary(), binary()}. + +timestamp_to_iso({{Year, Month, Day}, + {Hour, Minute, Second}}, + Timezone) -> Timestamp_string = - lists:flatten( - io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second])), - Timezone_string = - case Timezone of - utc -> "Z"; - {Sign, {TZh, TZm}} -> - io_lib:format("~s~2..0w:~2..0w", [Sign, TZh, TZm]); - {TZh, TZm} -> - Sign = case TZh >= 0 of - true -> "+"; - false -> "-" - end, - io_lib:format("~s~2..0w:~2..0w", [Sign, abs(TZh),TZm]) - end, - {Timestamp_string, Timezone_string}. - -timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}) -> - lists:flatten( - io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second])). + lists:flatten(io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, Second])), + Timezone_string = case Timezone of + utc -> "Z"; + {Sign, {TZh, TZm}} -> + io_lib:format("~s~2..0w:~2..0w", [Sign, TZh, TZm]); + {TZh, TZm} -> + Sign = case TZh >= 0 of + true -> "+"; + false -> "-" + end, + io_lib:format("~s~2..0w:~2..0w", + [Sign, abs(TZh), TZm]) + end, + {iolist_to_binary(Timestamp_string), iolist_to_binary(Timezone_string)}. + +-spec timestamp_to_iso(calendar:datetime()) -> binary(). + +timestamp_to_iso({{Year, Month, Day}, + {Hour, Minute, Second}}) -> + iolist_to_binary(io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, Second])). + +-spec timestamp_to_xml(calendar:datetime(), tz(), jid(), binary()) -> xmlel(). timestamp_to_xml(DateTime, Timezone, FromJID, Desc) -> - {T_string, Tz_string} = timestamp_to_iso(DateTime, Timezone), + {T_string, Tz_string} = timestamp_to_iso(DateTime, + Timezone), Text = [{xmlcdata, Desc}], From = jlib:jid_to_string(FromJID), - {xmlelement, "delay", - [{"xmlns", ?NS_DELAY}, - {"from", From}, - {"stamp", T_string ++ Tz_string}], - Text}. - %% TODO: Remove this function once XEP-0091 is Obsolete -timestamp_to_xml({{Year, Month, Day}, {Hour, Minute, Second}}) -> - {xmlelement, "x", - [{"xmlns", ?NS_DELAY91}, - {"stamp", lists:flatten( - io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second]))}], - []}. + #xmlel{name = <<"delay">>, + attrs = + [{<<"xmlns">>, ?NS_DELAY}, {<<"from">>, From}, + {<<"stamp">>, <<T_string/binary, Tz_string/binary>>}], + children = Text}. + +-spec timestamp_to_xml(calendar:datetime()) -> xmlel(). + +timestamp_to_xml({{Year, Month, Day}, + {Hour, Minute, Second}}) -> + #xmlel{name = <<"x">>, + attrs = + [{<<"xmlns">>, ?NS_DELAY91}, + {<<"stamp">>, + iolist_to_binary(io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, + Second]))}], + children = []}. + +-spec now_to_utc_string(erlang:timestamp()) -> binary(). now_to_utc_string({MegaSecs, Secs, MicroSecs}) -> {{Year, Month, Day}, {Hour, Minute, Second}} = - calendar:now_to_universal_time({MegaSecs, Secs, MicroSecs}), - lists:flatten( - io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6..0wZ", - [Year, Month, Day, Hour, Minute, Second, MicroSecs])). + calendar:now_to_universal_time({MegaSecs, Secs, + MicroSecs}), + list_to_binary(io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6." + ".0wZ", + [Year, Month, Day, Hour, Minute, Second, + MicroSecs])). + +-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, - lists:flatten( - io_lib:format("~4..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6..0w~s~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second, MicroSecs, Sign, H, M])). + 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..0w-~2..0w-~2..0wT~2..0w:~2..0w:~2..0w.~6." + ".0w~s~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, Second, + MicroSecs, Sign, H, M])). +-spec datetime_string_to_timestamp(binary()) -> undefined | erlang:timestamp(). -% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {MegaSecs, Secs, MicroSecs} datetime_string_to_timestamp(TimeStr) -> case catch parse_datetime(TimeStr) of - {'EXIT', _Err} -> - undefined; - TimeStamp -> - TimeStamp + {'EXIT', _Err} -> undefined; + TimeStamp -> TimeStamp end. parse_datetime(TimeStr) -> - [Date, Time] = string:tokens(TimeStr, "T"), + [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, + 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] = string:tokens(Date, "-"), - Date1 = {list_to_integer(Y), list_to_integer(M), list_to_integer(D)}, + [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 + true -> Date1; + _ -> false end. % hh:mm:ss[.sss]TZD parse_time(Time) -> - case string:str(Time, "Z") of - 0 -> - parse_time_with_timezone(Time); - _ -> - [T | _] = string:tokens(Time, "Z"), - {TT, MS} = parse_time1(T), - {TT, MS, 0, 0} + 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 string:str(Time, "+") of - 0 -> - case string:str(Time, "-") of - 0 -> - false; - _ -> - parse_time_with_timezone(Time, "-") - 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] = string:tokens(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} + <<"-">> -> {TT, MS, -TZH, -TZM}; + <<"+">> -> {TT, MS, TZH, TZM} end. parse_timezone(TZ) -> - [H, M] = string:tokens(TZ, ":"), + [H, M] = str:tokens(TZ, <<":">>), {[H1, M1], true} = check_list([{H, 12}, {M, 60}]), {H1, M1}. parse_time1(Time) -> - [HMS | T] = string:tokens(Time, "."), + [HMS | T] = str:tokens(Time, <<".">>), MS = case T of - [] -> - 0; - [Val] -> - list_to_integer(string:left(Val, 6, $0)) + [] -> 0; + [Val] -> binary_to_integer(str:left(Val, 6, $0)) end, - [H, M, S] = string:tokens(HMS, ":"), - {[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]), + [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 = list_to_integer(L), - if - (V >= 0) and (V =< N) -> - {V, B}; - true -> - {false, false} - end - end, true, 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 decode_base64(binary()) -> binary(). + decode_base64(S) -> - decode1_base64([C || C <- S, - C /= $ , - C /= $\t, - C /= $\n, - C /= $\r]). - -decode1_base64([]) -> - []; -decode1_base64([Sextet1,Sextet2,$=,$=|Rest]) -> - Bits2x6= - (d(Sextet1) bsl 18) bor - (d(Sextet2) bsl 12), - Octet1=Bits2x6 bsr 16, - [Octet1|decode1_base64(Rest)]; -decode1_base64([Sextet1,Sextet2,Sextet3,$=|Rest]) -> - Bits3x6= - (d(Sextet1) bsl 18) bor - (d(Sextet2) bsl 12) bor - (d(Sextet3) bsl 6), - Octet1=Bits3x6 bsr 16, - Octet2=(Bits3x6 bsr 8) band 16#ff, - [Octet1,Octet2|decode1_base64(Rest)]; -decode1_base64([Sextet1,Sextet2,Sextet3,Sextet4|Rest]) -> - Bits4x6= - (d(Sextet1) bsl 18) bor - (d(Sextet2) bsl 12) bor - (d(Sextet3) bsl 6) bor - d(Sextet4), - Octet1=Bits4x6 bsr 16, - Octet2=(Bits4x6 bsr 8) band 16#ff, - Octet3=Bits4x6 band 16#ff, - [Octet1,Octet2,Octet3|decode1_base64(Rest)]; -decode1_base64(_CatchAll) -> - "". - -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; + decode_base64_bin(S, <<>>). + +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. -encode_base64([]) -> - []; -encode_base64([A]) -> - [e(A bsr 2), e((A band 3) bsl 4), $=, $=]; -encode_base64([A,B]) -> - [e(A bsr 2), e(((A band 3) bsl 4) bor (B bsr 4)), e((B band 15) bsl 2), $=]; -encode_base64([A,B,C|Ls]) -> - encode_base64_do(A,B,C, Ls). -encode_base64_do(A,B,C, Rest) -> - BB = (A bsl 16) bor (B bsl 8) bor C, - [e(BB bsr 18), e((BB bsr 12) band 63), - e((BB bsr 6) band 63), e(BB band 63)|encode_base64(Rest)]. - -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}). - %% 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); -ip_to_list({_,_,_,_,_,_,_,_} = Ipv6Address) -> - inet_parse:ntoa(Ipv6Address); %% This function clause could use inet_parse too: -ip_to_list({A,B,C,D}) -> - lists:flatten(io_lib:format("~w.~w.~w.~w",[A,B,C,D])); +ip_to_list(undefined) -> + <<"unknown">>; ip_to_list(IP) -> - lists:flatten(io_lib:format("~w", [IP])). + list_to_binary(inet_parse:ntoa(IP)). + +binary_to_atom(Bin) -> + erlang:binary_to_atom(Bin, utf8). + +binary_to_integer(Bin) -> + list_to_integer(binary_to_list(Bin)). + +binary_to_integer(Bin, Base) -> + list_to_integer(binary_to_list(Bin), Base). + +integer_to_binary(I) -> + list_to_binary(integer_to_list(I)). + +integer_to_binary(I, Base) -> + list_to_binary(erlang:integer_to_list(I, Base)). + +tuple_to_binary(T) -> + iolist_to_binary(tuple_to_list(T)). + +atom_to_binary(A) -> + erlang:atom_to_binary(A, utf8). |