%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2006-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%
%%
%%

-module(orber_nat_SUITE).

-include("test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
-include_lib("orber/src/ifr_objects.hrl").
-include("idl_output/orber_test_server.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming_NamingContextExt.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming_NamingContext.hrl").


-define(default_timeout, ?t:minutes(15)).

-define(match(ExpectedRes,Expr),
	fun() ->
	       AcTuAlReS = (catch (Expr)),
	       case AcTuAlReS of
		   ExpectedRes ->
		       io:format("------ CORRECT RESULT ------~n~p~n",
				 [AcTuAlReS]),
		       AcTuAlReS;
		   _ ->
		       io:format("###### ERROR ERROR ######~nRESULT:  ~p~n",
				 [AcTuAlReS]),
		       ?line exit(AcTuAlReS)
	       end
       end()).

%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([all/1, cases/0, init_all/1, finish_all/1,
	 init_per_testcase/2, fin_per_testcase/2, 
	 nat_ip_address/1, nat_ip_address_multiple/1,
	 nat_ip_address_local/1, nat_ip_address_local_local/1,
	 nat_iiop_port/1, nat_iiop_port_local/1,
	 nat_iiop_port_local_local/1, nat_iiop_ssl_port/1,
	 nat_iiop_ssl_port_local/1]).


%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------

%%-----------------------------------------------------------------
%% Func: all/1
%% Args: 
%% Returns: 
%%-----------------------------------------------------------------
all(doc) -> ["API tests for multi orber interfaces", 
	     "This suite test intra-ORB communication. There are three scenarios:",
	     "* No security at all (multi_orber_api)",
	     "* Two secure orbs using ssl (ssl_multi_orb_api)",
	     "* One secure and one orb with no security. (ssl_multi_orb_api)"];
all(suite) -> {req,
               [mnesia],
               {conf, init_all, cases(), finish_all}}.

cases() ->
    [
     nat_ip_address,
     nat_ip_address_multiple,
     nat_ip_address_local,
     nat_iiop_port,
     nat_iiop_port_local,
     nat_ip_address_local_local,
     nat_iiop_port_local_local,
     nat_iiop_ssl_port,
     nat_iiop_ssl_port_local
    ].

%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------

init_per_testcase(_Case, Config) ->
    Path = code:which(?MODULE),
    code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
    Dog=test_server:timetrap(?default_timeout),
    orber:jump_start([{iiop_port, 0},
		      {flags, 0}]),
    oe_orber_test_server:oe_register(),
    [{watchdog, Dog}|Config].


fin_per_testcase(_Case, Config) ->
    oe_orber_test_server:oe_unregister(),
    orber:jump_stop(),
    Path = code:which(?MODULE),
    code:del_path(filename:join(filename:dirname(Path), "idl_output")),
    Dog = ?config(watchdog, Config),
    test_server:timetrap_cancel(Dog),
    ok.

init_all(Config) ->
    if
	is_list(Config) ->
	    Config;
	true ->
	    exit("Config not a list")
    end.

finish_all(Config) ->
    Config.

%%-----------------------------------------------------------------
%%  API tests for NAT
%%-----------------------------------------------------------------

nat_ip_address(doc) -> ["This case test if the server ORB use the correct",
			"interface when exporting IOR:s"];
nat_ip_address(suite) -> [];
nat_ip_address(_Config) ->
    IP = orber_test_lib:get_host(),
    Loopback = orber_test_lib:get_loopback_interface(),
    {ok, ServerNode, _ServerHost} = 
	?match({ok,_,_}, orber_test_lib:js_node([{flags, ?ORB_ENV_ENABLE_NAT},
						 {nat_ip_address, Loopback}])),
    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
    IOR = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++IP++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {Loopback, ServerPort, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR)),
    ok.

nat_ip_address_multiple(doc) -> ["This case test if the server ORB use the correct",
				 "interface when exporting IOR:s"];
nat_ip_address_multiple(suite) -> [];
nat_ip_address_multiple(_Config) ->
    IP = orber_test_lib:get_host(),
   
    {ok, ServerNode, _ServerHost} = 
	?match({ok,_,_}, orber_test_lib:js_node([{flags, ?ORB_ENV_ENABLE_NAT},
						 {nat_ip_address, {multiple, ["10.0.0.1"]}}])),
    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
    IOR = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++IP++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {"10.0.0.1", ServerPort, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR)),
    ok.

nat_ip_address_local(doc) -> ["This case test if the server ORB use the correct",
			      "interface when exporting IOR:s"];
