ftp.erl

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

ERL
1,598
字号
			 {error, epath});%%--------------------------------------------------------------------------%% File handling - transfer_*handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_file, Fd}} 		   = State) ->    NewState = accept_data_connection(State),    send_file(Fd, NewState); handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}} 		   = State) ->    NewState = accept_data_connection(State),    send_data_message(NewState, Bin),    close_data_connection(NewState),    activate_ctrl_connection(NewState),    {noreply, NewState#state{caller = transfer_data_second_phase,			     dsock = undefined}};%%--------------------------------------------------------------------------%% Defaulthandle_ctrl_result({Status, Lines}, #state{client = From} = State)   when From =/= undefined ->    ctrl_result_response(Status, State, {error, Lines}).%%--------------------------------------------------------------------------%% Help functions to handle_ctrl_result%%--------------------------------------------------------------------------ctrl_result_response(pos_compl, #state{client = From} = State, _)  ->    gen_server:reply(From, ok),    {noreply, State#state{client = undefined, caller = undefined}};ctrl_result_response(Status, #state{client = From} = State, _) whenStatus == etnospc; Status == epnospc; Status == efnamena; Status == econn ->%Status == etnospc; Status == epnospc; Status == econn ->    gen_server:reply(From, {error, Status}),%%    {stop, normal, {error, Status}, State#state{client = undefined}};    {stop, normal, State#state{client = undefined}};ctrl_result_response(_, #state{client = From} = State, ErrorMsg) ->    gen_server:reply(From, ErrorMsg),    {noreply, State#state{client = undefined, caller = undefined}}.%%--------------------------------------------------------------------------handle_caller(#state{caller = {dir, Dir, Len}} = State) ->    Cmd = case Len of	      short -> "NLST";	      long -> "LIST"	  end,    case Dir of 	"" ->	    send_ctrl_message(State, mk_cmd(Cmd, ""));	_ ->	    send_ctrl_message(State, mk_cmd(Cmd ++ " ~s", [Dir]))    end,    activate_ctrl_connection(State),    {noreply, State#state{caller = {dir, Dir}}};     handle_caller(#state{caller = {recv_bin, RemoteFile}} = State) ->    send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),    activate_ctrl_connection(State),    {noreply, State#state{caller = recv_bin}};handle_caller(#state{caller = {start_chunk_transfer, Cmd, RemoteFile}} = 	      State) ->    send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),    activate_ctrl_connection(State),    {noreply, State#state{caller = start_chunk_transfer}};handle_caller(#state{caller = {recv_file, RemoteFile, Fd}} = State) ->    send_ctrl_message(State, mk_cmd("RETR ~s", [RemoteFile])),     activate_ctrl_connection(State),    {noreply, State#state{caller = {recv_file, Fd}}};handle_caller(#state{caller = {transfer_file, {Cmd, LocalFile, RemoteFile}},		     ldir = LocalDir, client = From} = State) ->    case file_open(filename:absname(LocalFile, LocalDir), read) of	{ok, Fd} ->	    send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),	    activate_ctrl_connection(State),	    {noreply, State#state{caller = {transfer_file, Fd}}};	{error, _} ->	    gen_server:reply(From, {error, epath}),	    {noreply, State#state{client = undefined, caller = undefined,				  dsock = undefined}}     end;handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} = 	      State) ->    send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),    activate_ctrl_connection(State),    {noreply, State#state{caller = {transfer_data, Bin}}}.%%  ----------- FTP SERVER COMMUNICATION  ------------------------- %% Connect to FTP server at Host (default is TCP port 21) %% in order to establish a control connection.setup_ctrl_connection(Host, Port, Timeout, State)->    MsTime = millisec_time(),    case connect(Host, Port, Timeout, State) of	{Ipv, {ok, CSock}} ->	    NewState = 		case Ipv of		    ipv4 -> 			State#state{csock = CSock, ip_v6_disabled = true};		    ipv6 ->			State#state{csock = CSock}		end,	    activate_ctrl_connection(NewState),	    case Timeout - (millisec_time() - MsTime) of		Timeout2 when (Timeout2 >= 0) ->		    {noreply, NewState#state{caller = open}, Timeout2};		_ ->		    %% Oups: Simulate timeout		    self() ! timeout,		    {noreply, NewState#state{caller = open}}	    end;	{_,{error, _}} ->	    gen_server:reply(State#state.client, {error, ehost}),	    {stop, normal, State#state{client = undefined}}    end.setup_data_connection(#state{mode   = active, 			     caller = Caller, 			     csock  = CSock} = State) ->        IntToString = fun(Element) -> integer_to_list(Element) end,        case (catch inet:sockname(CSock)) of	{ok, {{_, _, _, _, _, _, _, _} = IP, _}} ->	    {ok, LSock} = 		gen_tcp:listen(0, [{ip, IP}, {active, false},				   inet6, binary, {packet, 0}]),	    {ok, Port} = inet:port(LSock),	    Cmd = mk_cmd("EPRT |2|~s:~s:~s:~s:~s:~s:~s:~s|~s|", 			 lists:map(IntToString, 				   tuple_to_list(IP) ++ [Port])),	    send_ctrl_message(State, Cmd),	    activate_ctrl_connection(State),  	    {noreply, State#state{caller = {setup_data_connection, 					    {LSock, Caller}}}};	{ok, {{_,_,_,_} = IP, _}} ->	    	    {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false},					     binary, {packet, 0}]),	    {ok, Port} = inet:port(LSock),	    {IP1, IP2, IP3, IP4} = IP,	    {Port1, Port2} = {Port div 256, Port rem 256},	    send_ctrl_message(State, 			      mk_cmd("PORT ~w,~w,~w,~w,~w,~w",				     [IP1, IP2, IP3, IP4, Port1, Port2])),	    activate_ctrl_connection(State),	    {noreply, State#state{caller = {setup_data_connection, 					    {LSock, Caller}}}}    end;setup_data_connection(#state{mode = passive, ip_v6_disabled = false,			     caller = Caller} = State) ->    send_ctrl_message(State, mk_cmd("EPSV", [])),    activate_ctrl_connection(State),    {noreply, State#state{caller = {setup_data_connection, Caller}}};setup_data_connection(#state{mode = passive, ip_v6_disabled = true,			     caller = Caller} = State) ->    send_ctrl_message(State, mk_cmd("PASV", [])),    activate_ctrl_connection(State),    {noreply, State#state{caller = {setup_data_connection, Caller}}}.connect(Host = {_,_,_,_}, Port, TimeOut, _) ->    {ipv4, gen_tcp:connect(Host, Port,[binary, {packet, 0}, {active, false}] ,		    TimeOut)};connect(Host = {_,_,_,_,_,_,_,_}, Port, TimeOut, 	#state{ip_v6_disabled = false}) ->    {ipv6, gen_tcp:connect(Host, Port,		    [binary, {packet, 0}, {active, false}, inet6],		    TimeOut)};    connect(Host, Port, TimeOut, #state{ip_v6_disabled = false}) ->    {Opts, NewHost, Ipv} = 	case (inet:getaddr(Host, inet6)) of	    %% If an ipv4-mapped ipv6 address is returned 	    %% use ipv4 directly as some ftp-servers does not	    %% handle "ip4-ipv6-compatiblity" mode well!	    {ok, IP = {0, 0, 0, 0, 0, 16#ffff, _, _}} ->		case inet:getaddr(Host, inet) of		    {ok,NewIP} -> 			{[binary, {packet, 0}, {active, false}], NewIP, ipv4};		    _Error ->			{[binary, {packet, 0}, {active, false}, inet6], 			 IP,ipv6}		end;	    {ok, IP} ->		{[binary, {packet, 0}, {active, false}, inet6], IP, ipv6};	    {error, _} ->		{[binary, {packet, 0}, {active, false}], Host, ipv4}	end,    {Ipv, gen_tcp:connect(NewHost, Port, Opts, TimeOut)};connect(Host, Port, TimeOut, #state{ip_v6_disabled = true}) ->     Opts = [binary, {packet, 0}, {active, false}],    {ipv4, gen_tcp:connect(Host, Port, Opts, TimeOut)}.accept_data_connection(#state{mode = active,			      dsock = {lsock, LSock}} = State) ->    {ok, Socket} = gen_tcp:accept(LSock),    gen_tcp:close(LSock),    State#state{dsock = Socket};accept_data_connection(#state{mode = passive} = State) ->    State.send_ctrl_message(#state{csock = Socket,verbose=Verbose}, Message) ->%    io:format("Sending: ~p~n",[Message]),    verbose(lists:flatten(Message),Verbose,send),    send_message(Socket, Message).send_data_message(#state{dsock = Socket}, Message) ->    send_message(Socket, Message).send_message(Socket, Message) ->    case gen_tcp:send(Socket, Message) of	ok ->	    ok;	{error, Reason} ->	    error_logger:error_report("gen_tcp:send/2 failed for "				      "reason ~p~n", [Reason]),	    %% If tcp does not work the only option is to terminate,	    %% this is the expected behavior under these circumstances.	    exit(normal) %% User will get error message from terminate/2    end.activate_ctrl_connection(#state{csock = Socket, ctrl_data = {<<>>, _, _}}) ->    activate_connection(Socket);activate_ctrl_connection(#state{csock = Socket}) ->    %% We have already received at least part of the next control message,    %% that has been saved in ctrl_data, process this first.    self() ! {tcp, Socket, <<>>}.activate_data_connection(#state{dsock = Socket}) ->    activate_connection(Socket).activate_connection(Socket) ->    inet:setopts(Socket, [{active, once}]).close_ctrl_connection(#state{csock = undefined}) ->    ok;close_ctrl_connection(#state{csock = Socket}) ->    close_connection(Socket).close_data_connection(#state{dsock = undefined}) ->    ok;close_data_connection(#state{dsock = {lsock, Socket}}) ->    close_connection(Socket);close_data_connection(#state{dsock = Socket}) ->    close_connection(Socket).close_connection(Socket) ->    gen_tcp:close(Socket).%%  ------------ FILE HANDELING  ----------------------------------------   send_file(Fd, State) ->    case file_read(Fd) of	{ok, N, Bin} when N > 0->	    send_data_message(State, Bin),	    progress_report({binary, Bin}, State),	    send_file(Fd, State);	{ok, _, _} ->	    file_close(Fd),	    close_data_connection(State),	    progress_report({transfer_size, 0}, State),	    activate_ctrl_connection(State),	    {noreply, State#state{caller = transfer_file_second_phase,				  dsock = undefined}};        {error, Reason} ->	    gen_server:reply(State#state.client, {error, Reason}),	    {stop, normal, State#state{client = undefined}}    end.file_open(File, Option) ->  file:open(File, [raw, binary, Option]).file_close(Fd) ->  file:close(Fd).file_read(Fd) ->				    case file:read(Fd, ?FILE_BUFSIZE) of	{ok, Bytes} ->	    {ok, size(Bytes), Bytes};	eof ->	    {ok, 0, []};	Other ->	    Other    end.file_write(Bytes, Fd) ->    file:write(Fd, Bytes).%% --------------  MISC ---------------------------------------------- call(GenServer, Msg, Format) ->    call(GenServer, Msg, Format, infinity).call(GenServer, Msg, Format, Timeout) ->       Result = (catch gen_server:call(GenServer, {self(), Msg}, Timeout)),    case Result of	{ok, Bin} when binary(Bin), Format == string ->	    {ok, binary_to_list(Bin)};	{'EXIT', _} ->	    {error, eclosed};	Result ->	    Result    end.cast(GenServer, Msg) ->    gen_server:cast(GenServer, {self(), Msg}).mk_cmd(Fmt, Args) ->    [io_lib:format(Fmt, Args)| [?CR, ?LF]].		% Deep list ok.pwd_result(Lines) ->    {_, [?DOUBLE_QUOTE | Rest]} = 	lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Lines),    {Dir, _} =	lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Rest),    Dir.is_verbose(Params) ->     check_param(verbose, Params).is_debug(Flags) ->     check_param(debug, Flags).is_trace(Flags) ->     check_param(trace, Flags).is_ipv6_disabled(Flags) ->     check_param(ip_v6_disabled, Flags).check_param(Param, Params) ->     lists:member(Param, Params).key_search(Key, List, Default)->	         case lists:keysearch(Key, 1, List) of	{value, {_,Val}} ->	    Val;	false ->	    Default    end.check_option(Pred, Value, Default) ->    case Pred(Value) of	true ->	    Value;	false ->	    Default    end.verbose(Lines, true, Direction) ->    DirStr =	case Direction of	    send ->		"Sending: ";	    _ ->		"Receiving: "	end,    Str = string:strip(string:strip(Lines, right, ?LF), right, ?CR),    erlang:display(DirStr++Str);verbose(_, false,_) ->    ok.ensure_started() ->    %% Start of the inets application should really be handled by the     %% application using inets.     case application:start(inets) of	{error,{already_started,inets}} ->	    ok;	{error,{{already_started, _}, % Started as an included application		{inets_app,start, _}}} ->	    ok;	ok ->	    error_logger:info_report("The inets application was not started."				     " Has now been started as a temporary" 				     " application.")    end.progress(Options) ->    ftp_progress:start_link(Options).progress_report(_, #state{progress = ignore}) ->    ok;progress_report(stop, #state{progress = ProgressPid}) ->    ftp_progress:stop(ProgressPid);progress_report({binary, Data}, #state{progress = ProgressPid}) ->    ftp_progress:report(ProgressPid, {transfer_size, size(Data)});progress_report(Report,  #state{progress = ProgressPid}) ->    ftp_progress:report(ProgressPid, Report).millisec_time() ->    {A,B,C} = erlang:now(),    A*1000000000+B*1000+(C div 1000).

⌨️ 快捷键说明

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