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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/net/ftp.rb +1 -0
  9. data/lib/net/ftptls.rb +1 -0
  10. data/lib/rubysl/net/ftp.rb +2 -0
  11. data/lib/rubysl/net/ftp/ftp.rb +926 -0
  12. data/lib/rubysl/net/ftp/version.rb +7 -0
  13. data/rubysl-net-ftp.gemspec +23 -0
  14. data/spec/FTPError_spec.rb +7 -0
  15. data/spec/FTPPermError_spec.rb +11 -0
  16. data/spec/FTPProtoError_spec.rb +11 -0
  17. data/spec/FTPReplyError_spec.rb +11 -0
  18. data/spec/FTPTempError_spec.rb +11 -0
  19. data/spec/abort_spec.rb +61 -0
  20. data/spec/acct_spec.rb +57 -0
  21. data/spec/binary_spec.rb +23 -0
  22. data/spec/chdir_spec.rb +100 -0
  23. data/spec/close_spec.rb +29 -0
  24. data/spec/closed_spec.rb +20 -0
  25. data/spec/connect_spec.rb +48 -0
  26. data/spec/debug_mode_spec.rb +22 -0
  27. data/spec/delete_spec.rb +58 -0
  28. data/spec/dir_spec.rb +7 -0
  29. data/spec/fixtures/putbinaryfile +3 -0
  30. data/spec/fixtures/puttextfile +3 -0
  31. data/spec/fixtures/server.rb +265 -0
  32. data/spec/get_spec.rb +20 -0
  33. data/spec/getbinaryfile_spec.rb +7 -0
  34. data/spec/getdir_spec.rb +6 -0
  35. data/spec/gettextfile_spec.rb +7 -0
  36. data/spec/help_spec.rb +65 -0
  37. data/spec/initialize_spec.rb +86 -0
  38. data/spec/last_response_code_spec.rb +7 -0
  39. data/spec/last_response_spec.rb +24 -0
  40. data/spec/lastresp_spec.rb +7 -0
  41. data/spec/list_spec.rb +7 -0
  42. data/spec/login_spec.rb +215 -0
  43. data/spec/ls_spec.rb +7 -0
  44. data/spec/mdtm_spec.rb +37 -0
  45. data/spec/mkdir_spec.rb +60 -0
  46. data/spec/mtime_spec.rb +49 -0
  47. data/spec/nlst_spec.rb +120 -0
  48. data/spec/noop_spec.rb +37 -0
  49. data/spec/open_spec.rb +54 -0
  50. data/spec/passive_spec.rb +23 -0
  51. data/spec/put_spec.rb +20 -0
  52. data/spec/putbinaryfile_spec.rb +7 -0
  53. data/spec/puttextfile_spec.rb +7 -0
  54. data/spec/pwd_spec.rb +52 -0
  55. data/spec/quit_spec.rb +32 -0
  56. data/spec/rename_spec.rb +93 -0
  57. data/spec/resume_spec.rb +22 -0
  58. data/spec/retrbinary_spec.rb +29 -0
  59. data/spec/retrlines_spec.rb +33 -0
  60. data/spec/return_code_spec.rb +23 -0
  61. data/spec/rmdir_spec.rb +57 -0
  62. data/spec/sendcmd_spec.rb +53 -0
  63. data/spec/set_socket_spec.rb +7 -0
  64. data/spec/shared/getbinaryfile.rb +179 -0
  65. data/spec/shared/gettextfile.rb +129 -0
  66. data/spec/shared/last_response_code.rb +25 -0
  67. data/spec/shared/list.rb +133 -0
  68. data/spec/shared/putbinaryfile.rb +232 -0
  69. data/spec/shared/puttextfile.rb +149 -0
  70. data/spec/shared/pwd.rb +3 -0
  71. data/spec/site_spec.rb +52 -0
  72. data/spec/size_spec.rb +47 -0
  73. data/spec/status_spec.rb +62 -0
  74. data/spec/storbinary_spec.rb +47 -0
  75. data/spec/storlines_spec.rb +42 -0
  76. data/spec/system_spec.rb +47 -0
  77. data/spec/voidcmd_spec.rb +53 -0
  78. data/spec/welcome_spec.rb +24 -0
  79. metadata +243 -0
@@ -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,7 @@
1
+ require 'net/ftp'
2
+ require File.expand_path('../fixtures/server', __FILE__)
3
+ require File.expand_path('../shared/list', __FILE__)
4
+
5
+ describe "Net::FTP#dir" do
6
+ it_behaves_like :net_ftp_list, :dir
7
+ end
@@ -0,0 +1,3 @@
1
+ This is an example file
2
+ which is going to be transmitted
3
+ using #putbinaryfile.
@@ -0,0 +1,3 @@
1
+ This is an example file
2
+ which is going to be transmitted
3
+ using #puttextfile.
@@ -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
@@ -0,0 +1,7 @@
1
+ require 'net/ftp'
2
+ require File.expand_path('../fixtures/server', __FILE__)
3
+ require File.expand_path('../shared/getbinaryfile', __FILE__)
4
+
5
+ describe "Net::FTP#getbinaryfile" do
6
+ it_behaves_like :net_ftp_getbinaryfile, :getbinaryfile
7
+ end
@@ -0,0 +1,6 @@
1
+ require 'net/ftp'
2
+ require File.expand_path('../shared/pwd', __FILE__)
3
+
4
+ describe "Net::FTP#getdir" do
5
+ it_behaves_like :net_ftp_pwd, :getdir
6
+ end
@@ -0,0 +1,7 @@
1
+ require 'net/ftp'
2
+ require File.expand_path('../fixtures/server', __FILE__)
3
+ require File.expand_path('../shared/gettextfile', __FILE__)
4
+
5
+ describe "Net::FTP#gettextfile" do
6
+ it_behaves_like :net_ftp_gettextfile, :gettextfile
7
+ end
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