nat_ip_address_local(suite) -> [];
nat_ip_address_local(_Config) ->
    IP = orber_test_lib:get_host(),
    {ok, ServerNode, _ServerHost} = 
	?match({ok,_,_}, orber_test_lib:js_node([{flags, ?ORB_ENV_ENABLE_NAT},
						 {nat_ip_address, {local, "10.0.0.1", [{IP, "127.0.0.1"}]}}])),
    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
    IOR = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++IP++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {"10.0.0.1", ServerPort, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR)),
    ok.

nat_ip_address_local_local(doc) -> ["This case test if the server ORB use the correct",
			      "interface when exporting IOR:s"];
nat_ip_address_local_local(suite) -> [];
nat_ip_address_local_local(_Config) ->
    IP = orber_test_lib:get_host(),
    Loopback = orber_test_lib:get_loopback_interface(),
    {ok, ServerNode, _ServerHost} = 
	?match({ok,_,_}, orber_test_lib:js_node([{flags, 
						  (?ORB_ENV_LOCAL_INTERFACE bor 
						   ?ORB_ENV_ENABLE_NAT)},
						 {nat_ip_address, {local, "10.0.0.1", [{IP, "10.0.0.2"}]}}])),
    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
    IOR1 = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++IP++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {"10.0.0.2", ServerPort, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR1)),
    IOR2 = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++Loopback++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {"10.0.0.1", ServerPort, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR2)),
    ok.

nat_iiop_port(doc) -> ["This case test if the server ORB use the correct",
		       "port when exporting IOR:s"];
nat_iiop_port(suite) -> [];
nat_iiop_port(_Config) ->
    IP = orber_test_lib:get_host(),
    {ok, ServerNode, _ServerHost} = 
	?match({ok,_,_}, orber_test_lib:js_node([{flags, ?ORB_ENV_ENABLE_NAT},
						 {nat_iiop_port, 42}])),
    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
    IOR = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++IP++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {_IP, 42, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR)),
    ok.

nat_iiop_port_local(doc) -> ["This case test if the server ORB use the correct",
		       "port when exporting IOR:s"];
nat_iiop_port_local(suite) -> [];
nat_iiop_port_local(_Config) ->
    IP = orber_test_lib:get_host(),
    {ok, ServerNode, _ServerHost} = 
	?match({ok,_,_}, orber_test_lib:js_node([{flags, ?ORB_ENV_ENABLE_NAT},
						 {nat_iiop_port, {local, 42, [{4001, 43}]}}])),
    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
    IOR = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++IP++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {_IP, 42, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR)),
    ok.

nat_iiop_port_local_local(doc) -> ["This case test if the server ORB use the correct",
				   "port when exporting IOR:s"];
