diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/cosFileTransfer/src/CosFileTransfer_FileTransferSession_impl.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/cosFileTransfer/src/CosFileTransfer_FileTransferSession_impl.erl')
-rw-r--r-- | lib/cosFileTransfer/src/CosFileTransfer_FileTransferSession_impl.erl | 1060 |
1 files changed, 1060 insertions, 0 deletions
diff --git a/lib/cosFileTransfer/src/CosFileTransfer_FileTransferSession_impl.erl b/lib/cosFileTransfer/src/CosFileTransfer_FileTransferSession_impl.erl new file mode 100644 index 0000000000..e222c5b92b --- /dev/null +++ b/lib/cosFileTransfer/src/CosFileTransfer_FileTransferSession_impl.erl @@ -0,0 +1,1060 @@ +%%---------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-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 : CosFileTransfer_FileTransferSession_impl.erl +%% Description : +%% +%% Created : 12 Sept 2000 +%%---------------------------------------------------------------------- +-module('CosFileTransfer_FileTransferSession_impl'). + + +%%---------------------------------------------------------------------- +%% Include files +%%---------------------------------------------------------------------- +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/src/orber_iiop.hrl"). + +-include("cosFileTransferApp.hrl"). + +%%---------------------------------------------------------------------- +%% External exports +%%---------------------------------------------------------------------- +-export([init/1, + terminate/2, + code_change/3, + handle_info/2]). + +%% Interface functions +-export(['_get_protocols_supported'/2, + set_directory/3, + create_file/3, + create_directory/3, + get_file/3, + delete/3, + transfer/4, + append/4, + insert/5, + logout/2]). + +%%---------------------------------------------------------------------- +%% Internal exports +%%---------------------------------------------------------------------- +-export([oe_orber_create_directory_current/2, oe_orber_get_content/4, + oe_orber_count_children/3]). +-export([invoke_call/3]). + +%%---------------------------------------------------------------------- +%% Records +%%---------------------------------------------------------------------- +-record(state, {protocols, server, type, current, module, connection, mytype, + connection_timeout}). + +%%---------------------------------------------------------------------- +%% Macros +%%---------------------------------------------------------------------- +-define(create_InitState(P, S, T, C, M, Co, Ty, CT), + #state{protocols=P, server=S, type=T, current=C, module=M, connection=Co, + mytype=Ty, connection_timeout=CT}). + +-define(get_Protocols(S), S#state.protocols). +-define(get_Server(S), S#state.server). +-define(get_CurrentDir(S), S#state.current). +-define(get_Module(S), S#state.module). +-define(get_Connection(S), S#state.connection). +-define(get_MyType(S), S#state.mytype). +-define(get_ConnectionTimeout(S), S#state.connection_timeout). +-define(set_CurrentDir(S, C), S#state{current=C}). + +-define(is_FTP(S), S#state.type=='FTP'). +-define(is_FTAM(S), S#state.type=='FTAM'). +-define(is_NATIVE(S), S#state.type=='NATIVE'). +-define(is_ORBER_NATIVE(S), S#state.module==cosFileTransferNATIVE_file). + + +%%====================================================================== +%% External functions +%%====================================================================== +%%---------------------------------------------------------------------- +%% Function : init/1 +%% Returns : {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%---------------------------------------------------------------------- +init(['FTP', Host, Port, User, Password, _Account, Protocol, Timeout]) -> + {ok, Pid} = inets:start(ftpc, [{host, Host}, {port, Port}], stand_alone), + ok = ftp:user(Pid, User, Password), + {ok, PWD} = ftp:pwd(Pid), + {Connection, ProtocolSupport} = setup_local(Protocol), + {ok, ?create_InitState(ProtocolSupport, Pid, 'FTP', + PWD, ftp, Connection, Protocol, Timeout)}; +init([{'NATIVE', Mod}, Host, Port, User, Password, _Account, Protocol, Timeout]) -> + {ok, Pid} = Mod:open(Host, Port), + ok = Mod:user(Pid, User, Password), + {ok, PWD} = Mod:pwd(Pid), + {Connection, ProtocolSupport} = setup_local(Protocol), + {ok, ?create_InitState(ProtocolSupport, Pid, 'NATIVE', + PWD, Mod, Connection, Protocol, Timeout)}. + + +%%---------------------------------------------------------------------- +%% Function : terminate/2 +%% Returns : any (ignored by gen_server) +%% Description: Shutdown the server +%%---------------------------------------------------------------------- +terminate(_Reason, #state{type = Type, server = Server, module = Mod} = State) -> + case ?get_MyType(State) of + ssl -> + catch ssl:close(?get_Connection(State)); + _ -> + catch gen_tcp:close(?get_Connection(State)) + end, + case Type of + 'FTP' -> + inets:stop(ftpc, Server); + 'NATIVE' -> + Mod:close(Server); + _ -> + ok + end, + ok. + +%%---------------------------------------------------------------------- +%% Function : code_change/3 +%% Returns : {ok, NewState} +%% Description: Convert process state when code is changed +%%---------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%---------------------------------------------------------------------% +%% function : handle_info/2 +%% Arguments: +%% Returns : +%% Effect : +%%---------------------------------------------------------------------- +handle_info(Info, State) -> + case Info of + {'EXIT', _Pid, Reason} -> + {stop, Reason, State}; + _Other -> + {noreply, State} + end. + +%%====================================================================== +%% CosFileTransfer::FileTransferSession +%%====================================================================== +%%---------------------------------------------------------------------% +%% Function : _get_protocols_supported +%% Arguments : +%% Returns : A list of CosFileTransfer::ProtocolSupport, i.e., +%% struct ProtocolSupport { +%% Istring protocol_name; +%% ProtocolAddressList addresses; %% eq a list of strings. +%% }; +%% Description: +%%---------------------------------------------------------------------- +'_get_protocols_supported'(_OE_This, State) -> + {reply, ?get_Protocols(State), State}. + +%%---------------------------------------------------------------------- +%% Function : set_directory +%% Arguments : Directory - CosFileTransfer::Directory +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +set_directory(_OE_This, State, Directory) when ?is_FTP(State); ?is_NATIVE(State) -> + Mod = ?get_Module(State), + Path = filename:join('CosFileTransfer_Directory': + '_get_complete_file_name'(Directory)), + case catch Mod:cd(?get_Server(State), Path) of + ok -> + {reply, ok, ?set_CurrentDir(State, Path)}; + {error, epath} -> + corba:raise(#'CosFileTransfer_FileNotFoundException' + {reason="Directory not found."}); + {error, elogin} -> + corba:raise(#'CosFileTransfer_SessionException' + {reason="User not loggen in."}); + {error, econn} -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Premature connection ending."}); + _ -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason = "Unexpected error."}) + end. + +%%---------------------------------------------------------------------- +%% Function : create_file +%% Arguments : FileNameList +%% Returns : File +%% Description: This operation creates a File Object representing a +%% file which may or may not exist. Typically used as +%% argument when invoking transfer/3. See also get_file/2. +%%---------------------------------------------------------------------- +create_file(OE_This, State, FileNameList) -> + {reply, cosFileTransferApp:create_file(OE_This, FileNameList), State}. + +%%---------------------------------------------------------------------- +%% Function : create_directory +%% Arguments : FileNameList - full path name. +%% Returns : Directory +%% Description: +%%---------------------------------------------------------------------- +create_directory(OE_This, State, FileNameList) when ?is_FTP(State); + ?is_NATIVE(State) -> + Mod = ?get_Module(State), + case Mod:mkdir(?get_Server(State), filename:join(FileNameList)) of + ok -> + {reply, cosFileTransferApp:create_dir(OE_This, FileNameList), State}; + {error, epath} -> + corba:raise(#'CosFileTransfer_FileNotFoundException' + {reason="Directory not found."}); + {error, elogin} -> + corba:raise(#'CosFileTransfer_SessionException' + {reason="User not loggen in."}); + {error, econn} -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Premature connection ending."}); + _ -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Unknown error."}) + end. + + +%%---------------------------------------------------------------------- +%% Function : get_file +%% Arguments : FileNameList +%% Returns : FileWrapper +%% Description: This operation should be independent of the working Directory, +%% i.e., a full path name must be supplied. The file or +%% directory the returned object is supposed to represent +%% MUST(!!!!) exist. +%%---------------------------------------------------------------------- +get_file(OE_This, State, FileNameList) when ?is_FTP(State); + ?is_NATIVE(State) -> + case check_type(OE_This, State, filename:join(FileNameList)) of + {ndirectory, _Listing} -> + {reply, + #'CosFileTransfer_FileWrapper'{the_file = + cosFileTransferApp: + create_dir(OE_This, + FileNameList), + file_type = ndirectory}, + State}; + nfile -> + {reply, + #'CosFileTransfer_FileWrapper'{the_file = + cosFileTransferApp: + create_file(OE_This, + FileNameList), + file_type = nfile}, + State}; + Other -> + %% If we want to return {stop, ....} + Other + end. + +%%---------------------------------------------------------------------- +%% Function : delete +%% Arguments : File +%% Returns : - +%% Description: +%%---------------------------------------------------------------------- +delete(_OE_This, State, File) when ?is_FTP(State); ?is_NATIVE(State) -> + Mod = ?get_Module(State), + Result = + case 'CosPropertyService_PropertySet': + get_property_value(File, "is_directory") of + #any{value=false} -> + Mod:delete(?get_Server(State), + filename:join('CosFileTransfer_File': + '_get_complete_file_name'(File))); + #any{value=true} -> + Mod:rmdir(?get_Server(State), + filename:join('CosFileTransfer_File': + '_get_complete_file_name'(File))); + Other -> + Other + end, + case Result of + ok -> + {reply, ok, State}; + {error, epath} -> + corba:raise(#'CosFileTransfer_FileNotFoundException' + {reason="File or Directory not found."}); + {error, elogin} -> + corba:raise(#'CosFileTransfer_SessionException' + {reason="User not loggen in."}); + {error, econn} -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Premature connection ending."}); + _ -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Unknown error."}) + end. + +%%---------------------------------------------------------------------- +%% Function : transfer +%% Arguments : SrcFile eq DestFile eq CosFileTransfer::File +%% Returns : - +%% Description: DestFile must be a newly created File object, using create_file() +%% on the Target FileTransferSession, prior to calling transfer(). +%%---------------------------------------------------------------------- +transfer(OE_This, State, SrcFile, DestFile) when ?is_ORBER_NATIVE(State) -> + case which_FTS_type(OE_This, SrcFile, DestFile) of + {source, TargetFTS} -> + %% The source FTS is supposed to be the active one, set up a connection. + Protocols = 'CosFileTransfer_FileTransferSession': + '_get_protocols_supported'(TargetFTS), + SrcName = 'CosFileTransfer_File':'_get_complete_file_name'(SrcFile), + Pid = spawn(?MODULE, invoke_call, [self(), transfer, + [TargetFTS, SrcFile, DestFile]]), + send_file(Protocols, ?get_MyType(State), ?get_ConnectionTimeout(State), + filename:join(SrcName)), + check_reply(Pid), + {reply, ok, State}; + {target, _SourceFTS} -> + DestName = 'CosFileTransfer_File':'_get_complete_file_name'(DestFile), + receive_file(?get_MyType(State), ?get_Connection(State), + ?get_ConnectionTimeout(State), + filename:join(DestName), write), + {reply, ok, State} + end; +transfer(OE_This, State, SrcFile, DestFile) when ?is_FTP(State); ?is_NATIVE(State) -> + case which_FTS_type(OE_This, SrcFile, DestFile) of + {source, TargetFTS} -> + source_FTS_operation(State, SrcFile, DestFile, transfer, 0, TargetFTS); + {target, _SourceFTS} -> + target_FTS_operation(State, SrcFile, DestFile, send, 0) + end. + + +%%---------------------------------------------------------------------- +%% Function : append +%% Arguments : SrcFile eq DestFile eq CosFileTransfer::File +%% Returns : - +%% Description: +%%---------------------------------------------------------------------- +append(OE_This, State, SrcFile, DestFile) when ?is_ORBER_NATIVE(State) -> + case which_FTS_type(OE_This, SrcFile, DestFile) of + {source, TargetFTS} -> + SrcName = filename:join('CosFileTransfer_File': + '_get_complete_file_name'(SrcFile)), + check_type(OE_This, State, SrcName), + %% The source FTS is supposed to be the active one, set up a connection. + Protocols = 'CosFileTransfer_FileTransferSession': + '_get_protocols_supported'(TargetFTS), + Pid = spawn(?MODULE, invoke_call, [self(), append, + [TargetFTS, SrcFile, DestFile]]), + send_file(Protocols, ?get_MyType(State), ?get_ConnectionTimeout(State), + SrcName), + check_reply(Pid), + {reply, ok, State}; + {target, _SourceFTS} -> + DestName = filename:join('CosFileTransfer_File': + '_get_complete_file_name'(DestFile)), + check_type(OE_This, State, DestName), + receive_file(?get_MyType(State), ?get_Connection(State), + ?get_ConnectionTimeout(State), DestName, append), + {reply, ok, State} + end; +append(OE_This, State, SrcFile, DestFile) when ?is_NATIVE(State) -> + case which_FTS_type(OE_This, SrcFile, DestFile) of + {source, TargetFTS} -> + source_FTS_operation(State, SrcFile, DestFile, append, 0, TargetFTS); + {target, _SourceFTS} -> + target_FTS_operation(State, SrcFile, DestFile, append, 0) + end; +append(_OE_This, _State, _SrcFile, _DestFile) -> + corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_NO}). + + +%%---------------------------------------------------------------------- +%% Function : insert +%% Arguments : SrcFile eq DestFile eq CosFileTransfer::File +%% Offset - long +%% Returns : - +%% Description: +%%---------------------------------------------------------------------- +insert(OE_This, State, SrcFile, DestFile, Offset) when ?is_NATIVE(State) -> + case which_FTS_type(OE_This, SrcFile, DestFile) of + {source, TargetFTS} when ?is_ORBER_NATIVE(State) -> + SrcName = 'CosFileTransfer_File':'_get_complete_file_name'(SrcFile), + check_type(OE_This, State, filename:join(SrcName)), + %% The source FTS is supposed to be the active one, set up a connection. + Protocols = 'CosFileTransfer_FileTransferSession': + '_get_protocols_supported'(TargetFTS), + Pid = spawn(?MODULE, invoke_call, [self(), insert, + [TargetFTS, SrcFile, + DestFile, Offset]]), + send_file(Protocols, ?get_MyType(State), ?get_ConnectionTimeout(State), + filename:join(SrcName)), + check_reply(Pid), + {reply, ok, State}; + {source, TargetFTS} -> + source_FTS_operation(State, SrcFile, DestFile, insert, Offset, TargetFTS); + {target, _SourceFTS} -> + target_FTS_operation(State, SrcFile, DestFile, insert, Offset) + end; +insert(_OE_This, _State, _SrcFile, _DestFile, _Offset) -> + corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_NO}). + + +%%---------------------------------------------------------------------- +%% Function : logout +%% Arguments : - +%% Returns : - +%% Description: +%%---------------------------------------------------------------------- +logout(_OE_This, State) when ?is_FTP(State); ?is_NATIVE(State) -> + Mod = ?get_Module(State), + catch Mod:close(?get_Server(State)), + {stop, normal, ok, State}. + +%%====================================================================== +%% Internal functions +%%====================================================================== +%%---------------------------------------------------------------------- +%% Function : oe_orber_create_directory_current +%% Arguments : - +%% Returns : Directory +%% Description: Creates a Directory describing the working directory +%% of the remote server, e.g., an FTP-server. +%%---------------------------------------------------------------------- +oe_orber_create_directory_current(OE_This, State) when ?is_FTP(State); + ?is_NATIVE(State) -> + Mod = ?get_Module(State), + FileNameList = filename:split(?get_CurrentDir(State)), + case Mod:nlist(?get_Server(State), ?get_CurrentDir(State)) of + {ok, _Listing} -> + {reply, cosFileTransferApp:create_dir(OE_This, FileNameList), + State}; + {error, epath} -> + corba:raise(#'CosFileTransfer_FileNotFoundException' + {reason="Directory not found."}); + {error, elogin} -> + corba:raise(#'CosFileTransfer_SessionException' + {reason="User not loggen in."}); + {error, econn} -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Premature connection ending."}); + _ -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Unknown error."}) + end. +%%---------------------------------------------------------------------- +%% Function : oe_orber_get_content +%% Arguments : - +%% Returns : string +%% Description: +%%---------------------------------------------------------------------- +oe_orber_get_content(OE_This, State, FileNameList, Parent) when ?is_FTP(State); + ?is_NATIVE(State) -> + Mod = ?get_Module(State), + case Mod:nlist(?get_Server(State), filename:join(FileNameList)) of + {ok, Listing} -> + create_content(Listing, OE_This, State, Parent, FileNameList); + {error, epath} -> + {reply, [], State}; + _ -> + corba:raise(#'CosFileTransfer_FileNotFoundException' + {reason="Directory not found."}) + end. + +%%---------------------------------------------------------------------- +%% Function : oe_orber_count_children +%% Arguments : - +%% Returns : string +%% Description: +%%---------------------------------------------------------------------- +oe_orber_count_children(OE_This, State, FileNameList) when ?is_FTP(State); + ?is_NATIVE(State) -> + case catch check_type(OE_This, State, filename:join(FileNameList)) of + {ndirectory, Members} -> + {reply, length(Members), State}; + {stop, normal, _, _} -> + {stop, normal, + {'EXCEPTION', #'INTERNAL'{completion_status=?COMPLETED_NO}}, + State}; + _-> + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%---------------------------------------------------------------------- +%% Function : delete_tmp_file +%% Arguments : - +%% Returns : ok | {'EXCEPTION', E} +%% Description: +%%---------------------------------------------------------------------- +delete_tmp_file(TmpFileName, ErrorMsg) -> + case file:delete(TmpFileName) of + ok -> + ok; + _ -> + corba:raise(#'CosFileTransfer_RequestFailureException'{reason=ErrorMsg}) + end. + + +%%---------------------------------------------------------------------- +%% Function : invoke_call +%% Arguments : - +%% Returns : ok | {'EXCEPTION', E} +%% Description: +%%---------------------------------------------------------------------- +invoke_call(Pid, Op, Args) -> + Result = (catch apply('CosFileTransfer_FileTransferSession', Op, Args)), + Pid ! {transfer_result, self(), Result}, + ok. + +%%---------------------------------------------------------------------- +%% Function : check_reply +%% Arguments : Pid - the pid of the spawned process. +%% Returns : ok | {'EXCEPTION', E} +%% Description: +%%---------------------------------------------------------------------- +check_reply(Pid) -> + receive + {transfer_result, Pid, ok} -> + ok; + {transfer_result, Pid, {'EXCEPTION', E}} -> + orber:debug_level_print("[~p] CosFileTransfer_FileTransferSession:check_reply(); +Raised exception: ", [?LINE, E], ?DEBUG_LEVEL), + corba:raise(E); + {transfer_result, Pid, {'EXIT', Reason}} -> + orber:debug_level_print("[~p] CosFileTransfer_FileTransferSession:check_reply(); +Got EXIT-signal with reason: ", [?LINE, Reason], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{minor=199, + completion_status=?COMPLETED_NO}) + after infinity -> + %% Should we add an exception here or do we reuse the iiop_timeout? + %% For now keep as is. + corba:raise(#'INTERNAL'{minor=199, + completion_status=?COMPLETED_NO}) + end. + + +%%---------------------------------------------------------------------- +%% Function : which_FTS_type +%% Arguments : - +%% Returns : {source, FTS} | {target, FTS} | {'EXCEPTION', #'BAD_PARAM'{}} +%% Description: Used to determine if the target FTS is supposed to act +%% as sender or receiver and also return the counter part FTS. +%% An exception is raised if the user supplied incorrect parameters. +%%---------------------------------------------------------------------- +which_FTS_type(OE_This, SrcFile, DestFile) -> + TargetFTS = 'CosFileTransfer_File':'_get_associated_session'(DestFile), + SourceFTS = 'CosFileTransfer_File':'_get_associated_session'(SrcFile), + case corba_object:is_equivalent(OE_This, TargetFTS) of + true -> + {target, SourceFTS}; + false -> + case corba_object:is_equivalent(OE_This, SourceFTS) of + true -> + {source, TargetFTS}; + false -> + corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}) + end + end. + + +%%---------------------------------------------------------------------- +%% Function : setup_connection +%% Arguments : A list of #'CosFileTransfer_ProtocolSupport'{} +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +setup_connection([], Protocol, _) -> + orber:debug_level_print("[~p] CosFileTransfer_FileTransferSession:setup_connection(~p); +The Protocols listed are not supported.", [?LINE, Protocol], ?DEBUG_LEVEL), + corba:raise(#'CosFileTransfer_TransferException'{reason="Unsupported protocol"}); +setup_connection([#'CosFileTransfer_ProtocolSupport'{protocol_name="TCP/IP", + addresses=Addr}|_], + tcp, Timeout) -> + setup_connection_helper(Addr, gen_tcp, [], Timeout); +setup_connection([#'CosFileTransfer_ProtocolSupport'{protocol_name="SSL", + addresses=Addr}|_], + ssl, Timeout) -> + Options = [{certfile, cosFileTransferApp:ssl_client_certfile()}, + {verify, cosFileTransferApp:ssl_client_verify()}, + {depth, cosFileTransferApp:ssl_client_depth()}] ++ + ssl_client_cacertfile_option(), + setup_connection_helper(Addr, ssl, Options, Timeout); +setup_connection([_|T], Type, Timeout) -> + setup_connection(T, Type, Timeout). + +setup_connection_helper([], _, _, _) -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Unable to contact remote server."}); +setup_connection_helper([H|T], Driver, Options, Timeout) -> + case string:tokens(H, ":") of + [Host, Port] when Driver == gen_tcp -> + case gen_tcp:connect(Host, list_to_integer(Port), + [binary, + {packet, raw}, + {reuseaddr, true}, + {nodelay, true}|Options], Timeout) of + {ok, Sock} -> + {gen_tcp, Sock}; + _-> + %% No response. + setup_connection_helper(T, Driver, Options, Timeout) + end; + [Host, Port] when Driver == ssl -> + case ssl:connect(Host, list_to_integer(Port), + [binary, + {packet, 0}, + {active, false}|Options], Timeout) of + {ok, Sock} -> + {ssl, Sock}; + _-> + %% No response. + setup_connection_helper(T, Driver, Options, Timeout) + end; + _ -> + %% Badly configured address. + setup_connection_helper(T, Driver, Options, Timeout) + end. + +ssl_client_cacertfile_option() -> + case cosFileTransferApp:ssl_client_cacertfile() of + [] -> + []; + X when is_list(X) -> + {cacertfile, X}; + _ -> + [] + end. + +%%---------------------------------------------------------------------- +%% Function : create_content +%% Arguments : +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +create_content(Listing, OE_This, State, Parent, PathList) -> + create_content(string:tokens(Listing, ?SEPARATOR), OE_This, + State, Parent, PathList, []). + +create_content([], _OE_This, State, _Parent, _PathList, Acc) -> + {reply, Acc, State}; +create_content([H|T], OE_This, State, Parent, PathList, Acc) -> + FullPathList = PathList ++[filename:basename(H)], + case check_type(OE_This, State, filename:join(FullPathList)) of + nfile -> + create_content(T, OE_This, State, Parent, PathList, + [#'CosFileTransfer_FileWrapper' + {the_file = cosFileTransferApp:create_file(OE_This, + FullPathList, + Parent), + file_type = nfile}|Acc]); + {ndirectory, _Members} -> + create_content(T, OE_This, State, Parent, PathList, + [#'CosFileTransfer_FileWrapper' + {the_file = cosFileTransferApp:create_dir(OE_This, + FullPathList, + Parent), + file_type = ndirectory}|Acc]); + Other -> + Other + end. + + +%%---------------------------------------------------------------------- +%% Function : MISC functions +%% Arguments : +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +setup_local(tcp) -> + {ok,Socket}=gen_tcp:listen(0, [binary, + {packet, 0}, + {backlog,1}, + {active, false}]), + {ok, Port} = inet:port(Socket), + {Socket, [#'CosFileTransfer_ProtocolSupport'{protocol_name="TCP/IP", + addresses = [local_address(Port)]}]}; +setup_local(ssl) -> + Options = [{certfile, cosFileTransferApp:ssl_server_certfile()}, + {verify, cosFileTransferApp:ssl_server_verify()}, + {depth, cosFileTransferApp:ssl_server_depth()}] ++ + ssl_server_cacertfile_option(), + {ok,Socket}=ssl:listen(0, [binary, + {packet, 0}, + {backlog,1}, + {active, false}|Options]), + {ok, {_Address, Port}} = ssl:sockname(Socket), + {Socket, [#'CosFileTransfer_ProtocolSupport'{protocol_name="SSL", + addresses = [local_address(Port)]}]}. + +local_address(Port) -> + {ok, Hostname} = inet:gethostname(), + {ok, {A1, A2, A3, A4}} = inet:getaddr(Hostname, inet), + integer_to_list(A1) ++ "." ++ integer_to_list(A2) ++ "." ++ integer_to_list(A3) + ++ "." ++ integer_to_list(A4)++":"++integer_to_list(Port). + +ssl_server_cacertfile_option() -> + case cosFileTransferApp:ssl_server_cacertfile() of + [] -> + []; + X when is_list(X) -> + [{cacertfile, X}]; + _ -> + [] + end. + +%%---------------------------------------------------------------------- +%% Function : source_file_operation +%% Arguments : +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +source_FTS_operation(State, SrcFile, DestFile, Op, Offset, FTS) -> + Mod = ?get_Module(State), + %% The source FTS is supposed to be the active one, set up a connection. + Protocols = 'CosFileTransfer_FileTransferSession':'_get_protocols_supported'(FTS), + SrcName = 'CosFileTransfer_File':'_get_complete_file_name'(SrcFile), + TempName = cosFileTransferApp:create_name("TemporarySrcFile"), + case Mod:recv(?get_Server(State), filename:join(SrcName), TempName) of + ok when Op == insert -> + %% Downloaded the File, we are now ready to transmit. + Pid = spawn(?MODULE, invoke_call, [self(), insert, + [FTS, SrcFile, DestFile, Offset]]), + send_file(Protocols, ?get_MyType(State), ?get_ConnectionTimeout(State), + TempName), + %% Delete the temporary local copy. + delete_tmp_file(TempName, + "Transfer completed but failed to remove temporary local copy."), + check_reply(Pid), + {reply, ok, State}; + ok -> + %% Downloaded the File, we are now ready to transmit. + Pid = spawn(?MODULE, invoke_call, [self(), Op, [FTS, SrcFile, DestFile]]), + send_file(Protocols, ?get_MyType(State), ?get_ConnectionTimeout(State), + TempName), + %% Delete the temporary local copy. + delete_tmp_file(TempName, + "Transfer completed but failed to remove temporary local copy."), + check_reply(Pid), + {reply, ok, State}; + {error, epath} -> + corba:raise(#'CosFileTransfer_FileNotFoundException' + {reason="File not found."}); + {error, elogin} -> + corba:raise(#'CosFileTransfer_SessionException' + {reason="User not loggen in."}); + {error, econn} -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Premature connection ending."}) + end. + +%%---------------------------------------------------------------------- +%% Function : target_file_operation +%% Arguments : +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +target_FTS_operation(State, _SrcFile, DestFile, Op, Offset) -> + Mod = ?get_Module(State), + DestName = 'CosFileTransfer_File':'_get_complete_file_name'(DestFile), + TempName = cosFileTransferApp:create_name("TemporaryDestFile"), + receive_file(?get_MyType(State), ?get_Connection(State), + ?get_ConnectionTimeout(State), TempName, write), + Result = + if + Op == insert -> + Mod:insert(?get_Server(State), TempName, filename:join(DestName), Offset); + true -> + Mod:Op(?get_Server(State), TempName, filename:join(DestName)) + end, + case Result of + ok -> + %% Delete the temporary local copy. + delete_tmp_file(TempName, + "Transfer completed but failed to remove temporary local copy."), + %% Completed the transfer succesfully. + {reply, ok, State}; + {error, epath} -> + delete_tmp_file(TempName, + "IllegalOperationException and not able to remove temporary local copy."), + corba:raise(#'CosFileTransfer_IllegalOperationException' + {reason="Not allowed by destination."}); + {error, elogin} -> + delete_tmp_file(TempName, + "SessionException and not able to remove temporary local copy."), + corba:raise(#'CosFileTransfer_SessionException' + {reason="User not logged in."}); + {error, econn} -> + delete_tmp_file(TempName, + "TransferException and not able to remove temporary local copy."), + corba:raise(#'CosFileTransfer_TransferException' + {reason="Premature connection ending."}); + {error, etnospc} -> + delete_tmp_file(TempName, + "RequestFailureException and not able to remove temporary local copy."), + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Premature connection ending."}); + {error, efnamena} -> + delete_tmp_file(TempName, + "IllegalOperationException and not able to remove temporary local copy."), + corba:raise(#'CosFileTransfer_IllegalOperationException' + {reason="Not allowed by destination."}) + end. + +%%---------------------------------------------------------------------- +%% Function : receive_file +%% Arguments : Driver - currently only gen_tcp supported. +%% LSocket - which socket to use. +%% FileName - an absolute file name representing the +%% file we want to create or append to. +%% Type - 'read', 'write', 'append'. +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +receive_file(tcp, LSock, Timeout, FileName, Type) -> + %% The Type can be the ones allowed by the file-module, i.e., + %% 'read', 'write' or 'append' + FD = file_open(FileName, Type), + case gen_tcp:accept(LSock, Timeout) of + {ok, Sock} -> + receive_file_helper(gen_tcp, Sock, FD); + {error, timeout} -> + orber:dbg("[~p] CosFileTransfer_FileTransferSession:receive_file();~n" + "gen_tcp:accept(~p) timed out", [?LINE, Timeout], ?DEBUG_LEVEL), + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="TCP accept timed out.."}); + {error, Why} -> + orber:dbg("[~p] CosFileTransfer_FileTransferSession:receive_file();~n" + "gen_tcp:accept(~p) failed: ~p", [?LINE, Timeout, Why], ?DEBUG_LEVEL), + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="TCP accept failed."}) + end; +receive_file(ssl, LSock, Timeout, FileName, Type) -> + %% The Type can be the ones allowed by the file-module, i.e., + %% 'read', 'write' or 'append' + FD = file_open(FileName, Type), + case ssl:transport_accept(LSock, Timeout) of + {ok, Sock} -> + case ssl:ssl_accept(Sock, Timeout) of + ok -> + receive_file_helper(ssl, Sock, FD); + {error, Error} -> + orber:dbg("[~p] CosFileTransfer_FileTransferSession:receive_file();~n" + "ssl:ssl_accept(~p) failed: ~p", + [?LINE, Timeout, Error], ?DEBUG_LEVEL), + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="TCP accept failed."}) + end; + {error, timeout} -> + orber:dbg("[~p] CosFileTransfer_FileTransferSession:receive_file();~n" + "ssl:transport_accept(~p) timed out", + [?LINE, Timeout], ?DEBUG_LEVEL), + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="TCP accept timed out.."}); + {error, Why} -> + orber:dbg("[~p] CosFileTransfer_FileTransferSession:receive_file();~n" + "ssl:transport_accept(~p) failed: ~p", + [?LINE, Timeout, Why], ?DEBUG_LEVEL), + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="TCP accept failed."}) + end. + +receive_file_helper(Driver, Sock, FD) -> + case Driver:recv(Sock, 0) of + {ok, Bin} -> + file:write(FD, Bin), + receive_file_helper(Driver, Sock, FD); + {error, closed} -> + file:close(FD); + What -> + orber:debug_level_print("[~p] CosFileTransfer_FileTransferSession:receive_file(~p); +Error occured when receiving data: ~p", [?LINE, Driver, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end. + +%%---------------------------------------------------------------------- +%% Function : send_file +%% Arguments : Driver - currently only gen_tcp supported. +%% Sock - which socket to use. +%% FileName - an absolute file name representing the +%% file we want to send. +%% Returns : +%% Description: +%%---------------------------------------------------------------------- +send_file(Protocols, Type, Timeout, FileName) -> + {Driver, Sock} = setup_connection(Protocols, Type, Timeout), + FD = file_open(FileName, read), + BuffSize = cosFileTransferApp:get_buffert_size(), + send_file_helper(Driver, Sock, FD, BuffSize). + +send_file_helper(Driver, Sock, FD, BuffSize) -> + case file:read(FD, BuffSize) of + eof -> + file:close(FD), + Driver:close(Sock); + {ok, Bin} -> + case Driver:send(Sock, Bin) of + ok -> + send_file_helper(Driver, Sock, FD, BuffSize); + What -> + orber:debug_level_print("[~p] CosFileTransfer_FileTransferSession:send_file_helper(~p); +Error occured when sending data: ~p", [?LINE, Driver, What], ?DEBUG_LEVEL), + corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO}) + end + end. + + +file_open(File, Type) -> + case file:open(File, [raw, binary, Type]) of + {ok, FD} -> + FD; + {error, What} -> + orber:debug_level_print("[~p] CosFileTransfer_FileTransferSession:file_open(~p); +Failed to open the file due to: ~p", [?LINE, File, What], ?DEBUG_LEVEL), + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Unable to open given file."}) + end. + +%%---------------------------------------------------------------------- +%% Function : check_type +%% Arguments : FullName - an absolute file name representing the +%% file or directory we want to evaluate. +%% Returns : +%% Description: +%% When communcating with FTP-servers on different platforms a variety of +%% answers can be returned. A few examples: +%% +%% ### ftp:nlist on an empty directory ### +%% {ok, ""}, {error, epath} +%% +%% ### ftp:nlist on a non-existing directory or file ### +%% {ok, "XXX: No such file or directory}, {error, epath} +%% +%% ### ftp:nlist on an existing directory with one contained item ### +%% {ok, "Item"} +%% +%% Comparing the above we see that it's virtually impossible to tell apart +%% {ok, "XXX: No such file or directory} and {ok, "Item"}. +%% Hence, it's easier to test if it's possible to do ftp:cd instead. +%% Ugly, but rather effective. If we look at the bright side, it's only +%% necessary when we try to lookup: +%% * non-existing item +%% * A directory with one member only. +%% * An empty directory. +%% +%% Furthermore, no need for traversing Listings etc. +%%---------------------------------------------------------------------- +check_type(_OE_This, State, FullName) when ?is_FTP(State); ?is_NATIVE(State) -> + Mod = ?get_Module(State), + Result = + case Mod:nlist(?get_Server(State), FullName) of + {ok, Listing} when length(Listing) > 0-> + case string:tokens(Listing, ?SEPARATOR) of + [FullName] -> + nfile; + Members when length(Members) > 1 -> + %% Must test if more than one member since sometimes + %% this operation returns for example: + %% {ok, "XXX No such file or drectory"} + {ndirectory, Members}; + Member -> + case Mod:cd(?get_Server(State), FullName) of + ok -> + case Mod:cd(?get_Server(State), + ?get_CurrentDir(State)) of + ok -> + {ndirectory, Member}; + _ -> + %% Failed, we cannot continue since the + %% FTS now pointso an incorrect Directory. + %% Hence, we must terminate. + {stop, normal, + {'EXCEPTION', + #'CosFileTransfer_RequestFailureException' + {reason="Unknown error."}}, State} + end; + {error, E} -> + {error, E}; + _ -> + nfile + end + end; + {error, epath} -> + %% Might be a file. + DirName = filename:dirname(FullName), + case Mod:nlist(?get_Server(State), DirName) of + {ok, Listing} when length(Listing) > 0-> + Members = string:tokens(Listing, ?SEPARATOR), + case lists:member(FullName, Members) of + true -> + nfile; + _ -> + BName = filename:basename(FullName), + case lists:member(BName, Members) of + true -> + nfile; + _ -> + {error, epath} + end + end; + _ -> + {error, epath} + end; + _ -> + case Mod:cd(?get_Server(State), FullName) of + ok -> + case Mod:cd(?get_Server(State), ?get_CurrentDir(State)) of + ok -> + {ndirectory, []}; + _ -> + %% Failed, we cannot continue since the + %% FTS now pointso an incorrect Directory. + %% Hence, we must terminate. + {stop, normal, + {'EXCEPTION', + #'CosFileTransfer_RequestFailureException' + {reason="Unknown error."}}, State} + end; + _ -> + {error, epath} + end + end, + case Result of + {error, epath} -> + corba:raise(#'CosFileTransfer_FileNotFoundException' + {reason="File or Directory not found."}); + {error, elogin} -> + corba:raise(#'CosFileTransfer_SessionException' + {reason="User not logged in."}); + {error, econn} -> + corba:raise(#'CosFileTransfer_RequestFailureException' + {reason="Premature connection ending."}); + Other -> + Other + end. + + + +%%====================================================================== +%% END OF MODULE +%%====================================================================== |