summaryrefslogtreecommitdiff
path: root/src/jlib.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/jlib.erl')
-rw-r--r--src/jlib.erl1156
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).