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 + -
显示快捷键?