aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl')
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl1582
1 files changed, 0 insertions, 1582 deletions
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl
deleted file mode 100644
index be06ec654c..0000000000
--- a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl
+++ /dev/null
@@ -1,1582 +0,0 @@
-%% ``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 via the world wide web 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.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id: ftp.erl,v 1.2 2009/03/03 01:55:01 kostis Exp $
-%%
--module(ftp).
-
--behaviour(gen_server).
-
-%% This module implements an ftp client based on socket(3)/gen_tcp(3),
-%% file(3) and filename(3).
-%%
-
-
--define(OPEN_TIMEOUT, 60*1000).
--define(BYTE_TIMEOUT, 1000). % Timeout for _ONE_ byte to arrive. (ms)
--define(OPER_TIMEOUT, 300). % Operation timeout (seconds)
--define(FTP_PORT, 21).
-
-%% Client interface
--export([cd/2, close/1, delete/2, formaterror/1, help/0,
- lcd/2, lpwd/1, ls/1, ls/2,
- mkdir/2, nlist/1, nlist/2,
- open/1, open/2, open/3,
- pwd/1,
- recv/2, recv/3, recv_bin/2,
- recv_chunk_start/2, recv_chunk/1,
- rename/3, rmdir/2,
- send/2, send/3, send_bin/3,
- send_chunk_start/2, send_chunk/2, send_chunk_end/1,
- type/2, user/3,user/4,account/2,
- append/3, append/2, append_bin/3,
- append_chunk/2, append_chunk_end/1, append_chunk_start/2]).
-
-%% Internal
--export([init/1, handle_call/3, handle_cast/2,
- handle_info/2, terminate/2,code_change/3]).
-
-
-%%
-%% CLIENT FUNCTIONS
-%%
-
-%% open(Host)
-%% open(Host, Flags)
-%%
-%% Purpose: Start an ftp client and connect to a host.
-%% Args: Host = string(),
-%% Port = integer(),
-%% Flags = [Flag],
-%% Flag = verbose | debug
-%% Returns: {ok, Pid} | {error, ehost}
-
-%%Tho only option was the host in textual form
-open({option_list,Option_list})->
- %% Dbg = {debug,[trace,log,statistics]},
- %% Options = [Dbg],
- Options = [],
- {ok,Pid1}=case lists:keysearch(flags,1,Option_list) of
- {value,{flags,Flags}}->
- {ok, Pid} = gen_server:start_link(?MODULE, [Flags], Options);
- false ->
- {ok, Pid} = gen_server:start_link(?MODULE, [], Options)
- end,
- gen_server:call(Pid1, {open, ip_comm,Option_list}, infinity);
-
-
-%%The only option was the tuple form of the ip-number
-open(Host)when tuple(Host) ->
- open(Host, ?FTP_PORT, []);
-
-%%Host is the string form of the hostname
-open(Host)->
- open(Host,?FTP_PORT,[]).
-
-
-
-open(Host, Port) when integer(Port) ->
- open(Host,Port,[]);
-
-open(Host, Flags) when list(Flags) ->
- open(Host,?FTP_PORT, Flags).
-
-open(Host,Port,Flags) when integer(Port), list(Flags) ->
- %% Dbg = {debug,[trace,log,statistics]},
- %% Options = [Dbg],
- Options = [],
- {ok, Pid} = gen_server:start_link(?MODULE, [Flags], Options),
- gen_server:call(Pid, {open, ip_comm, Host, Port}, infinity).
-
-%% user(Pid, User, Pass)
-%% Purpose: Login.
-%% Args: Pid = pid(), User = Pass = string()
-%% Returns: ok | {error, euser} | {error, econn}
-user(Pid, User, Pass) ->
- gen_server:call(Pid, {user, User, Pass}, infinity).
-
-%% user(Pid, User, Pass,Acc)
-%% Purpose: Login whith a supplied account name
-%% Args: Pid = pid(), User = Pass = Acc = string()
-%% Returns: ok | {error, euser} | {error, econn} | {error, eacct}
-user(Pid, User, Pass,Acc) ->
- gen_server:call(Pid, {user, User, Pass,Acc}, infinity).
-
-%% account(Pid,Acc)
-%% Purpose: Set a user Account.
-%% Args: Pid = pid(), Acc= string()
-%% Returns: ok | {error, eacct}
-account(Pid,Acc) ->
- gen_server:call(Pid, {account,Acc}, infinity).
-
-%% pwd(Pid)
-%%
-%% Purpose: Get the current working directory at remote server.
-%% Args: Pid = pid()
-%% Returns: {ok, Dir} | {error, elogin} | {error, econn}
-pwd(Pid) ->
- gen_server:call(Pid, pwd, infinity).
-
-%% lpwd(Pid)
-%%
-%% Purpose: Get the current working directory at local server.
-%% Args: Pid = pid()
-%% Returns: {ok, Dir} | {error, elogin}
-lpwd(Pid) ->
- gen_server:call(Pid, lpwd, infinity).
-
-%% cd(Pid, Dir)
-%%
-%% Purpose: Change current working directory at remote server.
-%% Args: Pid = pid(), Dir = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-cd(Pid, Dir) ->
- gen_server:call(Pid, {cd, Dir}, infinity).
-
-%% lcd(Pid, Dir)
-%%
-%% Purpose: Change current working directory for the local client.
-%% Args: Pid = pid(), Dir = string()
-%% Returns: ok | {error, epath}
-lcd(Pid, Dir) ->
- gen_server:call(Pid, {lcd, Dir}, infinity).
-
-%% ls(Pid)
-%% ls(Pid, Dir)
-%%
-%% Purpose: List the contents of current directory (ls/1) or directory
-%% Dir (ls/2) at remote server.
-%% Args: Pid = pid(), Dir = string()
-%% Returns: {ok, Listing} | {error, epath} | {error, elogin} | {error, econn}
-ls(Pid) ->
- ls(Pid, "").
-ls(Pid, Dir) ->
- gen_server:call(Pid, {dir, long, Dir}, infinity).
-
-%% nlist(Pid)
-%% nlist(Pid, Dir)
-%%
-%% Purpose: List the contents of current directory (ls/1) or directory
-%% Dir (ls/2) at remote server. The returned list is a stream
-%% of file names.
-%% Args: Pid = pid(), Dir = string()
-%% Returns: {ok, Listing} | {error, epath} | {error, elogin} | {error, econn}
-nlist(Pid) ->
- nlist(Pid, "").
-nlist(Pid, Dir) ->
- gen_server:call(Pid, {dir, short, Dir}, infinity).
-
-%% rename(Pid, CurrFile, NewFile)
-%%
-%% Purpose: Rename a file at remote server.
-%% Args: Pid = pid(), CurrFile = NewFile = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-rename(Pid, CurrFile, NewFile) ->
- gen_server:call(Pid, {rename, CurrFile, NewFile}, infinity).
-
-%% delete(Pid, File)
-%%
-%% Purpose: Remove file at remote server.
-%% Args: Pid = pid(), File = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-delete(Pid, File) ->
- gen_server:call(Pid, {delete, File}, infinity).
-
-%% mkdir(Pid, Dir)
-%%
-%% Purpose: Make directory at remote server.
-%% Args: Pid = pid(), Dir = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-mkdir(Pid, Dir) ->
- gen_server:call(Pid, {mkdir, Dir}, infinity).
-
-%% rmdir(Pid, Dir)
-%%
-%% Purpose: Remove directory at remote server.
-%% Args: Pid = pid(), Dir = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-rmdir(Pid, Dir) ->
- gen_server:call(Pid, {rmdir, Dir}, infinity).
-
-%% type(Pid, Type)
-%%
-%% Purpose: Set transfer type.
-%% Args: Pid = pid(), Type = ascii | binary
-%% Returns: ok | {error, etype} | {error, elogin} | {error, econn}
-type(Pid, Type) ->
- gen_server:call(Pid, {type, Type}, infinity).
-
-%% recv(Pid, RFile [, LFile])
-%%
-%% Purpose: Transfer file from remote server.
-%% Args: Pid = pid(), RFile = LFile = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-recv(Pid, RFile) ->
- recv(Pid, RFile, "").
-
-recv(Pid, RFile, LFile) ->
- gen_server:call(Pid, {recv, RFile, LFile}, infinity).
-
-%% recv_bin(Pid, RFile)
-%%
-%% Purpose: Transfer file from remote server into binary.
-%% Args: Pid = pid(), RFile = string()
-%% Returns: {ok, Bin} | {error, epath} | {error, elogin} | {error, econn}
-recv_bin(Pid, RFile) ->
- gen_server:call(Pid, {recv_bin, RFile}, infinity).
-
-%% recv_chunk_start(Pid, RFile)
-%%
-%% Purpose: Start receive of chunks of remote file.
-%% Args: Pid = pid(), RFile = string().
-%% Returns: ok | {error, elogin} | {error, epath} | {error, econn}
-recv_chunk_start(Pid, RFile) ->
- gen_server:call(Pid, {recv_chunk_start, RFile}, infinity).
-
-
-%% recv_chunk(Pid, RFile)
-%%
-%% Purpose: Transfer file from remote server into binary in chunks
-%% Args: Pid = pid(), RFile = string()
-%% Returns: Reference
-recv_chunk(Pid) ->
- gen_server:call(Pid, recv_chunk, infinity).
-
-%% send(Pid, LFile [, RFile])
-%%
-%% Purpose: Transfer file to remote server.
-%% Args: Pid = pid(), LFile = RFile = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-send(Pid, LFile) ->
- send(Pid, LFile, "").
-
-send(Pid, LFile, RFile) ->
- gen_server:call(Pid, {send, LFile, RFile}, infinity).
-
-%% send_bin(Pid, Bin, RFile)
-%%
-%% Purpose: Transfer a binary to a remote file.
-%% Args: Pid = pid(), Bin = binary(), RFile = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, enotbinary}
-%% | {error, econn}
-send_bin(Pid, Bin, RFile) when binary(Bin) ->
- gen_server:call(Pid, {send_bin, Bin, RFile}, infinity);
-send_bin(Pid, Bin, RFile) ->
- {error, enotbinary}.
-
-%% send_chunk_start(Pid, RFile)
-%%
-%% Purpose: Start transfer of chunks to remote file.
-%% Args: Pid = pid(), RFile = string().
-%% Returns: ok | {error, elogin} | {error, epath} | {error, econn}
-send_chunk_start(Pid, RFile) ->
- gen_server:call(Pid, {send_chunk_start, RFile}, infinity).
-
-
-%% append_chunk_start(Pid, RFile)
-%%
-%% Purpose: Start append chunks of data to remote file.
-%% Args: Pid = pid(), RFile = string().
-%% Returns: ok | {error, elogin} | {error, epath} | {error, econn}
-append_chunk_start(Pid, RFile) ->
- gen_server:call(Pid, {append_chunk_start, RFile}, infinity).
-
-
-%% send_chunk(Pid, Bin)
-%%
-%% Purpose: Send chunk to remote file.
-%% Args: Pid = pid(), Bin = binary().
-%% Returns: ok | {error, elogin} | {error, enotbinary} | {error, echunk}
-%% | {error, econn}
-send_chunk(Pid, Bin) when binary(Bin) ->
- gen_server:call(Pid, {send_chunk, Bin}, infinity);
-send_chunk(Pid, Bin) ->
- {error, enotbinary}.
-
-%%append_chunk(Pid, Bin)
-%%
-%% Purpose: Append chunk to remote file.
-%% Args: Pid = pid(), Bin = binary().
-%% Returns: ok | {error, elogin} | {error, enotbinary} | {error, echunk}
-%% | {error, econn}
-append_chunk(Pid, Bin) when binary(Bin) ->
- gen_server:call(Pid, {append_chunk, Bin}, infinity);
-append_chunk(Pid, Bin) ->
- {error, enotbinary}.
-
-%% send_chunk_end(Pid)
-%%
-%% Purpose: End sending of chunks to remote file.
-%% Args: Pid = pid().
-%% Returns: ok | {error, elogin} | {error, echunk} | {error, econn}
-send_chunk_end(Pid) ->
- gen_server:call(Pid, send_chunk_end, infinity).
-
-%% append_chunk_end(Pid)
-%%
-%% Purpose: End appending of chunks to remote file.
-%% Args: Pid = pid().
-%% Returns: ok | {error, elogin} | {error, echunk} | {error, econn}
-append_chunk_end(Pid) ->
- gen_server:call(Pid, append_chunk_end, infinity).
-
-%% append(Pid, LFile,RFile)
-%%
-%% Purpose: Append the local file to the remote file
-%% Args: Pid = pid(), LFile = RFile = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
-append(Pid, LFile) ->
- append(Pid, LFile, "").
-
-append(Pid, LFile, RFile) ->
- gen_server:call(Pid, {append, LFile, RFile}, infinity).
-
-%% append_bin(Pid, Bin, RFile)
-%%
-%% Purpose: Append a binary to a remote file.
-%% Args: Pid = pid(), Bin = binary(), RFile = string()
-%% Returns: ok | {error, epath} | {error, elogin} | {error, enotbinary}
-%% | {error, econn}
-append_bin(Pid, Bin, RFile) when binary(Bin) ->
- gen_server:call(Pid, {append_bin, Bin, RFile}, infinity);
-append_bin(Pid, Bin, RFile) ->
- {error, enotbinary}.
-
-
-%% close(Pid)
-%%
-%% Purpose: End the ftp session.
-%% Args: Pid = pid()
-%% Returns: ok
-close(Pid) ->
- case (catch gen_server:call(Pid, close, 30000)) of
- ok ->
- ok;
- {'EXIT',{noproc,_}} ->
- %% Already gone...
- ok;
- Res ->
- Res
- end.
-
-%% formaterror(Tag)
-%%
-%% Purpose: Return diagnostics.
-%% Args: Tag = atom() | {error, atom()}
-%% Returns: string().
-formaterror(Tag) ->
- errstr(Tag).
-
-%% help()
-%%
-%% Purpose: Print list of valid commands.
-%%
-%% Undocumented.
-%%
-help() ->
- io:format("\n Commands:\n"
- " ---------\n"
- " cd(Pid, Dir)\n"
- " close(Pid)\n"
- " delete(Pid, File)\n"
- " formaterror(Tag)\n"
- " help()\n"
- " lcd(Pid, Dir)\n"
- " lpwd(Pid)\n"
- " ls(Pid [, Dir])\n"
- " mkdir(Pid, Dir)\n"
- " nlist(Pid [, Dir])\n"
- " open(Host [Port, Flags])\n"
- " pwd(Pid)\n"
- " recv(Pid, RFile [, LFile])\n"
- " recv_bin(Pid, RFile)\n"
- " recv_chunk_start(Pid, RFile)\n"
- " recv_chunk(Pid)\n"
- " rename(Pid, CurrFile, NewFile)\n"
- " rmdir(Pid, Dir)\n"
- " send(Pid, LFile [, RFile])\n"
- " send_chunk(Pid, Bin)\n"
- " send_chunk_start(Pid, RFile)\n"
- " send_chunk_end(Pid)\n"
- " send_bin(Pid, Bin, RFile)\n"
- " append(Pid, LFile [, RFile])\n"
- " append_chunk(Pid, Bin)\n"
- " append_chunk_start(Pid, RFile)\n"
- " append_chunk_end(Pid)\n"
- " append_bin(Pid, Bin, RFile)\n"
- " type(Pid, Type)\n"
- " account(Pid,Account)\n"
- " user(Pid, User, Pass)\n"
- " user(Pid, User, Pass,Account)\n").
-
-%%
-%% INIT
-%%
-
--record(state, {csock = undefined, dsock = undefined, flags = undefined,
- ldir = undefined, type = undefined, chunk = false,
- pending = undefined}).
-
-init([Flags]) ->
- sock_start(),
- put(debug,get_debug(Flags)),
- put(verbose,get_verbose(Flags)),
- process_flag(priority, low),
- {ok, LDir} = file:get_cwd(),
- {ok, #state{flags = Flags, ldir = LDir}}.
-
-%%
-%% HANDLERS
-%%
-
-%% First group of reply code digits
--define(POS_PREL, 1).
--define(POS_COMPL, 2).
--define(POS_INTERM, 3).
--define(TRANS_NEG_COMPL, 4).
--define(PERM_NEG_COMPL, 5).
-
-%% Second group of reply code digits
--define(SYNTAX,0).
--define(INFORMATION,1).
--define(CONNECTION,2).
--define(AUTH_ACC,3).
--define(UNSPEC,4).
--define(FILE_SYSTEM,5).
-
-
--define(STOP_RET(E),{stop, normal, {error, E},
- State#state{csock = undefined}}).
-
-
-rescode(?POS_PREL,_,_) -> pos_prel; %%Positive Preleminary Reply
-rescode(?POS_COMPL,_,_) -> pos_compl; %%Positive Completion Reply
-rescode(?POS_INTERM,?AUTH_ACC,2) -> pos_interm_acct; %%Positive Intermediate Reply nedd account
-rescode(?POS_INTERM,_,_) -> pos_interm; %%Positive Intermediate Reply
-rescode(?TRANS_NEG_COMPL,?FILE_SYSTEM,2) -> trans_no_space; %%No storage area no action taken
-rescode(?TRANS_NEG_COMPL,_,_) -> trans_neg_compl;%%Temporary Error, no action taken
-rescode(?PERM_NEG_COMPL,?FILE_SYSTEM,2) -> perm_no_space; %%Permanent disk space error, the user shall not try again
-rescode(?PERM_NEG_COMPL,?FILE_SYSTEM,3) -> perm_fname_not_allowed;
-rescode(?PERM_NEG_COMPL,_,_) -> perm_neg_compl.
-
-retcode(trans_no_space,_) -> etnospc;
-retcode(perm_no_space,_) -> epnospc;
-retcode(perm_fname_not_allowed,_) -> efnamena;
-retcode(_,Otherwise) -> Otherwise.
-
-handle_call({open,ip_comm,Conn_data},From,State) ->
- case lists:keysearch(host,1,Conn_data) of
- {value,{host,Host}}->
- Port=get_key1(port,Conn_data,?FTP_PORT),
- Timeout=get_key1(timeout,Conn_data,?OPEN_TIMEOUT),
- open(Host,Port,Timeout,State);
- false ->
- ehost
- end;
-
-handle_call({open,ip_comm,Host,Port},From,State) ->
- open(Host,Port,?OPEN_TIMEOUT,State);
-
-handle_call({user, User, Pass}, _From, State) ->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock, "USER ~s", [User]) of
- pos_interm ->
- case ctrl_cmd(CSock, "PASS ~s", [Pass]) of
- pos_compl ->
- set_type(binary, CSock),
- {reply, ok, State#state{type = binary}};
- {error,enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, euser}, State}
- end;
- pos_compl ->
- set_type(binary, CSock),
- {reply, ok, State#state{type = binary}};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, euser}, State}
- end;
-
-handle_call({user, User, Pass,Acc}, _From, State) ->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock, "USER ~s", [User]) of
- pos_interm ->
- case ctrl_cmd(CSock, "PASS ~s", [Pass]) of
- pos_compl ->
- set_type(binary, CSock),
- {reply, ok, State#state{type = binary}};
- pos_interm_acct->
- case ctrl_cmd(CSock,"ACCT ~s",[Acc]) of
- pos_compl->
- set_type(binary, CSock),
- {reply, ok, State#state{type = binary}};
- {error,enotconn}->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, eacct}, State}
- end;
- {error,enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, euser}, State}
- end;
- pos_compl ->
- set_type(binary, CSock),
- {reply, ok, State#state{type = binary}};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, euser}, State}
- end;
-
-%%set_account(Acc,State)->Reply
-%%Reply={reply, {error, euser}, State} | {error,enotconn}->
-handle_call({account,Acc},_From,State)->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock,"ACCT ~s",[Acc]) of
- pos_compl->
- {reply, ok,State};
- {error,enotconn}->
- ?STOP_RET(econn);
- Error ->
- debug(" error: ~p",[Error]),
- {reply, {error, eacct}, State}
- end;
-
-handle_call(pwd, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- %%
- %% NOTE: The directory string comes over the control connection.
- case sock_write(CSock, mk_cmd("PWD", [])) of
- ok ->
- {_, Line} = result_line(CSock),
- {_, Cs} = split($", Line), % XXX Ugly
- {Dir0, _} = split($", Cs),
- Dir = lists:delete($", Dir0),
- {reply, {ok, Dir}, State};
- {error, enotconn} ->
- ?STOP_RET(econn)
- end;
-
-handle_call(lpwd, _From, State) ->
- #state{csock = CSock, ldir = LDir} = State,
- {reply, {ok, LDir}, State};
-
-handle_call({cd, Dir}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock, "CWD ~s", [Dir]) of
- pos_compl ->
- {reply, ok, State};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({lcd, Dir}, _From, State) ->
- #state{csock = CSock, ldir = LDir0} = State,
- LDir = absname(LDir0, Dir),
- case file:read_file_info(LDir) of
- {ok, _ } ->
- {reply, ok, State#state{ldir = LDir}};
- _ ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({dir, Len, Dir}, _From, State) when State#state.chunk == false ->
- debug(" dir : ~p: ~s~n",[Len,Dir]),
- #state{csock = CSock, type = Type} = State,
- set_type(ascii, Type, CSock),
- LSock = listen_data(CSock, raw),
- Cmd = case Len of
- short -> "NLST";
- long -> "LIST"
- end,
- Result = case Dir of
- "" ->
- ctrl_cmd(CSock, Cmd, "");
- _ ->
- ctrl_cmd(CSock, Cmd ++ " ~s", [Dir])
- end,
- debug(" ctrl : command result: ~p~n",[Result]),
- case Result of
- pos_prel ->
- debug(" dbg : await the data connection", []),
- DSock = accept_data(LSock),
- debug(" dbg : await the data", []),
- Reply0 =
- case recv_data(DSock) of
- {ok, DirData} ->
- debug(" data : DirData: ~p~n",[DirData]),
- case result(CSock) of
- pos_compl ->
- {ok, DirData};
- _ ->
- {error, epath}
- end;
- {error, Reason} ->
- sock_close(DSock),
- verbose(" data : error: ~p, ~p~n",[Reason, result(CSock)]),
- {error, epath}
- end,
-
- debug(" ctrl : reply: ~p~n",[Reply0]),
- reset_type(ascii, Type, CSock),
- {reply, Reply0, State};
- {closed, _Why} ->
- ?STOP_RET(econn);
- _ ->
- sock_close(LSock),
- {reply, {error, epath}, State}
- end;
-
-
-handle_call({rename, CurrFile, NewFile}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock, "RNFR ~s", [CurrFile]) of
- pos_interm ->
- case ctrl_cmd(CSock, "RNTO ~s", [NewFile]) of
- pos_compl ->
- {reply, ok, State};
- _ ->
- {reply, {error, epath}, State}
- end;
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({delete, File}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock, "DELE ~s", [File]) of
- pos_compl ->
- {reply, ok, State};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({mkdir, Dir}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock, "MKD ~s", [Dir]) of
- pos_compl ->
- {reply, ok, State};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({rmdir, Dir}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- case ctrl_cmd(CSock, "RMD ~s", [Dir]) of
- pos_compl ->
- {reply, ok, State};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({type, Type}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- case Type of
- ascii ->
- set_type(ascii, CSock),
- {reply, ok, State#state{type = ascii}};
- binary ->
- set_type(binary, CSock),
- {reply, ok, State#state{type = binary}};
- _ ->
- {reply, {error, etype}, State}
- end;
-
-handle_call({recv, RFile, LFile}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock, ldir = LDir} = State,
- ALFile = case LFile of
- "" ->
- absname(LDir, RFile);
- _ ->
- absname(LDir, LFile)
- end,
- case file_open(ALFile, write) of
- {ok, Fd} ->
- LSock = listen_data(CSock, binary),
- Ret = case ctrl_cmd(CSock, "RETR ~s", [RFile]) of
- pos_prel ->
- DSock = accept_data(LSock),
- recv_file(DSock, Fd),
- Reply0 = case result(CSock) of
- pos_compl ->
- ok;
- _ ->
- {error, epath}
- end,
- sock_close(DSock),
- {reply, Reply0, State};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, epath}, State}
- end,
- file_close(Fd),
- Ret;
- {error, _What} ->
- {reply, {error, epath}, State}
- end;
-
-handle_call({recv_bin, RFile}, _From, State) when State#state.chunk == false ->
- #state{csock = CSock, ldir = LDir} = State,
- LSock = listen_data(CSock, binary),
- case ctrl_cmd(CSock, "RETR ~s", [RFile]) of
- pos_prel ->
- DSock = accept_data(LSock),
- Reply = recv_binary(DSock,CSock),
- sock_close(DSock),
- {reply, Reply, State};
- {error, enotconn} ->
- ?STOP_RET(econn);
- _ ->
- {reply, {error, epath}, State}
- end;
-
-
-handle_call({recv_chunk_start, RFile}, _From, State)
- when State#state.chunk == false ->
- start_chunk_transfer("RETR",RFile,State);
-
-handle_call(recv_chunk, _From, State)
- when State#state.chunk == true ->
- do_recv_chunk(State);
-
-
-handle_call({send, LFile, RFile}, _From, State)
- when State#state.chunk == false ->
- transfer_file("STOR",LFile,RFile,State);
-
-handle_call({append, LFile, RFile}, _From, State)
- when State#state.chunk == false ->
- transfer_file("APPE",LFile,RFile,State);
-
-
-handle_call({send_bin, Bin, RFile}, _From, State)
- when State#state.chunk == false ->
- transfer_data("STOR",Bin,RFile,State);
-
-handle_call({append_bin, Bin, RFile}, _From, State)
- when State#state.chunk == false ->
- transfer_data("APPE",Bin,RFile,State);
-
-
-
-handle_call({send_chunk_start, RFile}, _From, State)
- when State#state.chunk == false ->
- start_chunk_transfer("STOR",RFile,State);
-
-handle_call({append_chunk_start,RFile},_From,State)
- when State#state.chunk==false->
- start_chunk_transfer("APPE",RFile,State);
-
-handle_call({send_chunk, Bin}, _From, State)
- when State#state.chunk == true ->
- chunk_transfer(Bin,State);
-
-handle_call({append_chunk, Bin}, _From, State)
- when State#state.chunk == true ->
- chunk_transfer(Bin,State);
-
-handle_call(append_chunk_end, _From, State)
- when State#state.chunk == true ->
- end_chunk_transfer(State);
-
-handle_call(send_chunk_end, _From, State)
- when State#state.chunk == true ->
- end_chunk_transfer(State);
-
-
-
-handle_call(close, _From, State) when State#state.chunk == false ->
- #state{csock = CSock} = State,
- ctrl_cmd(CSock, "QUIT", []),
- sock_close(CSock),
- {stop, normal, ok, State};
-
-handle_call(_, _From, State) when State#state.chunk == true ->
- {reply, {error, echunk}, State}.
-
-
-handle_cast(Msg, State) ->
- {noreply, State}.
-
-
-handle_info({Sock, {fromsocket, Bytes}}, State) when Sock == State#state.csock ->
- put(leftovers, Bytes ++ leftovers()),
- {noreply, State};
-
-%% Data connection closed (during chunk sending)
-handle_info({Sock, {socket_closed, _Reason}}, State) when Sock == State#state.dsock ->
- {noreply, State#state{dsock = undefined}};
-
-%% Control connection closed.
-handle_info({Sock, {socket_closed, _Reason}}, State) when Sock == State#state.csock ->
- debug(" sc : ~s~n",[leftovers()]),
- {stop, ftp_server_close, State#state{csock = undefined}};
-
-handle_info(Info, State) ->
- error_logger:info_msg("ftp : ~w : Unexpected message: ~w\n", [self(),Info]),
- {noreply, State}.
-
-code_change(OldVsn,State,Extra)->
- {ok,State}.
-
-terminate(Reason, State) ->
- ok.
-%%
-%% OPEN CONNECTION
-%%
-open(Host,Port,Timeout,State)->
- case sock_connect(Host,Port,Timeout) of
- {error, What} ->
- {stop, normal, {error, What}, State};
- CSock ->
- case result(CSock, State#state.flags) of
- {error,Reason} ->
- sock_close(CSock),
- {stop,normal,{error,Reason},State};
- _ -> % We should really check this...
- {reply, {ok, self()}, State#state{csock = CSock}}
- end
- end.
-
-
-
-%%
-%% CONTROL CONNECTION
-%%
-
-ctrl_cmd(CSock, Fmt, Args) ->
- Cmd = mk_cmd(Fmt, Args),
- case sock_write(CSock, Cmd) of
- ok ->
- debug(" cmd : ~s",[Cmd]),
- result(CSock);
- {error, enotconn} ->
- {error, enotconn};
- Other ->
- Other
- end.
-
-mk_cmd(Fmt, Args) ->
- [io_lib:format(Fmt, Args)| "\r\n"]. % Deep list ok.
-
-%%
-%% TRANSFER TYPE
-%%
-
-%%
-%% set_type(NewType, CurrType, CSock)
-%% reset_type(NewType, CurrType, CSock)
-%%
-set_type(Type, Type, CSock) ->
- ok;
-set_type(NewType, _OldType, CSock) ->
- set_type(NewType, CSock).
-
-reset_type(Type, Type, CSock) ->
- ok;
-reset_type(_NewType, OldType, CSock) ->
- set_type(OldType, CSock).
-
-set_type(ascii, CSock) ->
- ctrl_cmd(CSock, "TYPE A", []);
-set_type(binary, CSock) ->
- ctrl_cmd(CSock, "TYPE I", []).
-
-%%
-%% DATA CONNECTION
-%%
-
-%% Create a listen socket for a data connection and send a PORT command
-%% containing the IP address and port number. Mode is binary or raw.
-%%
-listen_data(CSock, Mode) ->
- {IP, _} = sock_name(CSock), % IP address of control conn.
- LSock = sock_listen(Mode, IP),
- Port = sock_listen_port(LSock),
- {A1, A2, A3, A4} = IP,
- {P1, P2} = {Port div 256, Port rem 256},
- ctrl_cmd(CSock, "PORT ~w,~w,~w,~w,~w,~w", [A1, A2, A3, A4, P1, P2]),
- LSock.
-
-%%
-%% Accept the data connection and close the listen socket.
-%%
-accept_data(LSock) ->
- Sock = sock_accept(LSock),
- sock_close(LSock),
- Sock.
-
-%%
-%% DATA COLLECTION (ls, dir)
-%%
-%% Socket is a byte stream in ASCII mode.
-%%
-
-%% Receive data (from data connection).
-recv_data(Sock) ->
- recv_data(Sock, [], 0).
-recv_data(Sock, Sofar, ?OPER_TIMEOUT) ->
- sock_close(Sock),
- {ok, lists:flatten(lists:reverse(Sofar))};
-recv_data(Sock, Sofar, Retry) ->
- case sock_read(Sock) of
- {ok, Data} ->
- debug(" dbg : received some data: ~n~s", [Data]),
- recv_data(Sock, [Data| Sofar], 0);
- {error, timeout} ->
- %% Retry..
- recv_data(Sock, Sofar, Retry+1);
- {error, Reason} ->
- SoFar1 = lists:flatten(lists:reverse(Sofar)),
- {error, {socket_error, Reason, SoFar1, Retry}};
- {closed, _} ->
- {ok, lists:flatten(lists:reverse(Sofar))}
- end.
-
-%%
-%% BINARY TRANSFER
-%%
-
-%% --------------------------------------------------
-
-%% recv_binary(DSock,CSock) = {ok,Bin} | {error,Reason}
-%%
-recv_binary(DSock,CSock) ->
- recv_binary1(recv_binary2(DSock,[],0),CSock).
-
-recv_binary1(Reply,Sock) ->
- case result(Sock) of
- pos_compl -> Reply;
- _ -> {error, epath}
- end.
-
-recv_binary2(Sock, _Bs, ?OPER_TIMEOUT) ->
- sock_close(Sock),
- {error,eclosed};
-recv_binary2(Sock, Bs, Retry) ->
- case sock_read(Sock) of
- {ok, Bin} ->
- recv_binary2(Sock, [Bs, Bin], 0);
- {error, timeout} ->
- recv_binary2(Sock, Bs, Retry+1);
- {closed, _Why} ->
- {ok,list_to_binary(Bs)}
- end.
-
-%% --------------------------------------------------
-
-%%
-%% recv_chunk
-%%
-
-do_recv_chunk(#state{dsock = undefined} = State) ->
- {reply, {error,econn}, State};
-do_recv_chunk(State) ->
- recv_chunk1(recv_chunk2(State, 0), State).
-
-recv_chunk1({ok, _Bin} = Reply, State) ->
- {reply, Reply, State};
-%% Reply = ok | {error, Reason}
-recv_chunk1(Reply, #state{csock = CSock} = State) ->
- State1 = State#state{dsock = undefined, chunk = false},
- case result(CSock) of
- pos_compl ->
- {reply, Reply, State1};
- _ ->
- {reply, {error, epath}, State1}
- end.
-
-recv_chunk2(#state{dsock = DSock} = State, ?OPER_TIMEOUT) ->
- sock_close(DSock),
- {error, eclosed};
-recv_chunk2(#state{dsock = DSock} = State, Retry) ->
- case sock_read(DSock) of
- {ok, Bin} ->
- {ok, Bin};
- {error, timeout} ->
- recv_chunk2(State, Retry+1);
- {closed, Reason} ->
- debug(" dbg : socket closed: ~p", [Reason]),
- ok
- end.
-
-
-%% --------------------------------------------------
-
-%%
-%% FILE TRANSFER
-%%
-
-recv_file(Sock, Fd) ->
- recv_file(Sock, Fd, 0).
-
-recv_file(Sock, Fd, ?OPER_TIMEOUT) ->
- sock_close(Sock),
- {closed, timeout};
-recv_file(Sock, Fd, Retry) ->
- case sock_read(Sock) of
- {ok, Bin} ->
- file_write(Fd, Bin),
- recv_file(Sock, Fd);
- {error, timeout} ->
- recv_file(Sock, Fd, Retry+1);
-% {error, Reason} ->
-% SoFar1 = lists:flatten(lists:reverse(Sofar)),
-% exit({socket_error, Reason, Sock, SoFar1, Retry});
- {closed, How} ->
- {closed, How}
- end.
-
-%%
-%% send_file(Fd, Sock) = ok | {error, Why}
-%%
-
-send_file(Fd, Sock) ->
- {N, Bin} = file_read(Fd),
- if
- N > 0 ->
- case sock_write(Sock, Bin) of
- ok ->
- send_file(Fd, Sock);
- {error, Reason} ->
- {error, Reason}
- end;
- true ->
- ok
- end.
-
-
-
-%%
-%% PARSING OF RESULT LINES
-%%
-
-%% Excerpt from RFC 959:
-%%
-%% "A reply is defined to contain the 3-digit code, followed by Space
-%% <SP>, followed by one line of text (where some maximum line length
-%% has been specified), and terminated by the Telnet end-of-line
-%% code. There will be cases however, where the text is longer than
-%% a single line. In these cases the complete text must be bracketed
-%% so the User-process knows when it may stop reading the reply (i.e.
-%% stop processing input on the control connection) and go do other
-%% things. This requires a special format on the first line to
-%% indicate that more than one line is coming, and another on the
-%% last line to designate it as the last. At least one of these must
-%% contain the appropriate reply code to indicate the state of the
-%% transaction. To satisfy all factions, it was decided that both
-%% the first and last line codes should be the same.
-%%
-%% Thus the format for multi-line replies is that the first line
-%% will begin with the exact required reply code, followed
-%% immediately by a Hyphen, "-" (also known as Minus), followed by
-%% text. The last line will begin with the same code, followed
-%% immediately by Space <SP>, optionally some text, and the Telnet
-%% end-of-line code.
-%%
-%% For example:
-%% 123-First line
-%% Second line
-%% 234 A line beginning with numbers
-%% 123 The last line
-%%
-%% The user-process then simply needs to search for the second
-%% occurrence of the same reply code, followed by <SP> (Space), at
-%% the beginning of a line, and ignore all intermediary lines. If
-%% an intermediary line begins with a 3-digit number, the Server
-%% must pad the front to avoid confusion.
-%%
-%% This scheme allows standard system routines to be used for
-%% reply information (such as for the STAT reply), with
-%% "artificial" first and last lines tacked on. In rare cases
-%% where these routines are able to generate three digits and a
-%% Space at the beginning of any line, the beginning of each
-%% text line should be offset by some neutral text, like Space.
-%%
-%% This scheme assumes that multi-line replies may not be nested."
-
-%% We have to collect the stream of result characters into lines (ending
-%% in "\r\n"; we check for "\n"). When a line is assembled, left-over
-%% characters are saved in the process dictionary.
-%%
-
-%% result(Sock) = rescode()
-%%
-result(Sock) ->
- result(Sock, false).
-
-result_line(Sock) ->
- result(Sock, true).
-
-%% result(Sock, Bool) = {error,Reason} | rescode() | {rescode(), Lines}
-%% Printout if Bool = true.
-%%
-result(Sock, RetForm) ->
- case getline(Sock) of
- Line when length(Line) > 3 ->
- [D1, D2, D3| Tail] = Line,
- case Tail of
- [$-| _] ->
- parse_to_end(Sock, [D1, D2, D3, $ ]); % 3 digits + space
- _ ->
- ok
- end,
- result(D1,D2,D3,Line,RetForm);
- _ ->
- retform(rescode(?PERM_NEG_COMPL,-1,-1),[],RetForm)
- end.
-
-result(D1,_D2,_D3,Line,_RetForm) when D1 - $0 > 10 ->
- {error,{invalid_server_response,Line}};
-result(D1,_D2,_D3,Line,_RetForm) when D1 - $0 < 0 ->
- {error,{invalid_server_response,Line}};
-result(D1,D2,D3,Line,RetForm) ->
- Res1 = D1 - $0,
- Res2 = D2 - $0,
- Res3 = D3 - $0,
- verbose(" ~w : ~s", [Res1, Line]),
- retform(rescode(Res1,Res2,Res3),Line,RetForm).
-
-retform(ResCode,Line,true) ->
- {ResCode,Line};
-retform(ResCode,_,_) ->
- ResCode.
-
-leftovers() ->
- case get(leftovers) of
- undefined -> [];
- X -> X
- end.
-
-%% getline(Sock) = Line
-%%
-getline(Sock) ->
- getline(Sock, leftovers()).
-
-getline(Sock, Rest) ->
- getline1(Sock, split($\n, Rest), 0).
-
-getline1(Sock, {[], Rest}, ?OPER_TIMEOUT) ->
- sock_close(Sock),
- put(leftovers, Rest),
- [];
-getline1(Sock, {[], Rest}, Retry) ->
- case sock_read(Sock) of
- {ok, More} ->
- debug(" read : ~s~n",[More]),
- getline(Sock, Rest ++ More);
- {error, timeout} ->
- %% Retry..
- getline1(Sock, {[], Rest}, Retry+1);
- Error ->
- put(leftovers, Rest),
- []
- end;
-getline1(Sock, {Line, Rest}, Retry) ->
- put(leftovers, Rest),
- Line.
-
-parse_to_end(Sock, Prefix) ->
- Line = getline(Sock),
- case lists:prefix(Prefix, Line) of
- false ->
- parse_to_end(Sock, Prefix);
- true ->
- ok
- end.
-
-
-%% Split list after first occurence of S.
-%% Returns {Prefix, Suffix} ({[], Cs} if S not found).
-split(S, Cs) ->
- split(S, Cs, []).
-
-split(S, [S| Cs], As) ->
- {lists:reverse([S|As]), Cs};
-split(S, [C| Cs], As) ->
- split(S, Cs, [C| As]);
-split(_, [], As) ->
- {[], lists:reverse(As)}.
-
-%%
-%% FILE INTERFACE
-%%
-%% All files are opened raw in binary mode.
-%%
--define(BUFSIZE, 4096).
-
-file_open(File, Option) ->
- file:open(File, [raw, binary, Option]).
-
-file_close(Fd) ->
- file:close(Fd).
-
-
-file_read(Fd) -> % Compatible with pre R2A.
- case file:read(Fd, ?BUFSIZE) of
- {ok, {N, Bytes}} ->
- {N, Bytes};
- {ok, Bytes} ->
- {size(Bytes), Bytes};
- eof ->
- {0, []}
- end.
-
-file_write(Fd, Bytes) ->
- file:write(Fd, Bytes).
-
-absname(Dir, File) -> % Args swapped.
- filename:absname(File, Dir).
-
-
-
-%% sock_start()
-%%
-
-%%
-%% USE GEN_TCP
-%%
-
-sock_start() ->
- inet_db:start().
-
-%%
-%% Connect to FTP server at Host (default is TCP port 21) in raw mode,
-%% in order to establish a control connection.
-%%
-
-sock_connect(Host,Port,TimeOut) ->
- debug(" info : connect to server on ~p:~p~n",[Host,Port]),
- Opts = [{packet, 0}, {active, false}],
- case (catch gen_tcp:connect(Host, Port, Opts,TimeOut)) of
- {'EXIT', R1} -> % XXX Probably no longer needed.
- debug(" error: socket connectionn failed with exit reason:"
- "~n ~p",[R1]),
- {error, ehost};
- {error, R2} ->
- debug(" error: socket connectionn failed with exit reason:"
- "~n ~p",[R2]),
- {error, ehost};
- {ok, Sock} ->
- Sock
- end.
-
-%%
-%% Create a listen socket (any port) in binary or raw non-packet mode for
-%% data connection.
-%%
-sock_listen(Mode, IP) ->
- Opts = case Mode of
- binary ->
- [binary, {packet, 0}];
- raw ->
- [{packet, 0}]
- end,
- {ok, Sock} = gen_tcp:listen(0, [{ip, IP}, {active, false} | Opts]),
- Sock.
-
-sock_accept(LSock) ->
- {ok, Sock} = gen_tcp:accept(LSock),
- Sock.
-
-sock_close(undefined) ->
- ok;
-sock_close(Sock) ->
- gen_tcp:close(Sock).
-
-sock_read(Sock) ->
- case gen_tcp:recv(Sock, 0, ?BYTE_TIMEOUT) of
- {ok, Bytes} ->
- {ok, Bytes};
-
- {error, closed} ->
- {closed, closed}; % Yes
-
- %% --- OTP-4770 begin ---
- %%
- %% This seems to happen on windows
- %% "Someone" tried to close an already closed socket...
- %%
-
- {error, enotsock} ->
- {closed, enotsock};
-
- %%
- %% --- OTP-4770 end ---
-
- {error, etimedout} ->
- {error, timeout};
-
- Other ->
- Other
- end.
-
-%% receive
-%% {tcp, Sock, Bytes} ->
-%% {ok, Bytes};
-%% {tcp_closed, Sock} ->
-%% {closed, closed}
-%% end.
-
-sock_write(Sock, Bytes) ->
- gen_tcp:send(Sock, Bytes).
-
-sock_name(Sock) ->
- {ok, {IP, Port}} = inet:sockname(Sock),
- {IP, Port}.
-
-sock_listen_port(LSock) ->
- {ok, Port} = inet:port(LSock),
- Port.
-
-
-%%
-%% ERROR STRINGS
-%%
-errstr({error, Reason}) ->
- errstr(Reason);
-
-errstr(echunk) -> "Synchronisation error during chung sending.";
-errstr(eclosed) -> "Session has been closed.";
-errstr(econn) -> "Connection to remote server prematurely closed.";
-errstr(eexists) ->"File or directory already exists.";
-errstr(ehost) -> "Host not found, FTP server not found, "
-"or connection rejected.";
-errstr(elogin) -> "User not logged in.";
-errstr(enotbinary) -> "Term is not a binary.";
-errstr(epath) -> "No such file or directory, already exists, "
-"or permission denied.";
-errstr(etype) -> "No such type.";
-errstr(euser) -> "User name or password not valid.";
-errstr(etnospc) -> "Insufficient storage space in system.";
-errstr(epnospc) -> "Exceeded storage allocation "
-"(for current directory or dataset).";
-errstr(efnamena) -> "File name not allowed.";
-errstr(Reason) ->
- lists:flatten(io_lib:format("Unknown error: ~w", [Reason])).
-
-
-
-%% ----------------------------------------------------------
-
-get_verbose(Params) -> check_param(verbose,Params).
-
-get_debug(Flags) -> check_param(debug,Flags).
-
-check_param(P,Ps) -> lists:member(P,Ps).
-
-
-%% verbose -> ok
-%%
-%% Prints the string if the Flags list is non-epmty
-%%
-%% Params: F Format string
-%% A Arguments to the format string
-%%
-verbose(F,A) -> verbose(get(verbose),F,A).
-
-verbose(true,F,A) -> print(F,A);
-verbose(_,_F,_A) -> ok.
-
-
-
-
-%% debug -> ok
-%%
-%% Prints the string if debug enabled
-%%
-%% Params: F Format string
-%% A Arguments to the format string
-%%
-debug(F,A) -> debug(get(debug),F,A).
-
-debug(true,F,A) -> print(F,A);
-debug(_,_F,_A) -> ok.
-
-
-print(F,A) -> io:format(F,A).
-
-
-
-transfer_file(Cmd,LFile,RFile,State)->
- #state{csock = CSock, ldir = LDir} = State,
- ARFile = case RFile of
- "" ->
- LFile;
- _ ->
- RFile
- end,
- ALFile = absname(LDir, LFile),
- case file_open(ALFile, read) of
- {ok, Fd} ->
- LSock = listen_data(CSock, binary),
- case ctrl_cmd(CSock, "~s ~s", [Cmd,ARFile]) of
- pos_prel ->
- DSock = accept_data(LSock),
- SFreply = send_file(Fd, DSock),
- file_close(Fd),
- sock_close(DSock),
- case {SFreply,result(CSock)} of
- {ok,pos_compl} ->
- {reply, ok, State};
- {ok,Other} ->
- debug(" error: unknown reply: ~p~n",[Other]),
- {reply, {error, epath}, State};
- {{error,Why},Result} ->
- ?STOP_RET(retcode(Result,econn))
- end;
- {error, enotconn} ->
- ?STOP_RET(econn);
- Other ->
- debug(" error: ctrl failed: ~p~n",[Other]),
- {reply, {error, epath}, State}
- end;
- {error, Reason} ->
- debug(" error: file open: ~p~n",[Reason]),
- {reply, {error, epath}, State}
- end.
-
-transfer_data(Cmd,Bin,RFile,State)->
- #state{csock = CSock, ldir = LDir} = State,
- LSock = listen_data(CSock, binary),
- case ctrl_cmd(CSock, "~s ~s", [Cmd,RFile]) of
- pos_prel ->
- DSock = accept_data(LSock),
- SReply = sock_write(DSock, Bin),
- sock_close(DSock),
- case {SReply,result(CSock)} of
- {ok,pos_compl} ->
- {reply, ok, State};
- {ok,trans_no_space} ->
- ?STOP_RET(etnospc);
- {ok,perm_no_space} ->
- ?STOP_RET(epnospc);
- {ok,perm_fname_not_allowed} ->
- ?STOP_RET(efnamena);
- {ok,Other} ->
- debug(" error: unknown reply: ~p~n",[Other]),
- {reply, {error, epath}, State};
- {{error,Why},Result} ->
- ?STOP_RET(retcode(Result,econn))
- %% {{error,_Why},_Result} ->
- %% ?STOP_RET(econn)
- end;
-
- {error, enotconn} ->
- ?STOP_RET(econn);
-
- Other ->
- debug(" error: ctrl failed: ~p~n",[Other]),
- {reply, {error, epath}, State}
- end.
-
-
-start_chunk_transfer(Cmd, RFile, #state{csock = CSock} = State) ->
- LSock = listen_data(CSock, binary),
- case ctrl_cmd(CSock, "~s ~s", [Cmd,RFile]) of
- pos_prel ->
- DSock = accept_data(LSock),
- {reply, ok, State#state{dsock = DSock, chunk = true}};
- {error, enotconn} ->
- ?STOP_RET(econn);
- Otherwise ->
- debug(" error: ctrl failed: ~p~n",[Otherwise]),
- {reply, {error, epath}, State}
- end.
-
-
-chunk_transfer(Bin,State)->
- #state{dsock = DSock, csock = CSock} = State,
- case DSock of
- undefined ->
- {reply,{error,econn},State};
- _ ->
- case sock_write(DSock, Bin) of
- ok ->
- {reply, ok, State};
- Other ->
- debug(" error: chunk write error: ~p~n",[Other]),
- {reply, {error, econn}, State#state{dsock = undefined}}
- end
- end.
-
-
-
-end_chunk_transfer(State)->
- #state{csock = CSock, dsock = DSock} = State,
- case DSock of
- undefined ->
- Result = result(CSock),
- case Result of
- pos_compl ->
- {reply,ok,State#state{dsock = undefined,
- chunk = false}};
- trans_no_space ->
- ?STOP_RET(etnospc);
- perm_no_space ->
- ?STOP_RET(epnospc);
- perm_fname_not_allowed ->
- ?STOP_RET(efnamena);
- Result ->
- debug(" error: send chunk end (1): ~p~n",
- [Result]),
- {reply,{error,epath},State#state{dsock = undefined,
- chunk = false}}
- end;
- _ ->
- sock_close(DSock),
- Result = result(CSock),
- case Result of
- pos_compl ->
- {reply,ok,State#state{dsock = undefined,
- chunk = false}};
- trans_no_space ->
- sock_close(CSock),
- ?STOP_RET(etnospc);
- perm_no_space ->
- sock_close(CSock),
- ?STOP_RET(epnospc);
- perm_fname_not_allowed ->
- sock_close(CSock),
- ?STOP_RET(efnamena);
- Result ->
- debug(" error: send chunk end (2): ~p~n",
- [Result]),
- {reply,{error,epath},State#state{dsock = undefined,
- chunk = false}}
- end
- end.
-
-get_key1(Key,List,Default)->
- case lists:keysearch(Key,1,List)of
- {value,{_,Val}}->
- Val;
- false->
- Default
- end.