nat_iiop_port_local_local(suite) -> [];
nat_iiop_port_local_local(_Config) ->
    IP = orber_test_lib:get_host(),
    Loopback = orber_test_lib:get_loopback_interface(),
    {ok, ServerNode, _ServerHost} = 
	?match({ok,_,_}, orber_test_lib:js_node([{flags, 
						  (?ORB_ENV_LOCAL_INTERFACE bor 
						   ?ORB_ENV_ENABLE_NAT)},
						 {ip_address, IP}])),
    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
    orber_test_lib:remote_apply(ServerNode, orber_env, configure_override, [nat_iiop_port, {local, 42, [{ServerPort, 43}]}]),
    IOR1 = ?match(#'IOP_IOR'{},
		 corba:string_to_object("corbaloc::1.2@"++IP++":"++integer_to_list(ServerPort)++"/NameService")),
    ?match({'external', {IP, 43, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR1)),
    {ok, Ref} = ?match({ok, _},
			orber_test_lib:remote_apply(ServerNode, orber, 
						    add_listen_interface, 
						    [Loopback, normal, 10088])),
    IOR2 = ?match(#'IOP_IOR'{},
		  corba:string_to_object("corbaloc::1.2@"++Loopback++":10088/NameService")),
    ?match({'external', {IP, 42, _ObjectKey, _Counter, _TP, _NewHD}}, 
	   iop_ior:get_key(IOR2)),
    ?match(ok, 
	   orber_test_lib:remote_apply(ServerNode, orber, 
				       remove_listen_interface, [Ref])),
    ok.


%%-----------------------------------------------------------------
%%  API tests for ORB to ORB, ssl security depth 1
%%-----------------------------------------------------------------

nat_iiop_ssl_port(doc) -> ["SECURE MULTI ORB API tests (SSL depth 1)", 
			   "Make sure NAT works for SSL"];
nat_iiop_ssl_port(suite) -> [];
nat_iiop_ssl_port(_Config) ->
    case os:type() of
        vxworks ->
	    {skipped, "No SSL-support for VxWorks."};
        _ ->
	    IP = orber_test_lib:get_host(),
	    ServerOptions = orber_test_lib:get_options(iiop_ssl, server, 
						       1, [{iiop_ssl_port, 0},
							   {flags, ?ORB_ENV_ENABLE_NAT},
							   {ip_address, IP}]),
	    ClientOptions = orber_test_lib:get_options(iiop_ssl, client, 
						       1, [{iiop_ssl_port, 0}]),
	    {ok, ServerNode, _ServerHost} = 
		?match({ok,_,_}, orber_test_lib:js_node(ServerOptions)),
	    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
	    SSLServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
	    NATSSLServerPort = SSLServerPort+1,
	    {ok, Ref} = ?match({ok, _},
			       orber_test_lib:remote_apply(ServerNode, orber, 
							   add_listen_interface, 
							   [IP, ssl, NATSSLServerPort])),
	    orber_test_lib:remote_apply(ServerNode, orber_env, configure_override, 
					[nat_iiop_ssl_port, 
					 {local, NATSSLServerPort, [{4001, 43}]}]),
	    
	    {ok, ClientNode, _ClientHost} = 
		?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
	    ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib, 
						   install_test_data, 
						   [ssl])),
	    
	    IOR1 = ?match(#'IOP_IOR'{}, 
			  orber_test_lib:remote_apply(ClientNode, corba, 
						      string_to_object,
						      ["corbaname::1.2@"++IP++":"++
						       integer_to_list(ServerPort)++"/NameService#mamba"])),
    
	    ?match({'external', {_IP, _Port, _ObjectKey, _Counter, _TP, 
				 #host_data{protocol = ssl, 
					    ssl_data = #'SSLIOP_SSL'{port = NATSSLServerPort}}}}, 
		   iop_ior:get_key(IOR1)),
	    ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib, 
						   uninstall_test_data, 
						   [ssl])),
	    ?match(ok, 
		   orber_test_lib:remote_apply(ServerNode, orber, 
					       remove_listen_interface, [Ref])),
	    ok
    end.

nat_iiop_ssl_port_local(doc) -> ["SECURE MULTI ORB API tests (SSL depth 1)", 
				 "Make sure NAT works for SSL"];
nat_iiop_ssl_port_local(suite) -> [];
nat_iiop_ssl_port_local(_Config) ->
    case os:type() of
        vxworks ->
	    {skipped, "No SSL-support for VxWorks."};
        _ ->
	    IP = orber_test_lib:get_host(),
	    ServerOptions = orber_test_lib:get_options(iiop_ssl, server, 
						       1, [{iiop_ssl_port, 0},
							   {flags, 
							    (?ORB_ENV_LOCAL_INTERFACE bor 
							     ?ORB_ENV_ENABLE_NAT)},
							   {ip_address, IP}]),
	    ClientOptions = orber_test_lib:get_options(iiop_ssl, client, 
						       1, [{iiop_ssl_port, 0}]),
	    {ok, ServerNode, _ServerHost} = 
		?match({ok,_,_}, orber_test_lib:js_node(ServerOptions)),
	    ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
	    SSLServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
	    NATSSLServerPort = SSLServerPort+1,
	    {ok, Ref} = ?match({ok, _},
			       orber_test_lib:remote_apply(ServerNode, orber, 
							   add_listen_interface, 
							   [IP, ssl, NATSSLServerPort])),
	    orber_test_lib:remote_apply(ServerNode, orber_env, configure_override, 
					[nat_iiop_ssl_port, 
					 {local, NATSSLServerPort, [{NATSSLServerPort, NATSSLServerPort}]}]),
	    
	    {ok, ClientNode, _ClientHost} = 
		?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
	    ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib, 
						   install_test_data, 
						   [ssl])),
	    
	    IOR1 = ?match(#'IOP_IOR'{}, 
			  orber_test_lib:remote_apply(ClientNode, corba, 
						      string_to_object,
						      ["corbaname::1.2@"++IP++":"++
						       integer_to_list(ServerPort)++"/NameService#mamba"])),
    
	    ?match({'external', {_IP, _Port, _ObjectKey, _Counter, _TP, 
				 #host_data{protocol = ssl, 
					    ssl_data = #'SSLIOP_SSL'{port = NATSSLServerPort}}}}, 
		   iop_ior:get_key(IOR1)),
	    ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib, 
						   uninstall_test_data, 
						   [ssl])),
	    ?match(ok, 
		   orber_test_lib:remote_apply(ServerNode, orber, 
					       remove_listen_interface, [Ref])),
	    ok
    end.

