Skip to content

Instantly share code, notes, and snippets.

@xslogic
Created November 15, 2010 20:43
Show Gist options
  • Save xslogic/700908 to your computer and use it in GitHub Desktop.
Save xslogic/700908 to your computer and use it in GitHub Desktop.
FTP Patch for resuming FTP download..
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index 534fcae..1062c08 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -31,11 +31,11 @@
%% API - Client interface
-export([cd/2, close/1, delete/2, formaterror/1,
lcd/2, lpwd/1, ls/1, ls/2,
- mkdir/2, nlist/1, nlist/2,
+ mkdir/2, nlist/1, nlist/2, sizeof/2,
open/1, open/2, open/3, force_active/1,
pwd/1, quote/2,
recv/2, recv/3, recv_bin/2,
- recv_chunk_start/2, recv_chunk/1,
+ recv_chunk_start/2, recv_chunk_start/3, 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,
@@ -250,12 +250,26 @@ cd(Pid, Dir) ->
lcd(Pid, Dir) ->
call(Pid, {lcd, Dir}, string).
+
+%%--------------------------------------------------------------------------
+%% sizeof(Pid, File) -> ok | {error, <something>}
+%% Pid = pid()
+%% Dir = string()
+%%
+%% Description: Return size of file.
+%%--------------------------------------------------------------------------
+sizeof(Pid, File) ->
+ call(Pid, {sizeof, File}, string).
+
+
%%--------------------------------------------------------------------------
%% ls(Pid) -> Result
%% ls(Pid, <Dir>) -> Result
+%% ls(Pid, <Dir>, RType) -> Result
%%
%% Pid = pid()
%% Dir = string()
+%% RType = string | binary
%% Result = {ok, Listing} | {error, Reason}
%% Listing = string()
%% Reason = epath | elogin | econn
@@ -263,9 +277,11 @@ lcd(Pid, Dir) ->
%% Description: Returns a list of files in long format.
%%--------------------------------------------------------------------------
ls(Pid) ->
- ls(Pid, "").
+ ls(Pid, "", string).
ls(Pid, Dir) ->
- call(Pid, {dir, long, Dir}, string).
+ ls(Pid, Dir, string).
+ls(Pid, Dir, RType) ->
+ call(Pid, {dir, long, Dir}, RType).
%%--------------------------------------------------------------------------
%% nlist(Pid) -> Result
@@ -373,6 +389,9 @@ recv_bin(Pid, RemoteFile) ->
recv_chunk_start(Pid, RemoteFile) ->
call(Pid, {recv_chunk_start, RemoteFile}, atom).
+recv_chunk_start(Pid, RemoteFile, Pos) ->
+ call(Pid, {recv_chunk_start, RemoteFile, Pos}, atom).
+
%%--------------------------------------------------------------------------
%% recv_chunk(Pid, RemoteFile) -> ok | {ok, Bin} | {error, Reason}
%% Pid = pid()
@@ -938,6 +957,12 @@ handle_call({_, {delete, File}}, {_Pid, _} = From,
activate_ctrl_connection(State),
{noreply, State#state{client = From}};
+handle_call({_, {sizeof, File}}, {_Pid, _} = From,
+ #state{chunk = false} = State) ->
+ send_ctrl_message(State, mk_cmd("SIZE ~s", [File])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{client = From, caller = sizeof}};
+
handle_call({_, {mkdir, Dir}}, From, #state{chunk = false} = State) ->
send_ctrl_message(State, mk_cmd("MKD ~s", [Dir])),
activate_ctrl_connection(State),
@@ -985,6 +1010,12 @@ handle_call({_, {recv_bin, RemoteFile}}, From, #state{chunk = false} =
setup_data_connection(State#state{caller = {recv_bin, RemoteFile},
client = From});
+handle_call({_,{recv_chunk_start, RemoteFile, Pos}}, From, #state{chunk = false}
+ = State) ->
+ setup_data_connection(State#state{caller = {start_chunk_transfer,
+ "RETR", RemoteFile, Pos},
+ client = From});
+
handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false}
= State) ->
setup_data_connection(State#state{caller = {start_chunk_transfer,
@@ -992,6 +1023,10 @@ handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false}
client = From});
handle_call({_, recv_chunk}, _, #state{chunk = false} = State) ->
+ activate_ctrl_connection(State#state{chunk = flush_ctrl}),
+ {reply, ok, State#state{chunk = flush_ctrl}};
+
+handle_call({_, recv_chunk}, _, #state{chunk = flush_ctrl} = State) ->
{reply, {error, "ftp:recv_chunk_start/2 not called"}, State};
handle_call({_, recv_chunk}, From, #state{chunk = true} = State) ->
@@ -1047,13 +1082,12 @@ handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State) ->
activate_ctrl_connection(State),
{noreply, State#state{client = From, caller = quote}};
-handle_call({_, _Req}, _From, #state{csock = CSock} = State)
- when (CSock =:= undefined) ->
- {reply, {error, not_connected}, State};
-
handle_call(_, _, #state{chunk = true} = State) ->
{reply, {error, echunk}, State};
+handle_call(_Request, _, #state{chunk = flush_ctrl} = State) ->
+ {reply, {wait, "Awaiting flush"}, State};
+
%% Catch all - This can only happen if the application programmer writes
%% really bad code that violates the API.
handle_call(Request, _Timeout, State) ->
@@ -1366,6 +1400,15 @@ handle_user_account(Acc, State) ->
%%--------------------------------------------------------------------------
%%--------------------------------------------------------------------------
%% Handling of control connection setup
+handle_ctrl_result({pos_size, Size}, State) ->
+ ctrl_result_response({pos_size, Size}, State, ok);
+
+handle_ctrl_result({pos_complx, _}, State) ->
+ handle_caller(State);
+
+handle_ctrl_result(_, #state{chunk = flush_ctrl} = State) ->
+ {noreply, State#state{chunk = false}};
+
handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From}
= State) ->
gen_server:reply(From, {ok, self()}),
@@ -1631,6 +1674,10 @@ handle_ctrl_result({Status, Lines}, #state{client = From} = State)
%%--------------------------------------------------------------------------
%% Help functions to handle_ctrl_result
%%--------------------------------------------------------------------------
+ctrl_result_response({pos_size, Size}, #state{client = From} = State, _) ->
+ gen_server:reply(From, {ok, Size}),
+ {noreply, State#state{client = undefined, caller = undefined}};
+
ctrl_result_response(pos_compl, #state{client = From} = State, _) ->
gen_server:reply(From, ok),
{noreply, State#state{client = undefined, caller = undefined}};
@@ -1669,6 +1716,13 @@ handle_caller(#state{caller = {recv_bin, RemoteFile}} = State) ->
activate_ctrl_connection(State),
{noreply, State#state{caller = recv_bin}};
+
+handle_caller(#state{caller = {start_chunk_transfer, Cmd, RemoteFile, Pos}} =
+ State) ->
+ send_ctrl_message(State, mk_cmd("~s ~p", ["REST", Pos])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{caller = {start_chunk_transfer, Cmd, RemoteFile}}};
+
handle_caller(#state{caller = {start_chunk_transfer, Cmd, RemoteFile}} =
State) ->
send_ctrl_message(State, mk_cmd("~s ~s", [Cmd, RemoteFile])),
@@ -1924,9 +1978,16 @@ file_write(Bytes, Fd) ->
call(GenServer, Msg, Format) ->
call(GenServer, Msg, Format, infinity).
call(GenServer, Msg, Format, Timeout) ->
- Req = {self(), Msg},
- case (catch gen_server:call(GenServer, Req, Timeout)) of
- {ok, Bin} when is_binary(Bin) andalso (Format =:= string) ->
+
+ Result = (catch gen_server:call(GenServer, {self(), Msg}, Timeout)),
+
+ case Result of
+ {wait, _} ->
+ receive
+ after 1500 ->
+ call(GenServer, Msg, Format, Timeout)
+ end;
+ {ok, Bin} when is_binary(Bin), Format == string ->
{ok, binary_to_list(Bin)};
{'EXIT', _} ->
{error, eclosed};
diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl
index faeacb3..404e3ae 100644
--- a/lib/inets/src/ftp/ftp_response.erl
+++ b/lib/inets/src/ftp/ftp_response.erl
@@ -175,9 +175,11 @@ error_string(Reason) ->
%% Positive Preleminary Reply
interpret_status(?POS_PREL,_,_) -> pos_prel;
%% Positive Completion Reply
+interpret_status(?POS_COMPL,?INFORMATION,3) -> pos_size;
interpret_status(?POS_COMPL,_,_) -> pos_compl;
%% Positive Intermediate Reply nedd account
interpret_status(?POS_INTERM,?AUTH_ACC,2) -> pos_interm_acct;
+interpret_status(?POS_INTERM,?FILE_SYSTEM,_) -> pos_complx;
%% Positive Intermediate Reply
interpret_status(?POS_INTERM,_,_) -> pos_interm;
%% No storage area no action taken
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment