%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%
%%
%%--------------------------------------------------------------------
%% File: orber_iiop_inrequest.erl
%% 
%% Description:
%%    This file contains the handling of incomming requests
%%
%%-----------------------------------------------------------------
-module(orber_iiop_inrequest).

-include_lib("orber/src/orber_iiop.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/include/orber_pi.hrl").

%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([start/5, start_fragment_collector/8]).

%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
-export([handle_message/5, fragment_collector/8]).

%%-----------------------------------------------------------------
%% Macros
%%-----------------------------------------------------------------
-define(DEBUG_LEVEL, 8).


%%-----------------------------------------------------------------
%% External interface functions
%%-----------------------------------------------------------------
start(GIOPHdr, Message, Type, Socket, Env) ->
    spawn_link(orber_iiop_inrequest, handle_message, 
	       [GIOPHdr, Message, Type, Socket, Env]).

start_fragment_collector(GIOPHdr, Message, Type, Socket, ReqId, Proxy, MaxFrags, Env) ->
    spawn_link(orber_iiop_inrequest, fragment_collector, 
	       [GIOPHdr, Message, Type, Socket, ReqId, Proxy, MaxFrags, Env]).

%%-----------------------------------------------------------------
%% Internal functions
%%-----------------------------------------------------------------

%%-----------------------------------------------------------------
%% Func: fragment_collector/4
%%-----------------------------------------------------------------
fragment_collector(GIOPHdr, Bytes, SocketType, Socket, ReqId, Proxy, MaxFrags, Env) ->
    case catch collect(Proxy, [], GIOPHdr#giop_message.byte_order, ReqId, 
		       MaxFrags, 0) of
	{ok, Buffer} ->
	    NewGIOP = GIOPHdr#giop_message
			{message = list_to_binary([GIOPHdr#giop_message.message|Buffer])},
	    %% NOTE, the third argument to dec_message_header must be complete
	    %% message (i.e. AllBytes), otherwise we cannot handle indirection.
	    case handle_message(NewGIOP, list_to_binary([Bytes| Buffer]), 
				SocketType, Socket, Env) of
		message_error ->
		    Proxy ! {message_error, self(), ReqId},
		    ok;
		_ ->
		    ok
	    end;
	ok ->
	    ok;
	{'EXCEPTION', E} ->
	    Proxy ! {message_error, self(), ReqId},
	    Reply = marshal_exception(Env, ReqId, E, enc_reply),
	    orber_socket:write(SocketType, Socket, Reply)
    end.



collect(_Proxy, _Buffer, _ByteOrder, _ReqId, MaxFrags, MaxFrags) ->
    orber:dbg("[~p] ~p:collect(~p)~nMax fragments limit reached.", 
	      [?LINE, ?MODULE, MaxFrags], ?DEBUG_LEVEL),
    {'EXCEPTION', #'IMP_LIMIT'{completion_status=?COMPLETED_NO}};
collect(Proxy, Buffer, ByteOrder, ReqId, MaxFrags, FragCounter) ->
    receive
        {Proxy, #giop_message{byte_order = ByteOrder,
			      message    = Message,
			      fragments  = true} = GIOPHdr} ->
	    {_, #fragment_header{request_id=ReqId}, FragBody, _, _} = 
		cdr_decode:dec_message_header(null, GIOPHdr, Message),
            collect(Proxy, [FragBody | Buffer], ByteOrder, ReqId, 
		    MaxFrags, FragCounter+1);
	{Proxy, #giop_message{byte_order = ByteOrder,
			      message = Message,
			      fragments = false} = GIOPHdr} ->
	    {_, #fragment_header{request_id=ReqId}, FragBody, _, _} = 
		cdr_decode:dec_message_header(null, GIOPHdr, Message),
	    {ok, lists:reverse([FragBody | Buffer])};
	{Proxy, GIOPHdr, _Data, _} ->
	    orber:dbg("[~p] orber_iiop_inrequest:collect(~p, ~p)~n"
		      "Incorrect Fragment. Might be different byteorder.", 
		      [?LINE, ByteOrder, GIOPHdr], ?DEBUG_LEVEL),
	    {'EXCEPTION', #'MARSHAL'{completion_status=?COMPLETED_NO}};
	{Proxy, cancel_request_header} ->
	    ok;
	Other ->
	    orber:dbg("[~p] ~p:collect(~p)~n"
		      "Unable to collect all fragments: ~p", 
		      [?LINE, ?MODULE, Buffer, Other], ?DEBUG_LEVEL),
	    {'EXCEPTION', #'MARSHAL'{completion_status=?COMPLETED_NO}}
    end.


%%-----------------------------------------------------------------
%% Func: handle_message/4
%%-----------------------------------------------------------------
handle_message(GIOPHdr, Message, SocketType, Socket, Env) ->
    %% Warning. We shouldn't set the flags like this here. But, for now, we'll
    %% do it due to performance reasons.
    put(oe_orber_flags, Env#giop_env.flags),
    case catch cdr_decode:dec_message_header(null, GIOPHdr, Message) of
	Hdr when is_record(Hdr, cancel_request_header) ->
            %% We just skips this message for the moment, the standard require that 
	    %% the client handles the reply anyway.
	    message_error;
	{location_forward, Object, ReqId, Version, OldObj} ->
	    Reply = call_interceptors_out(Env#giop_env{version = Version}, 
					  ReqId, [Object], OldObj,
					  'location_forward', 
					  "location_forward",
					  {{'tk_objref', "", ""}, [],[]}),
	    orber_socket:write(SocketType, Socket, Reply);
	{object_forward, Object, ReqId, Version, _OldObj} ->
	    Reply = handle_locate_request(Env#giop_env{version = Version}, 
					  {object_forward, Object, ReqId}),
	    orber_socket:write(SocketType, Socket, Reply);
	{Version, Hdr} when is_record(Hdr, locate_request_header) ->
	    Reply = handle_locate_request(Env#giop_env{version = Version}, Hdr),
	    orber_socket:write(SocketType, Socket, Reply);
	{Version, ReqHdr, Rest, Len, ByteOrder} when is_record(ReqHdr, request_header) ->
	    handle_request(Env#giop_env{version = Version}, ReqHdr, Rest, Len, 
			   ByteOrder, SocketType, Socket, Message);
	Other ->
	    %% This cluase takes care of all erranous messages.
	    orber:dbg("[~p] orber_iiop_inrequest:handle_message(~p)~n"
		      "Decoding Msg Header failed: ~p", 
		      [?LINE, Message, Other], ?DEBUG_LEVEL),
	    Reply = cdr_encode:enc_message_error(Env),
	    orber_socket:write(SocketType, Socket, Reply),
	    message_error
   end.


send_reply(oneway, _SocketType, _Socket) ->
    ok;
send_reply(Reply, SocketType, Socket) ->
    orber_socket:write(SocketType, Socket, Reply).

%%-----------------------------------------------------------------
%% Func: handle_request
%%-----------------------------------------------------------------
handle_request(#giop_env{interceptors = false} = Env, ReqHdr, Rest, Len, ByteOrder, 
	       SocketType, Socket, Message) ->
    NewEnv = check_context(ReqHdr#request_header.service_context, [], Env),
    case decode_body(NewEnv, ReqHdr, Rest, Len, ByteOrder, Message, enc_reply) of
	{error, E} ->
	    orber_socket:write(SocketType, Socket, E);
	{NewEnv2, Hdr, Par, TypeCodes} ->
	    Result = invoke_request(Hdr, Par, SocketType, TypeCodes, Env),
	    Reply  = evaluate(NewEnv2, Hdr, Result, TypeCodes, 
			      enc_reply, 'no_exception'),
	    send_reply(Reply, SocketType, Socket)
    end;
handle_request(Env, ReqHdr, Rest, Len, ByteOrder, SocketType, Socket, Message) ->
    NewEnv = check_context(ReqHdr#request_header.service_context, [], Env),
    case catch call_interceptors(SocketType, NewEnv, ReqHdr, 
				 Rest, Len, ByteOrder, Message) of
	{error, E} ->
	    %% Failed to decode body.
	    orber_socket:write(SocketType, Socket, E);
	{'EXCEPTION', Exc} ->
	    orber:dbg("[~p] orber_iiop_inrequest:handle_message(~p)~n"
		      "Invoking the interceptors resulted in: ~p", 
		      [?LINE, Message, Exc], ?DEBUG_LEVEL),
	    Reply = marshal_exception(NewEnv, 
				      ReqHdr#request_header.request_id, 
				      Exc, enc_reply),
	    orber_socket:write(SocketType, Socket, Reply);
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_iiop_inrequest:handle_message(~p)~n"
		      "Invoking the interceptors resulted in: ~p", 
		      [?LINE, ReqHdr, R], ?DEBUG_LEVEL),
	    Reply = marshal_exception(NewEnv, 
				      ReqHdr#request_header.request_id,
				      #'MARSHAL'{completion_status=?COMPLETED_MAYBE}, 
				      enc_reply),
	    orber_socket:write(SocketType, Socket, Reply);
	Reply ->
	    send_reply(Reply, SocketType, Socket)
    end.

check_context([], [], Env) ->
    Env;
check_context([], Acc, Env) ->
    Env#giop_env{ctx = Acc};
check_context([#'CSI_SASContextBody'
	       {label = ?CSI_MsgType_MTEstablishContext, 
		value = #'CSI_EstablishContext'
		{client_context_id = _Id, 
		 authorization_token = _AuthToken,
		 identity_token = _IdToken, 
		 client_authentication_token = _CAuthToken}}|Rest], Acc, Env) ->
    check_context(Rest, [#'IOP_ServiceContext'
			 {context_id=?IOP_SecurityAttributeService,
			  context_data = #'CSI_SASContextBody'
			  {label = ?CSI_MsgType_MTCompleteEstablishContext, 
			   value = #'CSI_CompleteEstablishContext'
			   {client_context_id = 0, 
			    context_stateful = false,
			    final_context_token = [0,255]}}}|Acc], Env);
check_context([_|Rest], Acc, Env) ->
    check_context(Rest, Acc, Env).
    

%%-----------------------------------------------------------------
%% Func: call_interceptors
%%-----------------------------------------------------------------
-dialyzer({no_improper_lists, call_interceptors/7}).
call_interceptors(SocketType, #giop_env{interceptors = {native, Ref, PIs},
					ctx = Ctx} = Env, 
		  ReqHdr, Rest, Len, ByteOrder, Msg) ->
    NewRest = orber_pi:in_request_enc(PIs, ReqHdr, Ref, Rest),
    case decode_body(Env, ReqHdr, NewRest, Len, ByteOrder, Msg, enc_reply) of
	{NewEnv, Hdr, Par, TypeCodes} ->
	    NewPar = orber_pi:in_request(PIs, ReqHdr, Ref, Par),
	    ResultInv = invoke_request(Hdr, NewPar, SocketType, TypeCodes, NewEnv),
	    Result = orber_pi:out_reply(PIs, ReqHdr, Ref, ResultInv, Ctx),
	    
            case evaluate(NewEnv, ReqHdr, Result, TypeCodes, enc_reply_split,
			  'no_exception') of
		{ReplyHdr, Reply, HdrL, _BodyL, Flags} ->
		    NewReply = orber_pi:out_reply_enc(PIs, ReqHdr, Ref, Reply, Ctx),
		    MessSize = HdrL+size(NewReply),
		    cdr_encode:enc_giop_message_header(NewEnv, 'reply', Flags, 
						       MessSize, [ReplyHdr|NewReply]);
		Other ->
		    Other
	    end;
	Other ->
	    Other
    end;
call_interceptors(SocketType, #giop_env{interceptors = {portable, _PIs}} = Env, 
		  ReqHdr, Rest, Len, ByteOrder, Msg) ->
    case decode_body(Env, ReqHdr, Rest, Len, ByteOrder, Msg, enc_reply) of
	{NewEnv, Hdr, Par, TypeCodes} ->
	    Result = invoke_request(Hdr, Par, SocketType, TypeCodes, NewEnv),
	    evaluate(NewEnv, ReqHdr, Result, TypeCodes, enc_reply, 'no_exception');
	Other ->
	    Other
    end.

%%-----------------------------------------------------------------
%% Func: call_interceptors_out
%%-----------------------------------------------------------------
-dialyzer({no_improper_lists, call_interceptors_out/7}).
call_interceptors_out(#giop_env{interceptors = {native, Ref, PIs}, ctx = Ctx} = Env, 
		      ReqId, Result, Obj, Type, Operation, TypeCodes) ->
    ReqHdr = #request_header{object_key = Obj,
			     service_context = Ctx,
			     response_expected = true,
			     request_id = ReqId,
			     operation = Operation},
    NewResult = (catch orber_pi:out_reply(PIs, ReqHdr, Ref, Result, Ctx)),
    {ReplyHdr, Reply, HdrL, _BodyL, Flags} = 
	evaluate(Env, ReqHdr, NewResult, TypeCodes, enc_reply_split, Type),
    NewReply = 
	case catch orber_pi:out_reply_enc(PIs, ReqHdr, Ref, Reply, Ctx) of
	    {'EXCEPTION', Exception} ->
		%% Since evaluate don't need TypeCodes or Status no need to supply
		%% them.
		evaluate(Env, ReqHdr, {'EXCEPTION', Exception}, undefined, 
			 enc_reply_split, undefined);
	    {'EXIT', E} ->
		orber:dbg("[~p] orber_iiop_inrequest:handle_location_forward(~p)~n"
			  "Resulted in exit: ~p", [?LINE, PIs, E], ?DEBUG_LEVEL),
		marshal_exception(Env, ReqId,
				  #'MARSHAL'{completion_status=?COMPLETED_NO}, 
				  enc_reply);
	    R ->
		R
	end,
    MessSize = HdrL+size(NewReply),
    cdr_encode:enc_giop_message_header(Env, 'reply', Flags, MessSize, 
				       [ReplyHdr|NewReply]);
call_interceptors_out(#giop_env{interceptors = {portable, _PIs}} = Env,
		      ReqId, Result, _Obj, Type, _, TypeCodes) ->
    Hdr = #request_header{response_expected = true,
			  request_id = ReqId},
    evaluate(Env, Hdr, Result, TypeCodes, enc_reply, Type);
call_interceptors_out(Env, ReqId, Result, _Obj, Type, _, TypeCodes) ->
    Hdr = #request_header{response_expected = true,
			  request_id = ReqId},
    evaluate(Env, Hdr, Result, TypeCodes, enc_reply, Type).


%%-----------------------------------------------------------------
%% Func: decode_body/2
%%-----------------------------------------------------------------
decode_body(#giop_env{version = Version} = Env, ReqHdr, Rest, Len, 
	    ByteOrder, Message, Func) ->
    case catch cdr_decode:dec_request_body(Version, ReqHdr, Rest, Len, 
					   ByteOrder, Message) of
	{NewVersion, ReqHdr, Par, TypeCodes} ->
	    {Env#giop_env{version = NewVersion}, ReqHdr, Par, TypeCodes};
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_iiop_inrequest:decode_body(~p, ~p)~n"
		      "Failed decoding request body: ~p", 
		      [?LINE, ReqHdr, Message, E], ?DEBUG_LEVEL),
	    {error, marshal_exception(Env, ReqHdr#request_header.request_id,
				      E, Func)};
	Other ->
	    %% This cluase takes care of all erranous messages.
	    orber:dbg("[~p] orber_iiop_inrequest:decode_body(~p, ~p)~n"
		      "Failed decoding request body: ~p", 
		      [?LINE, ReqHdr, Message, Other], ?DEBUG_LEVEL),
	    {error, marshal_exception(Env, ReqHdr#request_header.request_id,
				      #'MARSHAL'{completion_status=?COMPLETED_NO},
				      Func)}
    end.


%%-----------------------------------------------------------------
%% Func: handle_locate_request/2
%%-----------------------------------------------------------------
handle_locate_request(Env, {object_forward, Object, ReqId}) ->
    case catch cdr_encode:enc_locate_reply(
		 Env#giop_env{request_id = ReqId,
			      tc = {'tk_objref', "", ""},
			      result = Object,
			      reply_status = 'object_forward'}) of
	{'EXCEPTION', Exception} ->
	    orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(object_forward)~n"
		      "Raised the exception: ~p", [?LINE, Exception], ?DEBUG_LEVEL),
	    marshal_locate_exception(Env, ReqId, Exception);
	{'EXIT', E} ->
	    orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(object_forward)~n"
		      "Resulted in exit: ~p", [?LINE, E], ?DEBUG_LEVEL),
	    marshal_locate_exception(Env, ReqId,
				     #'MARSHAL'{completion_status=?COMPLETED_NO});
	R ->
	    R
    end;
handle_locate_request(Env, Hdr) ->
    Location = orber_objectkeys:check(Hdr#locate_request_header.object_key),
    case catch cdr_encode:enc_locate_reply(
		 Env#giop_env{request_id = Hdr#locate_request_header.request_id,
			      reply_status = Location}) of
	{'EXCEPTION', Exception} ->
	    orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(~p)~n"
		      "Raised the exception: ~p", 
		      [?LINE, Location, Exception], ?DEBUG_LEVEL),
	    marshal_locate_exception(Env, Hdr#locate_request_header.request_id, Exception);
	{'EXIT', E} ->
	    orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(~p)~n"
		      "Resulted in exit: ~p", [?LINE, Location, E], ?DEBUG_LEVEL),
	    marshal_locate_exception(Env, Hdr#locate_request_header.request_id,
				     #'MARSHAL'{completion_status=?COMPLETED_NO});
	R ->
	    R
    end.
  
%%-----------------------------------------------------------------
%% Func: invoke_request/2
%%-----------------------------------------------------------------
invoke_request(Hdr, Par, normal, TypeCodes, #giop_env{iiop_ssl_port = SSLPort, 
						      partial_security = PartialSec}) ->
    Result = 
	case SSLPort of
	    -1 ->
		corba:request_from_iiop(Hdr#request_header.object_key,
					Hdr#request_header.operation,
					Par, [], Hdr#request_header.response_expected,
					Hdr#request_header.service_context);
	    _ ->
		case Hdr#request_header.object_key of
		    {_,registered,orber_init,_,_,_} ->
			corba:request_from_iiop(Hdr#request_header.object_key,
						Hdr#request_header.operation,
						Par, [], 
						Hdr#request_header.response_expected,
						Hdr#request_header.service_context);
		    {_,_,_,_,_,Flags} when PartialSec == true, 
					   ?ORB_FLAG_TEST(Flags, ?ORB_NO_SECURITY) == true ->
			corba:request_from_iiop(Hdr#request_header.object_key,
						Hdr#request_header.operation,
						Par, [], 
						Hdr#request_header.response_expected,
						Hdr#request_header.service_context);
		    _ ->
			orber:dbg("[~p] orber_iiop_inrequest:invoke_request(~p)~n"
				  "SSL do not permit", 
				  [?LINE, Hdr#request_header.object_key], ?DEBUG_LEVEL),
			{'EXCEPTION', #'NO_PERMISSION'{completion_status=?COMPLETED_NO}}
		end
	end,
    result_to_list(Result, TypeCodes);	
invoke_request(Hdr, Par, ssl, TypeCodes, _) ->
    Result = corba:request_from_iiop(Hdr#request_header.object_key,
				     Hdr#request_header.operation,
				     Par, [], Hdr#request_header.response_expected,
				     Hdr#request_header.service_context),
    result_to_list(Result, TypeCodes).

%%-----------------------------------------------------------------
%% Func: evaluate/4
%%-----------------------------------------------------------------
evaluate(_, Hdr,_,_,_,_) when Hdr#request_header.response_expected == 'false' ->
    oneway;
evaluate(Env, Hdr, _, _, Func, _) 
  when Hdr#request_header.response_expected == 'true_oneway' ->
    %% Special case which only occurs when using IIOP-1.2
    cdr_encode:Func(Env#giop_env{request_id = Hdr#request_header.request_id,
				 reply_status = 'no_exception', 
				 tc = {tk_null,[],[]}, result = null});
evaluate(Env, Hdr, {'EXCEPTION', Exc}, _, Func, _) ->
    %% The exception can be user defined. Hence, we must check the result.
    case catch marshal_exception(Env, Hdr#request_header.request_id, Exc, Func) of
	{'EXCEPTION', Exception} ->
	    orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p)~n"
		      "Encoding (reply) exception: ~p", 
		      [?LINE, Hdr, Exception], ?DEBUG_LEVEL),
	    marshal_exception(Env, Hdr#request_header.request_id, Exception, Func);
	{'EXIT', E} ->
	    orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p)~n"
		      "Encode (reply) resulted in: ~p", 
		      [?LINE, Hdr, E], ?DEBUG_LEVEL),
	    marshal_exception(Env, Hdr#request_header.request_id,
			      #'MARSHAL'{completion_status=?COMPLETED_YES}, Func);
	R ->
	    R	
    end;
evaluate(#giop_env{version = {1,2}} = Env, Hdr, {'location_forward_perm', NewIOR}, _, 
	 Func, _)->
    case catch cdr_encode:Func(#giop_env{version = {1,2},
					 request_id = Hdr#request_header.request_id,
					 reply_status = 'location_forward_perm',
					 tc = {{'tk_objref', "", ""}, [],[]},
					 result = NewIOR}) of
	{'EXCEPTION', Exception} ->
	    orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p) " ++
		      "Encoding (reply) exception: ~p",
		      [?LINE, Hdr, Exception], ?DEBUG_LEVEL),
	    marshal_exception(Env, Hdr#request_header.request_id, Exception, Func);
	{'EXIT', E} ->
	    orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p) " ++
		      "Encode (reply) resulted in: ~p", 
		      [?LINE, Hdr, E], ?DEBUG_LEVEL),
	    marshal_exception(Env, Hdr#request_header.request_id,
			      #'MARSHAL'{completion_status=?COMPLETED_YES}, Func);
	R ->
	    R
    end;
evaluate(Env, Hdr, [Res |OutPar], TypeCodes, Func, Type) ->
    case catch cdr_encode:Func(Env#giop_env{request_id = Hdr#request_header.request_id,
					    reply_status = Type,
					    tc = TypeCodes, result = Res, 
					    parameters = OutPar}) of
	{'EXCEPTION', Exception} ->
	    orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p, ~p, ~p)~n"
		      "Encode exception: ~p", 
		      [?LINE, Hdr, Res, OutPar, Exception], ?DEBUG_LEVEL),
	    marshal_exception(Env, Hdr#request_header.request_id, Exception, Func);
	{'EXIT', E} ->
	    orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p, ~p, ~p)~n"
		      "Encode exit: ~p", 
		      [?LINE, Hdr, Res, OutPar, E], ?DEBUG_LEVEL),
	    marshal_exception(Env, Hdr#request_header.request_id,
			      #'MARSHAL'{completion_status=?COMPLETED_YES}, Func);
	R ->
	    R
    end;
evaluate(Env, Hdr, What, TypeCodes, Func, _) ->
    orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p)~n"
	      "Bad reply: ~p~n"
	      "Should be: ~p~n"
	      "GIOP Env : ~p", [?LINE, Hdr, What, TypeCodes, Env], ?DEBUG_LEVEL),
    marshal_exception(Env, Hdr#request_header.request_id, 
		      #'INTERNAL'{completion_status=?COMPLETED_MAYBE}, Func).

%%-----------------------------------------------------------------
%% Utility Functions
%%-----------------------------------------------------------------
result_to_list({'oe_location_forward_perm', NewIOR}, _) ->
    {'location_forward_perm', NewIOR};
result_to_list({'EXCEPTION', E}, _) ->
    {'EXCEPTION', E};
result_to_list(Result, {_TkRes, _, []}) ->
    [Result];
result_to_list(Result, {_TkRes, _, _TkOut}) ->
    tuple_to_list(Result).

marshal_exception(Env, Id, Exception, Func) ->
    {TypeOfException, ExceptionTypeCode, NewExc} =
	orber_exceptions:get_def(Exception),
    cdr_encode:Func(Env#giop_env{request_id = Id, 
				 reply_status = TypeOfException, 
				 tc = {ExceptionTypeCode, [], []}, 
				 result = NewExc}).

marshal_locate_exception(#giop_env{version = {1,2}} = Env, Id, Exception) ->
    case orber_exceptions:get_def(Exception) of
	{?SYSTEM_EXCEPTION, ExceptionTypeCode, NewExc} ->
	    cdr_encode:enc_locate_reply(
	      Env#giop_env{request_id = Id,
			   reply_status = 'loc_system_exception', 
			   tc = ExceptionTypeCode, result = NewExc});
	_ ->
	    %% This case is impossible (i.e. Orber only throws system
	    %% exceptions). But to be on the safe side...
	    marshal_locate_exception(Env, Id, #'MARSHAL'
				     {completion_status=?COMPLETED_YES})
    end;
marshal_locate_exception(Env, _Id, _Exception) ->
    %% There is no way to define an exception for IIOP-1.0/1.1 in a 
    %% locate_reply.
    cdr_encode:enc_message_error(Env).
