rubysl-net-ftp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/net/ftp.rb +1 -0
- data/lib/net/ftptls.rb +1 -0
- data/lib/rubysl/net/ftp.rb +2 -0
- data/lib/rubysl/net/ftp/ftp.rb +926 -0
- data/lib/rubysl/net/ftp/version.rb +7 -0
- data/rubysl-net-ftp.gemspec +23 -0
- data/spec/FTPError_spec.rb +7 -0
- data/spec/FTPPermError_spec.rb +11 -0
- data/spec/FTPProtoError_spec.rb +11 -0
- data/spec/FTPReplyError_spec.rb +11 -0
- data/spec/FTPTempError_spec.rb +11 -0
- data/spec/abort_spec.rb +61 -0
- data/spec/acct_spec.rb +57 -0
- data/spec/binary_spec.rb +23 -0
- data/spec/chdir_spec.rb +100 -0
- data/spec/close_spec.rb +29 -0
- data/spec/closed_spec.rb +20 -0
- data/spec/connect_spec.rb +48 -0
- data/spec/debug_mode_spec.rb +22 -0
- data/spec/delete_spec.rb +58 -0
- data/spec/dir_spec.rb +7 -0
- data/spec/fixtures/putbinaryfile +3 -0
- data/spec/fixtures/puttextfile +3 -0
- data/spec/fixtures/server.rb +265 -0
- data/spec/get_spec.rb +20 -0
- data/spec/getbinaryfile_spec.rb +7 -0
- data/spec/getdir_spec.rb +6 -0
- data/spec/gettextfile_spec.rb +7 -0
- data/spec/help_spec.rb +65 -0
- data/spec/initialize_spec.rb +86 -0
- data/spec/last_response_code_spec.rb +7 -0
- data/spec/last_response_spec.rb +24 -0
- data/spec/lastresp_spec.rb +7 -0
- data/spec/list_spec.rb +7 -0
- data/spec/login_spec.rb +215 -0
- data/spec/ls_spec.rb +7 -0
- data/spec/mdtm_spec.rb +37 -0
- data/spec/mkdir_spec.rb +60 -0
- data/spec/mtime_spec.rb +49 -0
- data/spec/nlst_spec.rb +120 -0
- data/spec/noop_spec.rb +37 -0
- data/spec/open_spec.rb +54 -0
- data/spec/passive_spec.rb +23 -0
- data/spec/put_spec.rb +20 -0
- data/spec/putbinaryfile_spec.rb +7 -0
- data/spec/puttextfile_spec.rb +7 -0
- data/spec/pwd_spec.rb +52 -0
- data/spec/quit_spec.rb +32 -0
- data/spec/rename_spec.rb +93 -0
- data/spec/resume_spec.rb +22 -0
- data/spec/retrbinary_spec.rb +29 -0
- data/spec/retrlines_spec.rb +33 -0
- data/spec/return_code_spec.rb +23 -0
- data/spec/rmdir_spec.rb +57 -0
- data/spec/sendcmd_spec.rb +53 -0
- data/spec/set_socket_spec.rb +7 -0
- data/spec/shared/getbinaryfile.rb +179 -0
- data/spec/shared/gettextfile.rb +129 -0
- data/spec/shared/last_response_code.rb +25 -0
- data/spec/shared/list.rb +133 -0
- data/spec/shared/putbinaryfile.rb +232 -0
- data/spec/shared/puttextfile.rb +149 -0
- data/spec/shared/pwd.rb +3 -0
- data/spec/site_spec.rb +52 -0
- data/spec/size_spec.rb +47 -0
- data/spec/status_spec.rb +62 -0
- data/spec/storbinary_spec.rb +47 -0
- data/spec/storlines_spec.rb +42 -0
- data/spec/system_spec.rb +47 -0
- data/spec/voidcmd_spec.rb +53 -0
- data/spec/welcome_spec.rb +24 -0
- metadata +243 -0
data/spec/delete_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
require File.expand_path('../fixtures/server', __FILE__)
|
3
|
+
|
4
|
+
describe "Net::FTP#delete" do
|
5
|
+
before(:each) do
|
6
|
+
@server = NetFTPSpecs::DummyFTP.new
|
7
|
+
@server.serve_once
|
8
|
+
|
9
|
+
@ftp = Net::FTP.new
|
10
|
+
@ftp.connect("localhost", 9921)
|
11
|
+
end
|
12
|
+
|
13
|
+
after(:each) do
|
14
|
+
@ftp.quit rescue nil
|
15
|
+
@ftp.close
|
16
|
+
@server.stop
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sends the DELE command with the passed filename to the server" do
|
20
|
+
@ftp.delete("test.file")
|
21
|
+
@ftp.last_response.should == "250 Requested file action okay, completed. (DELE test.file)\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises a Net::FTPTempError when the response code is 450" do
|
25
|
+
@server.should_receive(:dele).and_respond("450 Requested file action not taken.")
|
26
|
+
lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "raises a Net::FTPPermError when the response code is 550" do
|
30
|
+
@server.should_receive(:dele).and_respond("550 Requested action not taken.")
|
31
|
+
lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises a Net::FTPPermError when the response code is 500" do
|
35
|
+
@server.should_receive(:dele).and_respond("500 Syntax error, command unrecognized.")
|
36
|
+
lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "raises a Net::FTPPermError when the response code is 501" do
|
40
|
+
@server.should_receive(:dele).and_respond("501 Syntax error in parameters or arguments.")
|
41
|
+
lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "raises a Net::FTPPermError when the response code is 502" do
|
45
|
+
@server.should_receive(:dele).and_respond("502 Command not implemented.")
|
46
|
+
lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "raises a Net::FTPTempError when the response code is 421" do
|
50
|
+
@server.should_receive(:dele).and_respond("421 Service not available, closing control connection.")
|
51
|
+
lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "raises a Net::FTPPermError when the response code is 530" do
|
55
|
+
@server.should_receive(:dele).and_respond("530 Not logged in.")
|
56
|
+
lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError)
|
57
|
+
end
|
58
|
+
end
|
data/spec/dir_spec.rb
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
module NetFTPSpecs
|
2
|
+
class DummyFTP
|
3
|
+
attr_accessor :connect_message
|
4
|
+
attr_reader :login_user, :login_pass, :login_acct
|
5
|
+
|
6
|
+
def initialize(port = 9921)
|
7
|
+
@server = TCPServer.new("localhost", port)
|
8
|
+
|
9
|
+
@handlers = {}
|
10
|
+
@commands = []
|
11
|
+
@connect_message = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def serve_once
|
15
|
+
@thread = Thread.new do
|
16
|
+
@socket = @server.accept
|
17
|
+
handle_request
|
18
|
+
@socket.close
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle_request
|
23
|
+
# Send out the welcome message.
|
24
|
+
response @connect_message || "220 Dummy FTP Server ready!"
|
25
|
+
|
26
|
+
begin
|
27
|
+
loop do
|
28
|
+
command = @socket.recv(1024)
|
29
|
+
break if command.nil?
|
30
|
+
|
31
|
+
command, argument = command.chomp.split(" ", 2)
|
32
|
+
|
33
|
+
if command == "QUIT"
|
34
|
+
self.response("221 OK, bye")
|
35
|
+
break
|
36
|
+
elsif proc_handler = @handlers[command.downcase.to_sym]
|
37
|
+
if argument.nil?
|
38
|
+
proc_handler.call(self)
|
39
|
+
else
|
40
|
+
proc_handler.call(self, argument)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
if argument.nil?
|
44
|
+
self.send(command.downcase.to_sym)
|
45
|
+
else
|
46
|
+
self.send(command.downcase.to_sym, argument)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
rescue => e
|
51
|
+
self.error_response("Exception: #{e} #{e.backtrace.inspect}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def error_response(text)
|
56
|
+
self.response("451 #{text}")
|
57
|
+
end
|
58
|
+
|
59
|
+
def response(text)
|
60
|
+
@socket.puts(text) unless @socket.closed?
|
61
|
+
end
|
62
|
+
|
63
|
+
def stop
|
64
|
+
@datasocket.close unless @datasocket.nil? || @datasocket.closed?
|
65
|
+
@server.close
|
66
|
+
@thread.join
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
##
|
71
|
+
def handle(sym, &block)
|
72
|
+
@handlers[sym] = block
|
73
|
+
end
|
74
|
+
|
75
|
+
def should_receive(method)
|
76
|
+
@handler_for = method
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def and_respond(text)
|
81
|
+
@handlers[@handler_for] = lambda { |s, *args| s.response(text) }
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# FTP methods
|
86
|
+
##
|
87
|
+
|
88
|
+
def abor
|
89
|
+
self.response("226 Closing data connection. (ABOR)")
|
90
|
+
end
|
91
|
+
|
92
|
+
def acct(account)
|
93
|
+
@login_acct = account
|
94
|
+
self.response("230 User '#{account}' logged in, proceed. (ACCT)")
|
95
|
+
end
|
96
|
+
|
97
|
+
def cdup
|
98
|
+
self.response("200 Command okay. (CDUP)")
|
99
|
+
end
|
100
|
+
|
101
|
+
def cwd(dir)
|
102
|
+
self.response("200 Command okay. (CWD #{dir})")
|
103
|
+
end
|
104
|
+
|
105
|
+
def dele(file)
|
106
|
+
self.response("250 Requested file action okay, completed. (DELE #{file})")
|
107
|
+
end
|
108
|
+
|
109
|
+
def eprt(arg)
|
110
|
+
_, _, host, port = arg.split("|")
|
111
|
+
|
112
|
+
@datasocket = TCPSocket.new(host, port)
|
113
|
+
self.response("200 port opened")
|
114
|
+
end
|
115
|
+
|
116
|
+
def help(param = :default)
|
117
|
+
if param == :default
|
118
|
+
self.response("211 System status, or system help reply. (HELP)")
|
119
|
+
else
|
120
|
+
self.response("211 System status, or system help reply. (HELP #{param})")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def list(folder)
|
125
|
+
self.response("150 opening ASCII connection for file list")
|
126
|
+
@datasocket.puts("-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb")
|
127
|
+
@datasocket.puts("-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb")
|
128
|
+
@datasocket.puts("-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb")
|
129
|
+
@datasocket.close()
|
130
|
+
self.response("226 transfer complete (LIST #{folder})")
|
131
|
+
end
|
132
|
+
|
133
|
+
def mdtm(filename)
|
134
|
+
self.response("213 19980705132316")
|
135
|
+
end
|
136
|
+
|
137
|
+
def mkd(foldername)
|
138
|
+
self.response(%Q{257 "#{foldername.gsub('"', '""')}" created.})
|
139
|
+
end
|
140
|
+
|
141
|
+
def nlst(folder = nil)
|
142
|
+
self.response("150 opening ASCII connection for file list")
|
143
|
+
@datasocket.puts("last_response_code.rb")
|
144
|
+
@datasocket.puts("list.rb")
|
145
|
+
@datasocket.puts("pwd.rb")
|
146
|
+
@datasocket.close()
|
147
|
+
self.response("226 transfer complete (NLST#{folder ? " #{folder}" : ""})")
|
148
|
+
end
|
149
|
+
|
150
|
+
def noop
|
151
|
+
self.response("200 Command okay. (NOOP)")
|
152
|
+
end
|
153
|
+
|
154
|
+
def pass(password)
|
155
|
+
@login_pass = password
|
156
|
+
self.response("230 User logged in, proceed. (PASS #{password})")
|
157
|
+
end
|
158
|
+
|
159
|
+
def port(arg)
|
160
|
+
nums = arg.split(",")
|
161
|
+
|
162
|
+
if nums[0] == "::1"
|
163
|
+
# IPv6
|
164
|
+
port = nums[1].to_i * 256 + nums[2].to_i
|
165
|
+
host = nums[0]
|
166
|
+
else
|
167
|
+
# IPv4
|
168
|
+
port = nums[4].to_i * 256 + nums[5].to_i
|
169
|
+
host = nums[0..3].join(".")
|
170
|
+
end
|
171
|
+
|
172
|
+
@datasocket = TCPSocket.new(host, port)
|
173
|
+
self.response("200 port opened")
|
174
|
+
end
|
175
|
+
|
176
|
+
def pwd
|
177
|
+
self.response('257 "/some/dir/" - current directory')
|
178
|
+
end
|
179
|
+
|
180
|
+
def retr(file)
|
181
|
+
self.response("125 Data transfer starting")
|
182
|
+
if @restart_at && @restart_at == 20
|
183
|
+
@datasocket.puts("of the file named '#{file}'.")
|
184
|
+
@restart_at = nil
|
185
|
+
else
|
186
|
+
@datasocket.puts("This is the content")
|
187
|
+
@datasocket.puts("of the file named '#{file}'.")
|
188
|
+
end
|
189
|
+
@datasocket.close()
|
190
|
+
self.response("226 Closing data connection. (RETR #{file})")
|
191
|
+
end
|
192
|
+
|
193
|
+
def rest(at_bytes)
|
194
|
+
@restart_at = at_bytes.to_i
|
195
|
+
self.response("350 Requested file action pending further information. (REST)")
|
196
|
+
end
|
197
|
+
|
198
|
+
def rmd(folder)
|
199
|
+
self.response("250 Requested file action okay, completed. (RMD #{folder})")
|
200
|
+
end
|
201
|
+
|
202
|
+
def rnfr(from)
|
203
|
+
@rename_from = from
|
204
|
+
self.response("350 Requested file action pending further information.")
|
205
|
+
end
|
206
|
+
|
207
|
+
def rnto(to)
|
208
|
+
self.response("250 Requested file action okay, completed. (Renamed #{@rename_from} to #{to})")
|
209
|
+
@rename_from = nil
|
210
|
+
end
|
211
|
+
|
212
|
+
def site(param)
|
213
|
+
self.response("200 Command okay. (SITE #{param})")
|
214
|
+
end
|
215
|
+
|
216
|
+
def size(filename)
|
217
|
+
if filename == "binary"
|
218
|
+
self.response("213 24")
|
219
|
+
else
|
220
|
+
self.response("213 1024")
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def stat
|
225
|
+
self.response("211 System status, or system help reply. (STAT)")
|
226
|
+
end
|
227
|
+
|
228
|
+
def stor(file)
|
229
|
+
tmp_file = tmp("#{file}file", false)
|
230
|
+
|
231
|
+
self.response("125 Data transfer starting.")
|
232
|
+
|
233
|
+
mode = @restart_at ? "a" : "w"
|
234
|
+
|
235
|
+
File.open(tmp_file, mode + "b") do |f|
|
236
|
+
loop do
|
237
|
+
data = @datasocket.recv(1024)
|
238
|
+
break if !data || data.empty?
|
239
|
+
f << data
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
#@datasocket.close()
|
244
|
+
self.response("200 OK, Data received. (STOR #{file})")
|
245
|
+
end
|
246
|
+
|
247
|
+
def appe(file)
|
248
|
+
@restart_at = true
|
249
|
+
stor(file)
|
250
|
+
end
|
251
|
+
|
252
|
+
def syst
|
253
|
+
self.response("215 FTP Dummy Server (SYST)")
|
254
|
+
end
|
255
|
+
|
256
|
+
def type(type)
|
257
|
+
self.response("200 TYPE switched to #{type}")
|
258
|
+
end
|
259
|
+
|
260
|
+
def user(name)
|
261
|
+
@login_user = name
|
262
|
+
self.response("230 User logged in, proceed. (USER #{name})")
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
data/spec/get_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
require File.expand_path('../fixtures/server', __FILE__)
|
3
|
+
require File.expand_path('../shared/gettextfile', __FILE__)
|
4
|
+
require File.expand_path('../shared/getbinaryfile', __FILE__)
|
5
|
+
|
6
|
+
describe "Net::FTP#get (binary mode)" do
|
7
|
+
before(:each) do
|
8
|
+
@binary_mode = true
|
9
|
+
end
|
10
|
+
|
11
|
+
it_behaves_like :net_ftp_getbinaryfile, :get
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Net::FTP#get (text mode)" do
|
15
|
+
before(:each) do
|
16
|
+
@binary_mode = false
|
17
|
+
end
|
18
|
+
|
19
|
+
it_behaves_like :net_ftp_gettextfile, :get
|
20
|
+
end
|
data/spec/getdir_spec.rb
ADDED
data/spec/help_spec.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
require File.expand_path('../fixtures/server', __FILE__)
|
3
|
+
|
4
|
+
describe "Net::FTP#help" do
|
5
|
+
def with_connection
|
6
|
+
yield
|
7
|
+
end
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@server = NetFTPSpecs::DummyFTP.new
|
11
|
+
@server.serve_once
|
12
|
+
|
13
|
+
@ftp = Net::FTP.new
|
14
|
+
@ftp.connect("localhost", 9921)
|
15
|
+
end
|
16
|
+
|
17
|
+
after(:each) do
|
18
|
+
@ftp.quit rescue nil
|
19
|
+
@ftp.close
|
20
|
+
@server.stop
|
21
|
+
end
|
22
|
+
|
23
|
+
it "writes the HELP command to the server" do
|
24
|
+
@ftp.help
|
25
|
+
@ftp.last_response.should == "211 System status, or system help reply. (HELP)\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns the server's response" do
|
29
|
+
@ftp.help.should == "211 System status, or system help reply. (HELP)\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "writes the HELP command with an optional parameter to the socket" do
|
33
|
+
@ftp.help("some parameter").should == "211 System status, or system help reply. (HELP some parameter)\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "does not raise any error when the response code is 211" do
|
37
|
+
@server.should_receive(:help).and_respond("211 System status, or system help reply.")
|
38
|
+
lambda { @ftp.help }.should_not raise_error
|
39
|
+
end
|
40
|
+
|
41
|
+
it "does not raise any error when the response code is 214" do
|
42
|
+
@server.should_receive(:help).and_respond("214 Help message.")
|
43
|
+
lambda { @ftp.help }.should_not raise_error
|
44
|
+
end
|
45
|
+
|
46
|
+
it "raises a Net::FTPPermError when the response code is 500" do
|
47
|
+
@server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.")
|
48
|
+
lambda { @ftp.help }.should raise_error(Net::FTPPermError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises a Net::FTPPermError when the response code is 501" do
|
52
|
+
@server.should_receive(:help).and_respond("501 Syntax error in parameters or arguments.")
|
53
|
+
lambda { @ftp.help }.should raise_error(Net::FTPPermError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "raises a Net::FTPPermError when the response code is 502" do
|
57
|
+
@server.should_receive(:help).and_respond("502 Command not implemented.")
|
58
|
+
lambda { @ftp.help }.should raise_error(Net::FTPPermError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises a Net::FTPTempError when the response code is 421" do
|
62
|
+
@server.should_receive(:help).and_respond("421 Service not available, closing control connection.")
|
63
|
+
lambda { @ftp.help }.should raise_error(Net::FTPTempError)
|
64
|
+
end
|
65
|
+
end
|