%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
%%-----------------------------------------------------------------
%% File: orber_cosnaming_utils.erl
%% Modified:
%%
%%-----------------------------------------------------------------
-module(orber_cosnaming_utils).
-include("orber_cosnaming.hrl").
-include("CosNaming.hrl").
-include("CosNaming_NamingContext.hrl").
-include("CosNaming_NamingContextExt.hrl").
-include_lib("orber/include/corba.hrl").
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([query_result/1]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
-export([addresses/1, name/1,
check_addresses/1, check_name/1,
key/1, select_type/1, lookup/1, lookup/2,
escape_string/1, unescape_string/1,
name2string/1, string2name/1]).
%%-----------------------------------------------------------------
%% Records
%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
%% Defines
%%-----------------------------------------------------------------
%% DEFAULT VALUES:
%%
%% IIOP:
%% - port: 2809
%% - iiop version: 1.0
-define(DEF_VERS, {1,0}).
-define(DEF_PORT, 2809).
-define(DEF_KEY, "NameService").
-define(HTTP_DEF_PORT, 80).
%% DEBUG INFO
-define(DEBUG_LEVEL, 5).
%%-----------------------------------------------------------------
%% External interface functions
%%-----------------------------------------------------------------
%% Check a read transaction
query_result({atomic, Qres}) ->
case Qres of
[Hres] ->
Hres#orber_CosNaming.nameindex;
[Hres|Tres] ->
orber:dbg("[~p] orber_cosnaming_utils:query_result(~p);~n"
"Multiple Hits: ~p", [?LINE, Qres, [Hres|Tres]], ?DEBUG_LEVEL),
error;
[] ->
orber:dbg("[~p] orber_cosnaming_utils:query_result();~n"
"No hit", [?LINE], ?DEBUG_LEVEL),
error;
Other ->
orber:dbg("[~p] orber_cosnaming_utils:query_result(~p);~n"
"Mnesia Access Failed ~p", [?LINE, Qres, Other], ?DEBUG_LEVEL),
error
end;
query_result({aborted, Qres}) ->
orber:dbg("[~p] orber_cosnaming_utils:query_result(~p);~n"
"Mnesia Access Aborted", [?LINE, Qres], ?DEBUG_LEVEL),
error;
query_result(What) ->
orber:dbg("[~p] orber_cosnaming_utils:query_result(~p);~n"
"Mnesia Access Failed", [?LINE, What], ?DEBUG_LEVEL),
error.
%%----------------------------------------------------------------------
%% Function : check_addresses
%% Arguments :
%% Description:
%% Returns :
%%----------------------------------------------------------------------
check_addresses(Str) ->
{_, Rest2} = addresses(Str),
case key(Rest2) of
{_, []} ->
ok;
What ->
orber:dbg("[~p] orber_cosnaming_utils:check_addresses(~p);~n"
"Key ~p", [?LINE, Str, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end.
%%----------------------------------------------------------------------
%% Function : check_name
%% Arguments :
%% Description:
%% Returns :
%%----------------------------------------------------------------------
check_name(Str) ->
name(Str).
%%----------------------------------------------------------------------
%% Function : select_type
%% Arguments : A corbaloc/corbaname-string.
%% Description:
%% Returns : A tuple which contain data about what connection we want to use |
%% {'EXCEPTION', #'CosNaming_NamingContextExt_InvalidAddress'{}}
%%----------------------------------------------------------------------
select_type([$c, $o, $r, $b, $a, $l, $o, $c, $:|Rest1]) ->
{Addresses, Rest2} = addresses(Rest1),
case key(Rest2) of
{Key, []} ->
{corbaloc, Addresses, Key};
What ->
orber:dbg("[~p] orber_cosnaming_utils:select_type(~p);~n"
"Key ~p", [?LINE, Rest1, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end;
select_type([$c, $o, $r, $b, $a, $n, $a, $m, $e, $:|Rest1]) ->
{Addresses, Rest2} = addresses(Rest1),
{Key, Rest3} = key(Rest2),
Name = name(Rest3),
{corbaname, Addresses, Key, string2name(Name)};
select_type([$f, $i, $l, $e, $:, $/ |Rest]) ->
file(Rest);
select_type([$f, $t, $p, $:, $/, $/ |Rest]) ->
ftp(Rest);
select_type([$h, $t, $t, $p, $:, $/, $/ |Rest]) ->
http(Rest);
select_type(What) ->
orber:dbg("[~p] orber_cosnaming_utils:select_type(~p);~n"
"Malformed or unsupported type.",
[?LINE, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{}).
%%----------------------------------------------------------------------
%% Function : addresses
%% Arguments : A corbaloc string.
%% Description:
%% Returns : A list of addresses an the remaining part possibly containg
%% a Key and a stringified Name
%%----------------------------------------------------------------------
addresses(Str) ->
addresses(address(protocol, Str, [], []), []).
addresses({false, rir, Rest}, []) ->
{rir, Rest};
addresses({false, Adr, Rest}, Addresses) ->
{lists:reverse([Adr|Addresses]), Rest};
addresses({true, Adr, Rest}, Addresses) ->
addresses(address(protocol, Rest, [], []), [Adr|Addresses]).
%% Which protocol.
address(protocol, [$:|T], [], []) ->
address(version, T, [], [iiop]);
address(protocol, [$i, $i, $o, $p, $:|T], [], []) ->
address(version, T, [], [iiop]);
address(protocol, [$r, $i, $r, $:|T], [], []) ->
{false, rir, T};
address(protocol, What, _, _) ->
orber:dbg("[~p] orber_cosnaming_utils:address(~p);~n"
"Malformed or unsupported protocol.",
[?LINE, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{});
%% Parsed one address, no version found or port found.
address(version, [$,|T], Acc, Previous) ->
{true, lists:reverse([?DEF_PORT, lists:reverse(Acc), ?DEF_VERS|Previous]), T};
address(version, [$/|T], Acc, Previous) ->
{false, lists:reverse([?DEF_PORT, lists:reverse(Acc), ?DEF_VERS|Previous]), T};
%% Found iiop version.
address(version, [$@|T], Acc, Previous) ->
case Acc of
[Minor, $., Major] ->
address(address, T, [], [{Major-$0, Minor-$0}|Previous]);
What ->
orber:dbg("[~p] orber_cosnaming_utils:address(~p);~n"
"Malformed or unsupported version.",
[?LINE, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end;
%% Found no iiop version, switch to port. In this case Acc contains the
%% Host.
address(version, [$:|T], Acc, Previous) ->
case check_ip_version(T, [$:|Acc]) of
false ->
address(port, T, [], [lists:reverse(Acc), ?DEF_VERS|Previous]);
{ok, NewAcc, NewT, Type} ->
address(Type, NewT, [], [lists:reverse(NewAcc), ?DEF_VERS|Previous])
end;
%% Parsed one address, port not found.
address(address, [$,|T], [], Previous) ->
{true, lists:reverse([?DEF_PORT|Previous]), T};
address(address, [$/|T], [], Previous) ->
{false, lists:reverse([?DEF_PORT|Previous]), T};
address(address, [$,|T], Acc, Previous) ->
{true, lists:reverse([?DEF_PORT, lists:reverse(Acc)|Previous]), T};
address(address, [$/|T], Acc, Previous) ->
{false, lists:reverse([?DEF_PORT, lists:reverse(Acc)|Previous]), T};
%% Parsed one address.
address(port, [$/|T], Acc, Previous) ->
case catch list_to_integer(lists:reverse(Acc)) of
Port when is_integer(Port) ->
{false, lists:reverse([Port|Previous]), T};
What ->
orber:dbg("[~p] orber_cosnaming_utils:address(~p);~n"
"Malformed port.", [?LINE, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end;
address(port, [$,|T], Acc, Previous) ->
case catch list_to_integer(lists:reverse(Acc)) of
Port when is_integer(Port) ->
{true, lists:reverse([Port|Previous]), T};
What ->
orber:dbg("[~p] orber_cosnaming_utils:address(~p);~n"
"Malformed port.", [?LINE, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end;
%% EOS, check how far we have reached so far and add necessary default values.
address(version, [], Acc, Previous) ->
{false, lists:reverse([?DEF_PORT, lists:reverse(Acc), ?DEF_VERS|Previous]), []};
address(port, [], [], Previous) ->
{false, lists:reverse([?DEF_PORT|Previous]), []};
address(port, [], Acc, Previous) ->
case catch list_to_integer(lists:reverse(Acc)) of
Port when is_integer(Port) ->
{false, lists:reverse([Port|Previous]), []};
What ->
orber:dbg("[~p] orber_cosnaming_utils:address(~p);~n"
"Malformed port.", [?LINE, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end;
address(address, [], [], Previous) ->
{false, lists:reverse([?DEF_PORT|Previous]), []};
address(address, [], Acc, Previous) ->
{false, lists:reverse([?DEF_PORT, lists:reverse(Acc)|Previous]), []};
%% Found port
address(address, [$:|T], Acc, Previous) ->
case check_ip_version(T, [$:|Acc]) of
false ->
address(port, T, [], [lists:reverse(Acc)|Previous]);
{ok, NewAcc, NewT, Type} ->
address(Type, NewT, [], [lists:reverse(NewAcc)|Previous])
end;
address(Type, [H|T], Acc, Previous) ->
address(Type, T, [H|Acc], Previous).
check_ip_version(T, Acc) ->
case orber_env:ip_version() of
inet ->
false;
inet6 ->
case search_for_delimiter(1, T, Acc, $:) of
{ok, NewAcc, NewT, Type} ->
{ok, NewAcc, NewT, Type};
_ ->
false
end
end.
%% An IPv6 address may look like (x == hex, d == dec):
%% * "0:0:0:0:0:0:10.1.1.1" - x:x:x:x:x:x:d.d.d.d
%% * "0:0:0:0:8:800:200C:417A" - x:x:x:x:x:x:x:x
%% We cannot allow compressed addresses (::10.1.1.1) since we it is not
%% possible to know if the last part is a port number or part of the address.
search_for_delimiter(7, [], Acc, $:) ->
{ok, Acc, [], address};
search_for_delimiter(9, [], Acc, $.) ->
{ok, Acc, [], address};
search_for_delimiter(_, [], _, _) ->
false;
search_for_delimiter(7, [$/|T], Acc, $:) ->
{ok, Acc, [$/|T], address};
search_for_delimiter(9, [$/|T], Acc, $.) ->
{ok, Acc, [$/|T], address};
search_for_delimiter(_, [$/|_T], _Acc, _) ->
false;
search_for_delimiter(7, [$,|T], Acc, $:) ->
{ok, Acc, [$,|T], address};
search_for_delimiter(9, [$,|T], Acc, $.) ->
{ok, Acc, [$,|T], address};
search_for_delimiter(_, [$,|_T], _Acc, _) ->
false;
search_for_delimiter(7, [$:|T], Acc, $:) ->
{ok, Acc, T, port};
search_for_delimiter(9, [$:|T], Acc, $.) ->
{ok, Acc, T, port};
search_for_delimiter(N, [$:|T], Acc, $:) ->
search_for_delimiter(N+1, T, [$:|Acc], $:);
search_for_delimiter(N, [$.|T], Acc, $.) when N > 6, N < 9 ->
search_for_delimiter(N+1, T, [$.|Acc], $.);
search_for_delimiter(6, [$.|T], Acc, $:) ->
search_for_delimiter(7, T, [$.|Acc], $.);
search_for_delimiter(N, [H|T], Acc, LookingFor) ->
search_for_delimiter(N, T, [H|Acc], LookingFor).
%%----------------------------------------------------------------------
%% Function : key
%% Arguments : A string which contain a Key we want to use and, if defined,
%% stringified NameComponent sequence.
%% Description:
%% Returns : The Key and the remaining part, i.e., a stringified
%% NameComponent sequence.
%%----------------------------------------------------------------------
key(Str) ->
key(Str, []).
key([], []) ->
{?DEF_KEY, []};
key([], Acc) ->
{lists:reverse(Acc), []};
key([$#|T], []) ->
{?DEF_KEY, T};
key([$#|T], Acc) ->
{lists:reverse(Acc), T};
key([$/|T], []) ->
key(T, []);
key([H|T], Acc) ->
key(T, [H|Acc]).
%%----------------------------------------------------------------------
%% Function : name
%% Arguments : A string describing a NameComponent sequence.
%% Description:
%% Returns : The input string |
%% {'EXCEPTION', #'CosNaming_NamingContext_InvalidName'{}}
%%----------------------------------------------------------------------
name(Str) ->
name(Str, []).
name([], Acc) ->
lists:reverse(Acc);
name([$., $/|_T], _) ->
corba:raise(#'CosNaming_NamingContext_InvalidName'{});
name([$/, $/|_T], _) ->
corba:raise(#'CosNaming_NamingContext_InvalidName'{});
name([$/|T], []) ->
name(T, []);
name([H|T], Acc) ->
name(T, [H|Acc]).
%%----------------------------------------------------------------------
%% Function : file
%% Arguments : A string describing connection parameters.
%% Description:
%% Returns : A tuple consisting of data extracted from the given string.
%%----------------------------------------------------------------------
file(File) ->
{file, File}.
%%----------------------------------------------------------------------
%% Function : ftp
%% Arguments : A string describing connection parameters.
%% Description:
%% Returns : A tuple consisting of data extracted from the given string.
%%----------------------------------------------------------------------
ftp(Address) ->
%% Perhaps we should run some checks here?
{ftp, Address}.
%%----------------------------------------------------------------------
%% Function : http
%% Arguments : A string describing connection parameters.
%% Description:
%% Returns : A tuple consisting of data extracted from the given string.
%%----------------------------------------------------------------------
http(Address) ->
case string:tokens(Address, ":") of
[Host, Rest] ->
%% At his stage we know that address contains a Port number.
{Port, Key} = split_to_slash(Rest, []),
case catch list_to_integer(Port) of
PortInt when is_integer(PortInt) ->
{http, Host, PortInt, Key};
_ ->
orber:dbg("[~p] orber_cosnaming_utils:http(~p);~n"
"Malformed key; should be http://Host:Port/path/name.html~n"
"or http://Host/path/name.html",
[?LINE, Address], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end;
[Address] ->
%% Use default port
{Host, Key} = split_to_slash(Address, []),
{http, Host, ?HTTP_DEF_PORT, Key};
_What ->
orber:dbg("[~p] orber_cosnaming_utils:http(~p);~n"
"Malformed key; should be http://Host:Port/path/name.html~n"
"or http://Host/path/name.html",
[?LINE, Address], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{})
end.
split_to_slash([], _Acc) ->
orber:dbg("[~p] orber_cosnaming_utils:split_to_slash();~n"
"No Key given Host:Port/Key.html", [?LINE], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{});
split_to_slash([$/|Rest], Acc) ->
{lists:reverse(Acc), [$/|Rest]};
split_to_slash([H|T], Acc) ->
split_to_slash(T, [H|Acc]).
%%----------------------------------------------------------------------
%% Function : lookup
%% Arguments : A tuple which contain data about what connection we want to use.
%% Description:
%% Returns : Object |
%% {'EXCEPTION', E}
%%----------------------------------------------------------------------
lookup(Data) ->
lookup(Data, []).
lookup({corbaname, rir, _Key, []}, Ctx) ->
%% If no object key supplied NameService is defined to be default.
corba:resolve_initial_references("NameService", Ctx);
lookup({corbaname, rir, Key, Name}, Ctx) ->
NS = corba:resolve_initial_references(Key, Ctx),
'CosNaming_NamingContext':resolve(NS, Ctx, Name);
lookup({corbaloc, rir, Key}, Ctx) ->
corba:resolve_initial_references(Key, Ctx);
lookup({corbaname, [], _Key, _Name}, _Ctx) ->
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{});
lookup({corbaname, Addresses, Key, ""}, Ctx) ->
%% Not Name-string defined, which is the same as corbaloc.
lookup({corbaloc, Addresses, Key}, Ctx);
lookup({corbaname, [[iiop, Vers, Host, Port]|Addresses], Key, Name}, Ctx) ->
NS = iop_ior:create_external(Vers, key2id(Key), Host, Port, Key),
case catch 'CosNaming_NamingContext':resolve(NS, Ctx, Name) of
{'EXCEPTION', _} ->
lookup({corbaname, Addresses, Key, Name}, Ctx);
Obj ->
Obj
end;
lookup({corbaname, [_|Addresses], Key, Name}, Ctx) ->
lookup({corbaname, Addresses, Key, Name}, Ctx);
lookup({corbaloc, [], _Key}, _Ctx) ->
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{});
lookup({corbaloc, [[iiop, Vers, Host, Port]|Addresses], Key}, Ctx) ->
ObjRef = iop_ior:create_external(Vers, key2id(Key), Host, Port, Key),
OldVal = put(orber_forward_notify, true),
case catch corba_object:non_existent(ObjRef, Ctx) of
{location_forward, Result} ->
put(orber_forward_notify, OldVal),
Result;
false ->
put(orber_forward_notify, OldVal),
ObjRef;
true ->
put(orber_forward_notify, OldVal),
lookup({corbaloc, Addresses, Key}, Ctx);
_ ->
%% May be located on a version using '_not_existent'
%% see CORBA2.3.1 page 15-34 try again.
case catch corba_object:not_existent(ObjRef, Ctx) of
{location_forward, Result} ->
put(orber_forward_notify, OldVal),
Result;
false ->
put(orber_forward_notify, OldVal),
ObjRef;
_ ->
put(orber_forward_notify, OldVal),
lookup({corbaloc, Addresses, Key}, Ctx)
end
end;
lookup({corbaloc, [_|Addresses], Key}, Ctx) ->
lookup({corbaloc, Addresses, Key}, Ctx);
lookup({file, File}, _Ctx) ->
case file:read_file(File) of
{ok, IOR} ->
binary_to_list(IOR);
{error, Reason} ->
orber:dbg("[~p] orber_cosnaming_utils:lookup(~p);~n"
"Failed to access file: ~p.",
[?LINE, File, Reason], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContext_InvalidName'{})
end;
lookup({http, Host, Port, Key}, _Ctx) ->
SetupTimeout = orber:iiop_setup_connection_timeout(),
SendTimeout = orber:iiop_timeout(),
{ok, Socket} = create_connection(Host, Port, SetupTimeout),
Request = "GET " ++ Key ++ " HTTP/1.0\r\n\r\n",
case gen_tcp:send(Socket, Request) of
ok ->
receive_msg(Socket, [], SendTimeout);
{error, Reason} ->
orber:dbg("[~p] orber_cosnaming_utils:lookup();~n"
"Failed to send request: ~p.",
[?LINE, Reason], ?DEBUG_LEVEL),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
end;
lookup({ftp, _Address}, _Ctx) ->
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{});
lookup(_, _Ctx) ->
corba:raise(#'CosNaming_NamingContextExt_InvalidAddress'{}).
receive_msg(Socket, Acc, Timeout) ->
receive
{tcp_closed, Socket} ->
[_Header, Body] = re:split(Acc,"\r\n\r\n",[{return,list}]),
Body;
{tcp, Socket, Response} ->
receive_msg(Socket, Acc ++ Response, Timeout);
{tcp_error, Socket, Reason} ->
orber:dbg("[~p] orber_cosnaming_utils:receive_msg();~n"
"connection failed: ~p.",
[?LINE, Reason], ?DEBUG_LEVEL),
gen_tcp:close(Socket),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
after Timeout ->
gen_tcp:close(Socket),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
end.
create_connection(Host, Port, Timeout) ->
case gen_tcp:connect(Host,Port,[{packet,0},{reuseaddr,true}], Timeout) of
{ok,Socket} ->
{ok,Socket};
Error ->
orber:dbg("[~p] orber_cosnaming_utils:create_connection(~p, ~p, ~p);~n"
"Reason: ~p",
[?LINE, Host, Port, Timeout, Error], ?DEBUG_LEVEL),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
end.
%%----------------------------------------------------------------------
%% Function : key2id
%% Arguments : An objectkey (e.g. NameService)
%% Description:
%% Returns : The associated IFR-id
%%----------------------------------------------------------------------
key2id(Key) ->
%% We need this test to avoid returning an exit if an XX:typeID()
%% fails (e.g. the module doesn't exist).
case catch key2id_helper(Key) of
{ok, Id} ->
Id;
_ ->
""
end.
key2id_helper("NameService") ->
{ok, 'CosNaming_NamingContext':typeID()};
key2id_helper("RootPOA") ->
{ok, "IDL:omg.org/PortableServer/POA:1.0"};
key2id_helper("POACurrent") ->
{ok, "IDL:omg.org/PortableServer/Current:1.0"};
key2id_helper("InterfaceRepository") ->
{ok, "IDL:omg.org/CORBA/Repository:1.0"};
key2id_helper("TradingService") ->
{ok, "IDL:omg.org/CosTrading/Lookup:1.0"};
key2id_helper("TransactionCurrent") ->
{ok, "IDL:omg.org/CosTransactions/Current:1.0"};
key2id_helper("DynAnyFactory") ->
{ok, "IDL:omg.org/DynamicAny/DynAnyFactory:1.0"};
key2id_helper("ORBPolicyManager") ->
{ok, "IDL:omg.org/CORBA/PolicyManager:1.0"};
key2id_helper("PolicyCurrent") ->
{ok, "IDL:omg.org/CORBA/PolicyCurrent:1.0"};
key2id_helper("NotificationService") ->
{ok, "IDL:omg.org/CosNotifyChannelAdmin/EventChannelFactory:1.0"};
key2id_helper("TypedNotificationService") ->
{ok, "IDL:omg.org/CosTypedNotifyChannelAdmin::TypedEventChannelFactory:1.0"};
key2id_helper("CodecFactory") ->
{ok, "IDL:omg.org/IOP/CodecFactory:1.0"};
key2id_helper("PICurrent") ->
{ok, "IDL:omg.org/PortableInterceptors/Current:1.0"};
%% Should we use SecurityLevel1 instead?? This key can be either.
key2id_helper("SecurityCurrent") ->
{ok, "IDL:omg.org/SecurityLevel2/Current:1.0"};
%% Unknown - use the empty string. Might not work for all other ORB's but it's
%% the only option we've got.
key2id_helper(_) ->
{ok, ""}.
%%----------------------------------------------------------------------
%% Function : name2string
%% Arguments : A sequence of NameComponents
%% Description:
%% Returns : A string describing the sequence.
%%----------------------------------------------------------------------
name2string(Name) ->
name2string(lists:reverse(Name), []).
name2string([], Acc) ->
lists:flatten(Acc);
name2string([#'CosNaming_NameComponent'{id="", kind=""}], Acc) ->
name2string([], [$.|Acc]);
name2string([#'CosNaming_NameComponent'{id=ID, kind=""}], Acc) ->
name2string([], [convert_reserved(ID)|Acc]);
name2string([#'CosNaming_NameComponent'{id=ID, kind=Kind}], Acc) ->
name2string([], [convert_reserved(ID), $., convert_reserved(Kind)|Acc]);
name2string([#'CosNaming_NameComponent'{id="", kind=""}|T], Acc) ->
name2string(T, [$/, $.|Acc]);
name2string([#'CosNaming_NameComponent'{id=ID, kind=""}|T], Acc) ->
name2string(T, [$/, convert_reserved(ID)|Acc]);
name2string([#'CosNaming_NameComponent'{id=ID, kind=Kind}|T], Acc) ->
name2string(T, [$/, convert_reserved(ID), $., convert_reserved(Kind)|Acc]);
name2string(What, Acc) ->
orber:dbg("[~p] orber_cosnaming_utils:name2string(~p)~n"
"Malformed NameComponent: ~p",
[?LINE, Acc, What], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContext_InvalidName'{}).
%% '/' and '.' are reserved as separators but can be overridden by using '\'.
convert_reserved([]) ->
[];
convert_reserved([$/|T]) ->
[$\\, $/|convert_reserved(T)];
convert_reserved([$.|T]) ->
[$\\, $.|convert_reserved(T)];
convert_reserved([$\\, H|T]) ->
[$\\, H|convert_reserved(T)];
convert_reserved([H|T]) ->
[H|convert_reserved(T)].
%%----------------------------------------------------------------------
%% Function : string2name
%% Arguments : A string describing a sequence of NameComponents.
%% Description:
%% Returns : A sequence of NameComponents
%%----------------------------------------------------------------------
string2name([]) ->
[];
string2name(Str) ->
{NC, Rest} = get_NC(id, Str, [], []),
[NC|string2name(Rest)].
get_NC(id, [], ID, _Kind) ->
{#'CosNaming_NameComponent'{id=lists:reverse(ID), kind=""}, []};
get_NC(kind, [], ID, Kind) ->
{#'CosNaming_NameComponent'{id=lists:reverse(ID), kind=lists:reverse(Kind)}, []};
%% // is not allowed; must be /./
get_NC(id, [$/|_T], [], _) ->
orber:dbg("[~p] orber_cosnaming_utils:get_NC();~n"
"'//' not allowed, use '/./'", [?LINE], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContext_InvalidName'{});
get_NC(id, [$., $/|T], [], _) ->
{#'CosNaming_NameComponent'{id="", kind=""}, T};
%% End of this ID/Kind; in this case kind eq. "".
get_NC(id, [$/|T], ID, _Kind) ->
{#'CosNaming_NameComponent'{id=lists:reverse(ID), kind=""}, T};
get_NC(kind, [$/|T], ID, Kind) ->
{#'CosNaming_NameComponent'{id=lists:reverse(ID), kind=lists:reverse(Kind)}, T};
%% ID exist but it's not allowed to write "id1./id2.kind2".
get_NC(id, [$., $/|_T], _, _) ->
orber:dbg("[~p] orber_cosnaming_utils:get_NC();~n"
"'id1./id2.kind2' not allowed, use 'id1/id2.kind2'",
[?LINE], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContext_InvalidName'{});
get_NC(id, [$\\, $., H|T], ID, Kind) ->
get_NC(id, T, [H, $.|ID], Kind);
get_NC(id, [$\\, $/, H|T], ID, Kind) ->
get_NC(id, T, [H, $/|ID], Kind);
get_NC(kind, [$\\, $., H|T], ID, Kind) ->
get_NC(kind, T, ID, [H|Kind]);
get_NC(kind, [$\\, $/, H|T], ID, Kind) ->
get_NC(kind, T, ID, [H|Kind]);
get_NC(id, [$.|T], ID, Kind) ->
get_NC(kind, T, ID, Kind);
get_NC(id, [H|T], ID, Kind) ->
get_NC(id, T, [H|ID], Kind);
get_NC(kind, [H|T], ID, Kind) ->
get_NC(kind, T, ID, [H|Kind]);
get_NC(Type, Data, ID, Kind) ->
orber:dbg("[~p] orber_cosnaming_utils:get_NC(~p, ~p, ~p, ~p);~n"
"Unknown", [?LINE, Type, Data, ID, Kind], ?DEBUG_LEVEL),
corba:raise(#'CosNaming_NamingContext_InvalidName'{}).
%% Converts \< to '%3c'
escape_string(Str) ->
escape_string(Str, []).
escape_string([], Acc) ->
lists:reverse(Acc);
escape_string([$\\, Char |T], Acc) ->
escape_string(T, [code_character(16#0f band Char),
code_character(16#0f band (Char bsr 4)),$%|Acc]);
escape_string([Char|T], Acc) ->
escape_string(T, [Char|Acc]).
code_character(N) when N < 10 ->
$0 + N;
code_character(N) ->
$a + (N - 10).
%% Converts '%3c' to \<
unescape_string(Str) ->
unescape_string(Str, []).
unescape_string([], Acc) ->
lists:reverse(Acc);
unescape_string([$%, H1, H2 |T], Acc) ->
I1 = hex2int(H1),
I2 = hex2int(H2),
I = I1 * 16 + I2,
unescape_string(T, [I, $\\|Acc]);
unescape_string([H|T], Acc) ->
unescape_string(T, [H|Acc]).
hex2int(H) when H >= $a ->
10 + H - $a;
hex2int(H) when H >= $A ->
10 + H -$A;
hex2int(H) ->
H - $0.
%%-------------------------- END OF MODULE -----------------------------