ftp.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,598 行 · 第 1/4 页
ERL
1,598 行
progress_report({transfer_size, 0}, State), activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, data = <<>>}};handle_info({tcp_closed, Socket}, #state{dsock = Socket, client = From, caller = recv_chunk} = State) -> gen_server:reply(From, ok), {noreply, State#state{dsock = undefined, client = undefined, data = <<>>, caller = undefined, chunk = false}};handle_info({tcp_closed, Socket}, #state{dsock = Socket, caller = recv_bin, data = Data} = State) -> activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, data = <<>>, caller = {recv_bin, Data}}};handle_info({tcp_closed, Socket}, #state{dsock = Socket, data = Data, caller = {handle_dir_result, Dir}} = State) -> activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, caller = {handle_dir_result, Dir, Data},% data = <<?CR,?LF>>}}; data = <<>>}}; handle_info({tcp_error, Socket, Reason}, #state{dsock = Socket, client = From} = State) -> gen_server:reply(From, {error, Reason}), close_data_connection(State), {noreply, State#state{dsock = undefined, client = undefined, data = <<>>, caller = undefined, chunk = false}};%%% Ctrl socket messages %%%handle_info({tcp, Socket, Data}, #state{csock = Socket, verbose = Verbose, caller = Caller, client = From, ctrl_data = {CtrlData, AccLines, LineStatus}} = State) -> case ftp_response:parse_lines(<<CtrlData/binary, Data/binary>>, AccLines, LineStatus) of {ok, Lines, NextMsgData} -> verbose(Lines, Verbose, 'receive'), CtrlResult = ftp_response:interpret(Lines), case Caller of quote -> gen_server:reply(From, string:tokens(Lines, [?CR, ?LF])), {noreply, State#state{client = undefined, caller = undefined, ctrl_data = {NextMsgData, [], start}}}; _ -> handle_ctrl_result(CtrlResult, State#state{ctrl_data = {NextMsgData, [], start}}) end; {continue, NewCtrlData} -> activate_ctrl_connection(State), {noreply, State#state{ctrl_data = NewCtrlData}} end;handle_info({tcp_closed, Socket}, #state{csock = Socket}) -> %% If the server closes the control channel it is %% the expected behavior that connection process terminates. exit(normal); %% User will get error message from terminate/2handle_info({tcp_error, Socket, Reason}, _) -> error_logger:error_report("tcp_error on socket: ~p for reason: ~p~n", [Socket, 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%% Monitor messages - if the process owning the ftp connection goes%% down there is no point in continuing.handle_info({'DOWN', _Ref, _Type, _Process, normal}, State) -> {stop, normal, State#state{client = undefined}};handle_info({'DOWN', _Ref, _Type, _Process, shutdown}, State) -> {stop, normal, State#state{client = undefined}}; handle_info({'DOWN', _Ref, _Type, _Process, timeout}, State) -> {stop, normal, State#state{client = undefined}}; handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) -> {stop, {stopped, {'EXIT', Process, Reason}}, State#state{client = undefined}};handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) -> error_logger:info_report("Progress reporting stopped for reason ~p~n", Reason), {noreply, State#state{progress = ignore}}; %% Catch all - throws away unknown messages (This could happen by "accident"%% so we do not want to crash, but we make a log entry as it is an%% unwanted behaviour.) handle_info(Info, State) -> error_logger:info_report("ftp : ~p : Unexpected message: ~p\n", [self(), Info]), {noreply, State}.%%--------------------------------------------------------------------------%% terminate/2 and code_change/3%%--------------------------------------------------------------------------terminate(normal, State) -> %% If terminate reason =/= normal the progress reporting process will %% be killed by the exit signal. progress_report(stop, State), do_termiante({error, econn}, State);terminate(Reason, State) -> error_logger:error_report("Ftp connection closed due to: ~p~n", [Reason]), do_termiante({error, eclosed}, State).do_termiante(ErrorMsg, State) -> close_data_connection(State), close_ctrl_connection(State), case State#state.client of undefined -> ok; From -> gen_server:reply(From, ErrorMsg) end, ok. code_change(_, State, _) -> {ok, State}.%%%=========================================================================%% Start/stop%%%=========================================================================%%--------------------------------------------------------------------------%% start_link_sup([Args, Options]) -> {ok, Pid} | {error, Reason} %% %% Description: Callback function for the ftp supervisor. It is called %% : when open/[1,3] calls ftp_sup:start_child/1 to start an %% : instance of the ftp process.%%--------------------------------------------------------------------------start_link_sup([Args, Options]) -> gen_server:start_link(?MODULE, Args, Options).%%% Stop functionality is handled by close/1%%%========================================================================%%% Internal functions%%%========================================================================%%--------------------------------------------------------------------------%%% Help functions to handle_call and/or handle_ctrl_result%%--------------------------------------------------------------------------%% User handling handle_user(User, Password, Acc, State) -> send_ctrl_message(State, mk_cmd("USER ~s", [User])), activate_ctrl_connection(State), {noreply, State#state{caller = {handle_user, Password, Acc}}}.handle_user_passwd(Password, Acc, State) -> send_ctrl_message(State, mk_cmd("PASS ~s", [Password])), activate_ctrl_connection(State), {noreply, State#state{caller = {handle_user_passwd, Acc}}}.handle_user_account(Acc, State) -> send_ctrl_message(State, mk_cmd("ACCT ~s", [Acc])), activate_ctrl_connection(State), {noreply, State#state{caller = handle_user_account}}.%%--------------------------------------------------------------------------%% handle_ctrl_result %%--------------------------------------------------------------------------%%--------------------------------------------------------------------------%% Handling of control connection setuphandle_ctrl_result({pos_compl, _}, #state{caller = open, client = From} = State) -> gen_server:reply(From, {ok, self()}), {noreply, State#state{client = undefined, caller = undefined }};handle_ctrl_result({_, Lines}, #state{caller = open} = State) -> ctrl_result_response(econn, State, {error, Lines});%%--------------------------------------------------------------------------%% Data connection setup active mode handle_ctrl_result({pos_compl, _Lines}, #state{mode = active, caller = {setup_data_connection, {LSock, Caller}}} = State) -> handle_caller(State#state{caller = Caller, dsock = {lsock, LSock}});handle_ctrl_result({Status, Lines}, #state{mode = active, caller = {setup_data_connection, {LSock, _}}} = State) -> close_connection(LSock), ctrl_result_response(Status, State, {error, Lines});%% Data connection setup passive mode handle_ctrl_result({pos_compl, Lines}, #state{mode = passive, ip_v6_disabled = false, client=From, caller = {setup_data_connection, Caller}, csock = CSock, timeout = Timeout} = State) -> [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")), {ok, {IP, _}} = inet:peername(CSock), case connect(IP, list_to_integer(PortStr), Timeout, State) of {_,{ok, Socket}} -> handle_caller(State#state{caller = Caller, dsock = Socket}); {_,{error,Reason}} -> gen_server:reply(From,{error,Reason}), {noreply,State#state{client = undefined, caller = undefined}} end;handle_ctrl_result({pos_compl, Lines}, #state{mode = passive, ip_v6_disabled = true, client=From, caller = {setup_data_connection, Caller}, timeout = Timeout} = State) -> {_, [?LEFT_PAREN | Rest]} = lists:splitwith(fun(?LEFT_PAREN) -> false; (_) -> true end, Lines), {NewPortAddr, _} = lists:splitwith(fun(?RIGHT_PAREN) -> false; (_) -> true end, Rest), [A1, A2, A3, A4, P1, P2] = lists:map(fun(X) -> list_to_integer(X) end, string:tokens(NewPortAddr, [$,])), case connect({A1, A2, A3, A4}, (P1 * 256) + P2, Timeout, State) of {_,{ok,Socket}} -> handle_caller(State#state{caller = Caller, dsock = Socket}); {_,{error,Reason}} -> gen_server:reply(From,{error,Reason}), {noreply,State#state{client = undefined, caller = undefined}} end;%% FTP server does not support passive mode try to fallback on active modehandle_ctrl_result(_, #state{mode = passive, caller = {setup_data_connection, Caller}} = State) -> setup_data_connection(State#state{mode = active, caller = Caller}); %%--------------------------------------------------------------------------%% User handling handle_ctrl_result({pos_interm, _}, #state{caller = {handle_user, PassWord, Acc}} = State) -> handle_user_passwd(PassWord, Acc, State);handle_ctrl_result({Status, _}, #state{caller = {handle_user, _, _}} = State) -> ctrl_result_response(Status, State, {error, euser});%% Accounts handle_ctrl_result({pos_interm_acct, _}, #state{caller = {handle_user_passwd, Acc}} = State) when Acc =/= "" -> handle_user_account(Acc, State);handle_ctrl_result({Status, _}, #state{caller = {handle_user_passwd, _}} = State) -> ctrl_result_response(Status, State, {error, euser});%%--------------------------------------------------------------------------%% Print current working directoryhandle_ctrl_result({pos_compl, Lines}, #state{caller = pwd, client = From} = State) -> Dir = pwd_result(Lines), gen_server:reply(From, {ok, Dir}), {noreply, State#state{client = undefined, caller = undefined}};%%--------------------------------------------------------------------------%% Directory listing handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State) -> NewState = accept_data_connection(State), activate_data_connection(NewState), {noreply, NewState#state{caller = {handle_dir_result, Dir}}};handle_ctrl_result({pos_compl, _}, #state{caller = {handle_dir_result, Dir, Data}, client = From} = State) -> case Dir of "" -> % Current directory gen_server:reply(From, {ok, Data}), {noreply, State#state{client = undefined, caller = undefined}}; _ -> %% If there is only one line it might be a directory with on %% file but it might be an error message that the directory %% was not found. So in this case we have to endure a little %% overhead to be able to give a good return value. Alas not %% all ftp implementations behave the same and returning %% an error string is allowed by the FTP RFC. case lists:dropwhile(fun(?CR) -> false;(_) -> true end, binary_to_list(Data)) of L when L == [?CR, ?LF]; L == [] -> send_ctrl_message(State, mk_cmd("PWD", [])), activate_ctrl_connection(State), {noreply, State#state{caller = {handle_dir_data, Dir, Data}}}; _ -> gen_server:reply(From, {ok, Data}), {noreply, State#state{client = undefined, caller = undefined}} end end;handle_ctrl_result({pos_compl, Lines}, #state{caller = {handle_dir_data, Dir, DirData}} = State) -> OldDir = pwd_result(Lines), send_ctrl_message(State, mk_cmd("CWD ~s", [Dir])), activate_ctrl_connection(State), {noreply, State#state{caller = {handle_dir_data_second_phase, OldDir, DirData}}};handle_ctrl_result({Status, _}, #state{caller = {handle_dir_data, _, _}} = State) -> ctrl_result_response(Status, State, {error, epath});handle_ctrl_result(S={_Status, _}, #state{caller = {handle_dir_result, _, _}} = State) -> %% OTP-5731, macosx ctrl_result_response(S, State, {error, epath});handle_ctrl_result({pos_compl, _}, #state{caller = {handle_dir_data_second_phase, OldDir, DirData}} = State) -> send_ctrl_message(State, mk_cmd("CWD ~s", [OldDir])), activate_ctrl_connection(State), {noreply, State#state{caller = {handle_dir_data_third_phase, DirData}}};handle_ctrl_result({Status, _}, #state{caller = {handle_dir_data_second_phase, _, _}} = State) -> ctrl_result_response(Status, State, {error, epath});handle_ctrl_result(_, #state{caller = {handle_dir_data_third_phase, DirData}, client = From} = State) -> gen_server:reply(From, {ok, DirData}), {noreply, State#state{client = undefined, caller = undefined}};handle_ctrl_result({Status, _}, #state{caller = cd} = State) -> ctrl_result_response(Status, State, {error, epath});handle_ctrl_result(Status={epath, _}, #state{caller = {dir,_}} = State) -> ctrl_result_response(Status, State, {error, epath});%%--------------------------------------------------------------------------%% File renaminghandle_ctrl_result({pos_interm, _}, #state{caller = {rename, NewFile}} = State) -> send_ctrl_message(State, mk_cmd("RNTO ~s", [NewFile])), activate_ctrl_connection(State), {noreply, State#state{caller = rename_second_phase}}; handle_ctrl_result({Status, _}, #state{caller = {rename, _}} = State) -> ctrl_result_response(Status, State, {error, epath});handle_ctrl_result({Status, _}, #state{caller = rename_second_phase} = State) -> ctrl_result_response(Status, State, {error, epath});%%--------------------------------------------------------------------------%% File handling - recv_binhandle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State) -> NewState = accept_data_connection(State), activate_data_connection(NewState), {noreply, NewState};handle_ctrl_result({pos_compl, _}, #state{caller = {recv_bin, Data}, client = From} = State) -> gen_server:reply(From, {ok, Data}), close_data_connection(State), {noreply, State#state{client = undefined, caller = undefined}};handle_ctrl_result({Status, _}, #state{caller = recv_bin} = State) -> close_data_connection(State), ctrl_result_response(Status, State#state{dsock = undefined}, {error, epath});handle_ctrl_result({Status, _}, #state{caller = {recv_bin, _}} = State) -> close_data_connection(State), ctrl_result_response(Status, State#state{dsock = undefined}, {error, epath});%%--------------------------------------------------------------------------%% File handling - start_chunk_transferhandle_ctrl_result({pos_prel, _}, #state{client = From, caller = start_chunk_transfer} = State) -> NewState = accept_data_connection(State), gen_server:reply(From, ok), {noreply, NewState#state{chunk = true, client = undefined, caller = undefined}};%%--------------------------------------------------------------------------%% File handling - recv_filehandle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State) -> NewState = accept_data_connection(State), activate_data_connection(NewState), {noreply, NewState};handle_ctrl_result({Status, _}, #state{caller = {recv_file, Fd}} = State) -> file_close(Fd), close_data_connection(State), ctrl_result_response(Status, State#state{dsock = undefined},
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?