%%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov %%% @doc %%% JID processing library %%% @end %%% Created : 24 Nov 2015 by Evgeny Khramtsov %%% %%% %%% 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(jid). %% API -export([start/0, make/1, make/3, split/1, from_string/1, to_string/1, is_nodename/1, nodeprep/1, nameprep/1, resourceprep/1, tolower/1, remove_resource/1, replace_resource/2]). -include("jlib.hrl"). -export_type([jid/0]). %%%=================================================================== %%% API %%%=================================================================== -spec start() -> ok. start() -> SplitPattern = binary:compile_pattern([<<"@">>, <<"/">>]), catch ets:new(jlib, [named_table, protected, set, {keypos, 1}]), ets:insert(jlib, {string_to_jid_pattern, SplitPattern}), ok. -spec make(binary(), binary(), binary()) -> jid() | error. make(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 end. -spec make({binary(), binary(), binary()}) -> jid() | error. make({User, Server, Resource}) -> make(User, Server, Resource). %% This is the reverse of make_jid/1 -spec split(jid()) -> {binary(), binary(), binary()} | error. split(#jid{user = U, server = S, resource = R}) -> {U, S, R}; split(_) -> error. -spec from_string([binary()|string()]) -> jid() | error. from_string(S) when is_list(S) -> %% We do not accept list because we want to enforce good practice of %% using binaries for string. However, we do not let it crash to avoid %% losing associated ets table. {error, need_jid_as_binary}; from_string(S) when is_binary(S) -> SplitPattern = ets:lookup_element(jlib, string_to_jid_pattern, 2), Size = size(S), End = Size-1, case binary:match(S, SplitPattern) of {0, _} -> error; {End, _} -> error; {Pos1, _} -> case binary:at(S, Pos1) of $/ -> make(<<>>, binary:part(S, 0, Pos1), binary:part(S, Pos1+1, Size-Pos1-1)); _ -> Pos1N = Pos1+1, case binary:match(S, SplitPattern, [{scope, {Pos1+1, Size-Pos1-1}}]) of {End, _} -> error; {Pos1N, _} -> error; {Pos2, _} -> case binary:at(S, Pos2) of $/ -> make(binary:part(S, 0, Pos1), binary:part(S, Pos1+1, Pos2-Pos1-1), binary:part(S, Pos2+1, Size-Pos2-1)); _ -> error end; _ -> make(binary:part(S, 0, Pos1), binary:part(S, Pos1+1, Size-Pos1-1), <<>>) end end; _ -> make(<<>>, S, <<>>) end. -spec to_string(jid() | ljid()) -> binary(). to_string(#jid{user = User, server = Server, resource = Resource}) -> to_string({User, Server, Resource}); to_string({N, S, R}) -> Node = iolist_to_binary(N), Server = iolist_to_binary(S), Resource = iolist_to_binary(R), S1 = case Node of <<"">> -> <<"">>; _ -> <> end, S2 = <>, S3 = case Resource of <<"">> -> S2; _ -> <> end, S3. -spec is_nodename(binary()) -> boolean(). is_nodename(Node) -> N = nodeprep(Node), (N /= error) and (N /= <<>>). -define(LOWER(Char), if Char >= $A, Char =< $Z -> Char + 32; true -> Char end). -spec nodeprep(binary()) -> binary() | error. nodeprep("") -> <<>>; nodeprep(S) when byte_size(S) < 1024 -> R = stringprep:nodeprep(S), if byte_size(R) < 1024 -> R; true -> error end; nodeprep(_) -> error. -spec nameprep(binary()) -> binary() | error. nameprep(S) when byte_size(S) < 1024 -> R = stringprep:nameprep(S), if byte_size(R) < 1024 -> R; true -> error end; nameprep(_) -> error. -spec resourceprep(binary()) -> binary() | error. resourceprep(S) when byte_size(S) < 1024 -> R = stringprep:resourceprep(S), if byte_size(R) < 1024 -> R; true -> error end; resourceprep(_) -> error. -spec tolower(jid() | ljid()) -> error | ljid(). tolower(#jid{luser = U, lserver = S, lresource = R}) -> {U, S, R}; 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 end. -spec remove_resource(jid()) -> jid(); (ljid()) -> ljid(). remove_resource(#jid{} = JID) -> JID#jid{resource = <<"">>, lresource = <<"">>}; remove_resource({U, S, _R}) -> {U, S, <<"">>}. -spec replace_resource(jid(), binary()) -> error | jid(). replace_resource(JID, Resource) -> case resourceprep(Resource) of error -> error; LResource -> JID#jid{resource = Resource, lresource = LResource} end. %%%=================================================================== %%% Internal functions %%%===================================================================