ftp.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,598 行 · 第 1/4 页

ERL
1,598
字号
send_chunk_end(Pid) ->    call(Pid, chunk_end, atom).%%--------------------------------------------------------------------------%% append_chunk_end(Pid) ->  ok | {error, elogin} | {error, echunk} %%			     | {error, econn}%%	Pid = pid()%%%% Description:  End appending of chunks to remote file.%%--------------------------------------------------------------------------append_chunk_end(Pid) ->    call(Pid, chunk_end, atom).%%--------------------------------------------------------------------------%% append(Pid, LocalFileName, RemotFileName) -> ok | {error, epath} %%                                          | {error, elogin} | {error, econn}%%	Pid = pid()%%	LocalFileName = RemotFileName = string()%%%% Description:  Append the local file to the remote file%%--------------------------------------------------------------------------append(Pid, LocalFileName) ->    append(Pid, LocalFileName, LocalFileName).append(Pid, LocalFileName, RemotFileName) ->    call(Pid, {append, LocalFileName, RemotFileName}, atom).%%--------------------------------------------------------------------------%% append_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %%				  | {error, enotbinary} | {error, econn}%%	Pid = pid()%%	Bin = binary()%%	RemoteFile = string()%%%% Purpose:  Append a binary to a remote file.%%--------------------------------------------------------------------------append_bin(Pid, Bin, RemoteFile) when binary(Bin) ->    call(Pid, {append_bin, Bin, RemoteFile}, atom);append_bin(_Pid, _Bin, _RemoteFile) ->    {error, enotbinary}.%%--------------------------------------------------------------------------%% quote(Pid, Cmd) -> ok%%	Pid = pid()%%	Cmd = string()%%%% Description: Send arbitrary ftp command.%%--------------------------------------------------------------------------quote(Pid, Cmd) when list(Cmd) ->    call(Pid, {quote, Cmd}, atom).%%--------------------------------------------------------------------------%% close(Pid) -> ok%%	Pid = pid()%%%% Description:  End the ftp session.%%--------------------------------------------------------------------------close(Pid) ->    cast(Pid, close),    ok.%%--------------------------------------------------------------------------%% force_active(Pid) -> ok%%	Pid = pid()%%%% Description: Force connection to use active mode. %%--------------------------------------------------------------------------force_active(Pid) ->    error_logger:info_report("This function is deprecated use the mode flag "			     "to open/[1,2,3] instead", []),    call(Pid, force_active, atom).%%--------------------------------------------------------------------------%% formaterror(Tag) -> string()%%	Tag = atom() | {error, atom()}%%%% Description:  Return diagnostics.%%--------------------------------------------------------------------------formaterror(Tag) ->  ftp_response:error_string(Tag).%%%========================================================================%%% gen_server callback functions %%%========================================================================%%-------------------------------------------------------------------------%% init(Args) -> {ok, State} | {ok, State, Timeout} | {stop, Reason}%% Description: Initiates the erlang process that manages a ftp connection.%%-------------------------------------------------------------------------init([{client, ClientPid}, Flags]) ->    process_flag(trap_exit, true),    erlang:monitor(process, ClientPid),    inet_db:start(),    {ok, LDir} = file:get_cwd(),    State = case is_debug(Flags) or is_trace(Flags) of		true ->		    dbg:tracer(),		    dbg:p(all, [call]),		    case  is_debug(Flags) of 			true ->			    dbg:tp(ftp, [{'_', [], [{return_trace}]}]),			    dbg:tp(ftp_response, [{'_', [], 						   [{return_trace}]}]),			    dbg:tp(ftp_progress, [{'_', [], 						   [{return_trace}]}]); 			false -> %trace			    dbg:tpl(ftp, [{'_', [], [{return_trace}]}]),			    dbg:tpl(ftp_response, [{'_', [], 						    [{return_trace}]}]),			    dbg:tpl(ftp_progress, [{'_', [], 						    [{return_trace}]}])  		    end,		    #state{ldir = LDir};		false ->		    case is_verbose(Flags) of			true ->			    #state{verbose = true, ldir = LDir};			false ->			    #state{ldir = LDir}  		    end	    end,    process_flag(priority, low),     {ok, State#state{owner = ClientPid,	   ip_v6_disabled = is_ipv6_disabled(Flags)}}.%%--------------------------------------------------------------------------%% handle_call(Request, From, State) -> {reply, Reply, State} |%%                                      {reply, Reply, State, Timeout} |%%                                      {noreply, State}               |%%                                      {noreply, State, Timeout}      |%%                                      {stop, Reason, Reply, State}   |%% Description: Handle incoming requests. %%-------------------------------------------------------------------------handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid ->    {reply, {error, not_connection_owner}, State};handle_call({_, {open, ip_comm, Opts}}, From, State) ->    case key_search(host, Opts, undefined) of	undefined ->	    {stop, normal, {error, ehost}, State};	Host ->	    IsPosInt = fun(Int) when is_integer(Int), Int > 0 ->			       true;			  (_) -> 			       false		       end,	    	    IsModeAtom = fun(active) ->				 true;			    (passive) ->				 true;			    (_) ->				 false			 end,	    	    Mode = check_option(IsModeAtom,				key_search(mode, Opts, ?DEFAULT_MODE),				?DEFAULT_MODE),	    Port = check_option(IsPosInt, key_search(port, Opts, ?FTP_PORT), 				?FTP_PORT),	    Timeout = check_option(IsPosInt, key_search(timeout, Opts, 							?CONNECTION_TIMEOUT),				   ?CONNECTION_TIMEOUT),	    ProgressOptions = key_search(progress, Opts, ignore),	    	    setup_ctrl_connection(Host, Port, Timeout, 				  State#state{client = From, mode = Mode,					      progress = 					      progress(ProgressOptions)})    end;	handle_call({_, force_active}, _, State) ->    {reply, ok, State#state{mode = active}};handle_call({_, {user, User, Password}}, From, State) ->    handle_user(User, Password, "", State#state{client = From});handle_call({_, {user, User, Password, Acc}}, From, State) ->    handle_user(User, Password, Acc, State#state{client = From});   handle_call({_, {account, Acc}}, From, State)->    handle_user_account(Acc, State#state{client = From});handle_call({_, pwd}, From, #state{chunk = false} = State) ->    send_ctrl_message(State, mk_cmd("PWD", [])),     activate_ctrl_connection(State),    {noreply, State#state{client = From, caller = pwd}};handle_call({_, lpwd}, From,  #state{ldir = LDir} = State) ->    {reply, {ok, LDir}, State#state{client = From}};handle_call({_, {cd, Dir}}, From,  #state{chunk = false} 	    = State) ->    send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])),     activate_ctrl_connection(State),    {noreply, State#state{client = From, caller = cd}};handle_call({_,{lcd, Dir}}, _From, #state{ldir = LDir0} = State) ->    LDir = filename:absname(Dir, LDir0),    case file:read_file_info(LDir) of %% FIX better check that LDir is a dir.	{ok, _ } ->	    {reply, ok, State#state{ldir = LDir}};	_  ->	    {reply, {error, epath}, State}    end;handle_call({_, {dir, Len, Dir}}, {_Pid, _} = From, 	    #state{chunk = false} = State) ->    setup_data_connection(State#state{caller = {dir, Dir, Len},				      client = From});handle_call({_, {rename, CurrFile, NewFile}}, From,	    #state{chunk = false} = State) ->    send_ctrl_message(State, mk_cmd("RNFR ~s", [CurrFile])),    activate_ctrl_connection(State),    {noreply, State#state{caller = {rename, NewFile}, client = From}};handle_call({_, {delete, File}}, {_Pid, _} = From, 	    #state{chunk = false} = State) ->    send_ctrl_message(State, mk_cmd("DELE ~s", [File])),    activate_ctrl_connection(State),    {noreply, State#state{client = From}};handle_call({_, {mkdir, Dir}}, From,  #state{chunk = false} = State) ->    send_ctrl_message(State, mk_cmd("MKD ~s", [Dir])),    activate_ctrl_connection(State),    {noreply, State#state{client = From}};handle_call({_,{rmdir, Dir}}, From, #state{chunk = false} = State) ->    send_ctrl_message(State, mk_cmd("RMD ~s", [Dir])),    activate_ctrl_connection(State),    {noreply, State#state{client = From}};handle_call({_,{type, Type}}, From,  #state{chunk = false} 	    = State) ->      case Type of	ascii ->	    send_ctrl_message(State, mk_cmd("TYPE A", [])),	    activate_ctrl_connection(State),	    {noreply, State#state{caller = type, type = ascii, 				  client = From}};	binary ->	    send_ctrl_message(State, mk_cmd("TYPE I", [])),	    activate_ctrl_connection(State),	    {noreply, State#state{caller = type, type = binary, 				  client = From}};	_ ->	    {reply, {error, etype}, State}    end;handle_call({_,{recv, RemoteFile, LocalFile}}, From, 	    #state{chunk = false, ldir = LocalDir} = State) ->    progress_report({remote_file, RemoteFile}, State),    NewLocalFile = filename:absname(LocalFile, LocalDir),    case file_open(NewLocalFile, write) of	{ok, Fd} ->	    setup_data_connection(State#state{client = From,					      caller = 					      {recv_file, 					       RemoteFile, Fd}});	{error, _What} ->	    {reply, {error, epath}, State}    end;handle_call({_, {recv_bin, RemoteFile}}, From, #state{chunk = false} = 	    State) ->    setup_data_connection(State#state{caller = {recv_bin, RemoteFile},				      client = From});handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false} 	    = State) ->    setup_data_connection(State#state{caller = {start_chunk_transfer,						"RETR", RemoteFile},				      client = From});handle_call({_, recv_chunk}, _, #state{chunk = false} = State) ->    {reply, {error, "ftp:recv_chunk_start/2 not called"}, State}; handle_call({_, recv_chunk}, From, #state{chunk = true} = State) ->    activate_data_connection(State),    {noreply, State#state{client = From, caller = recv_chunk}};    handle_call({_, {send, LocalFile, RemoteFile}}, From, 	    #state{chunk = false, ldir = LocalDir} = State) ->    progress_report({local_file, filename:absname(LocalFile, LocalDir)}, 		    State),    setup_data_connection(State#state{caller = {transfer_file,						   {"STOR", 						    LocalFile, RemoteFile}},					 client = From});handle_call({_, {append, LocalFile, RemoteFile}}, From, 	    #state{chunk = false} = State) ->    setup_data_connection(State#state{caller = {transfer_file,						{"APPE", 						 LocalFile, RemoteFile}},				      client = From});handle_call({_, {send_bin, Bin, RemoteFile}}, From, 	    #state{chunk = false} = State) ->    setup_data_connection(State#state{caller = {transfer_data,					       {"STOR", Bin, RemoteFile}},				      client = From});handle_call({_,{append_bin, Bin, RemoteFile}}, From, 	    #state{chunk = false} = State) ->    setup_data_connection(State#state{caller = {transfer_data,						{"APPE", Bin, RemoteFile}},				      client = From});handle_call({_, {send_chunk_start, RemoteFile}}, From, #state{chunk = false} 	    = State) ->    setup_data_connection(State#state{caller = {start_chunk_transfer,						"STOR", RemoteFile},				      client = From});handle_call({_, {append_chunk_start, RemoteFile}}, From, #state{chunk = false} 	    = State) ->    setup_data_connection(State#state{caller = {start_chunk_transfer,						"APPE", RemoteFile},				      client = From});handle_call({_, {transfer_chunk, Bin}}, _, #state{chunk = true} = State) ->    send_data_message(State, Bin),    {reply, ok, State};handle_call({_, chunk_end}, From, #state{chunk = true} = State) ->    close_data_connection(State),    activate_ctrl_connection(State),    {noreply, State#state{client = From, dsock = undefined, 			  caller = end_chunk_transfer, chunk = false}};handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State) ->    send_ctrl_message(State, mk_cmd(Cmd, [])),    activate_ctrl_connection(State),    {noreply, State#state{client = From, caller = quote}};handle_call(_, _, #state{chunk = true} = State) ->    {reply, {error, echunk}, State};%% Catch all -  This can only happen if the application programmer writes %% really bad code that violates the API.handle_call(Request, _Timeout, State) ->    {stop, {'API_violation_connection_closed', Request},     {error, {connection_terminated, 'API_violation'}}, State}.%%--------------------------------------------------------------------------%% handle_cast(Request, State) -> {noreply, State} | %%                                {noreply, State, Timeout} |%%                                {stop, Reason, State} %% Description: Handles cast messages.         %%-------------------------------------------------------------------------handle_cast({Pid, close}, #state{owner = Pid} = State) ->    send_ctrl_message(State, mk_cmd("QUIT", [])),    close_ctrl_connection(State),    close_data_connection(State),    {stop, normal, State#state{csock = undefined, dsock = undefined}};handle_cast({Pid, close}, State) ->    error_logger:info_report("A none owner process ~p tried to close an "			     "ftp connection: ~n", [Pid]),    {noreply, State};%% Catch all -  This can oly happen if the application programmer writes %% really bad code that violates the API.handle_cast(Msg, State) ->  {stop, {'API_violation_connection_colsed', Msg}, State}.%%--------------------------------------------------------------------------%% handle_info(Msg, State) -> {noreply, State} | {noreply, State, Timeout} |%%			      {stop, Reason, State}%% Description: Handles tcp messages from the ftp-server.%% Note: The order of the function clauses is significant.%%--------------------------------------------------------------------------handle_info(timeout, #state{caller = open} = State) ->    {stop, timeout, State};handle_info(timeout, State) ->    {noreply, State};%%% Data socket messages %%%handle_info({tcp, Socket, Data}, 	    #state{dsock = Socket, 		   caller = {recv_file, Fd}} = State) ->        file_write(binary_to_list(Data), Fd),    progress_report({binary, Data}, State),    activate_data_connection(State),    {noreply, State};handle_info({tcp, Socket, Data}, #state{dsock = Socket, client = From,						caller = recv_chunk} 	    = State)  ->        gen_server:reply(From, {ok, Data}),    {noreply, State#state{client = undefined, data = <<>>}};handle_info({tcp, Socket, Data}, #state{dsock = Socket} = State) ->    activate_data_connection(State),    {noreply, State#state{data = <<(State#state.data)/binary,				  Data/binary>>}};handle_info({tcp_closed, Socket}, #state{dsock = Socket,					 caller = {recv_file, Fd}} 	    = State) ->    file_close(Fd),

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?