raval 0.0.1

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.
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+
3
+ module Raval
4
+ # converts a list of objects that describe the contents of a directory and
5
+ # formats them to be valid responses to the LIST or NLST FTP commands.
6
+ class ListFormatter
7
+ def initialize(files)
8
+ @files = check_duck_type(files)
9
+ end
10
+
11
+ # response to the NLST command
12
+ #
13
+ def short
14
+ @files.map(&:name)
15
+ end
16
+
17
+ # response to the LIST command
18
+ #
19
+ def detailed
20
+ now = Time.now
21
+ @files.map { |item|
22
+ directory = item.directory ? 'd' : '-'
23
+ permissions = item.permissions || 'rwxrwxrwx'
24
+ owner = item.owner || 'owner'
25
+ group = item.group || 'group'
26
+ size = (item.size || 0).to_s.rjust(12)
27
+ time = (item.time || now).strftime("%b %d %H:%M")
28
+ name = item.name || "UNKNOWN"
29
+
30
+ "#{directory}#{permissions} 1 #{owner} #{group} #{size} #{time} #{name}"
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ def check_duck_type(files)
37
+ files.each do |file|
38
+ file.respond_to?(:directory) || raise(ArgumentError, "file must respond to #directory")
39
+ file.respond_to?(:size) || raise(ArgumentError, "file must respond to #size")
40
+ file.respond_to?(:permissions) || raise(ArgumentError, "file must respond to #permissions")
41
+ file.respond_to?(:owner) || raise(ArgumentError, "file must respond to #owner")
42
+ file.respond_to?(:group) || raise(ArgumentError, "file must respond to #group")
43
+ file.respond_to?(:time) || raise(ArgumentError, "file must respond to #time")
44
+ file.respond_to?(:name) || raise(ArgumentError, "file must respond to #name")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,86 @@
1
+ # coding: utf-8
2
+
3
+ require 'celluloid/io'
4
+
5
+ module Raval
6
+ # In passive FTP mode, the server opens a listening data socket and the client
7
+ # will connect to it.
8
+ #
9
+ # A different class is used when operating in active FTP mode. They both
10
+ # have a #read and #write method, so the quack like each other in the ways
11
+ # that matter.
12
+ class PassiveSocket
13
+ include Celluloid::IO
14
+
15
+ attr_reader :port
16
+
17
+ def initialize(host)
18
+ @host = host
19
+ @socket = nil
20
+ puts "*** Starting passive socket on #{host}"
21
+
22
+ # TODO ::TcpServer#addr exists, but Celluloid::IO::TcpServer#addr does
23
+ # not. Once that's fixed, make this port random by replacing 30000
24
+ # with 0
25
+ #@server = TCPServer.new(@host, 0)
26
+ #@port = @server.addr[1]
27
+ @port = choose_port(@host)
28
+ @server = TCPServer.new(@host, @port)
29
+ #@port = @server.addr[1]
30
+ run!
31
+ end
32
+
33
+ def run
34
+ handle_connection @server.accept
35
+ end
36
+
37
+ def read
38
+ raise "socket not connected" unless @socket
39
+ @socket.readpartial(4096)
40
+ rescue EOFError, Errno::ECONNRESET
41
+ close
42
+ nil
43
+ end
44
+
45
+ def write(data)
46
+ raise "socket not connected" unless @socket
47
+ @socket.write(data)
48
+ end
49
+
50
+ def connected?
51
+ @socket != nil
52
+ end
53
+
54
+ def close
55
+ @socket.close if @socket
56
+ @socket = nil
57
+ @server.close if @server
58
+ @server = nil
59
+ end
60
+
61
+ private
62
+
63
+ def handle_connection(socket)
64
+ @socket = socket
65
+ @server.close
66
+ @server = nil
67
+ rescue EOFError, IOError
68
+ puts "*** passive socket disconnected"
69
+ end
70
+
71
+ def choose_port(host)
72
+ candidate = 30000
73
+ while candidate < 40000
74
+ begin
75
+ server = TCPServer.new(host, candidate)
76
+ server.close
77
+ return candidate
78
+ rescue
79
+ candidate += 1
80
+ end
81
+ end
82
+ nil
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+
3
+ require 'celluloid/io'
4
+ require 'raval/handler'
5
+ require 'raval/connection'
6
+
7
+ module Raval
8
+ # The beginning of things. Listens on a TCP socket for incoming connections
9
+ # and spins off a new agent to handle each connection.
10
+ class Server
11
+ include Celluloid::IO
12
+
13
+ def initialize(host, port, driver, driver_opts)
14
+ @driver, @driver_opts = driver, driver_opts
15
+ # Since we included Celluloid::IO, we're actually making a
16
+ # Celluloid::IO::TCPServer here
17
+ @server = TCPServer.new(host, port)
18
+ run!
19
+ rescue Errno::EACCES
20
+ $stderr.puts "Unable to listen on port #{port}"
21
+ exit
22
+ end
23
+
24
+ def finalize
25
+ @server.close if @server
26
+ end
27
+
28
+ def run
29
+ loop { handle_connection! @server.accept }
30
+ end
31
+
32
+ def handle_connection(socket)
33
+ if @driver_opts
34
+ driver = @driver.new(@driver_opts)
35
+ else
36
+ driver = @driver.new
37
+ end
38
+ handler = Handler.new(driver)
39
+ connection = Connection.new(handler, socket)
40
+ connection.read_commands
41
+ rescue EOFError, IOError
42
+ puts "*** #{connection.host}:#{connection.port} disconnected"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,832 @@
1
+ # coding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ def handler_with_unauthenticated_user
6
+ connection = double(:connection)
7
+
8
+ handler = Raval::Handler.new(TestDriver.new)
9
+ connection.should_receive(:send_response).with(220, anything)
10
+ handler.new_connection(connection)
11
+ handler
12
+ end
13
+
14
+ def handler_with_authenticated_user
15
+ connection = double(:connection)
16
+
17
+ handler = Raval::Handler.new(TestDriver.new)
18
+ connection.should_receive(:send_response).with(220, anything)
19
+ handler.new_connection(connection)
20
+ connection.should_receive(:send_response).with(331, anything).and_return(331)
21
+ connection.should_receive(:send_response).with(230, anything).and_return(230)
22
+ handler.receive_line("USER test")
23
+ handler.receive_line("PASS 1234")
24
+ handler
25
+ end
26
+
27
+ describe Raval::Handler, "#new_connection" do
28
+
29
+ let(:connection) { double(:connection) }
30
+
31
+ subject {
32
+ Raval::Handler.new(TestDriver.new)
33
+ }
34
+
35
+ it "should default to a root name_prefix" do
36
+ connection.should_receive(:send_response).with(220, anything)
37
+ subject.new_connection(connection)
38
+ subject.name_prefix.should eql("/")
39
+ end
40
+
41
+ it "should respond with 220 when connection is opened" do
42
+ connection.should_receive(:send_response).with(220, anything)
43
+ subject.new_connection(connection)
44
+ end
45
+ end
46
+
47
+ describe Raval::Handler, "ALLO" do
48
+ context "with an unauthenticated user" do
49
+ subject { handler_with_unauthenticated_user }
50
+
51
+ it "should always respond with 202 when called" do
52
+ subject.connection.should_receive(:send_response).with(202, anything)
53
+ subject.receive_line("ALLO")
54
+ end
55
+ end
56
+ end
57
+
58
+ describe Raval::Handler, "USER" do
59
+ context "with an unauthenticated user" do
60
+
61
+ subject { handler_with_unauthenticated_user }
62
+
63
+ it "should respond with 331 when called by non-logged in user" do
64
+ subject.connection.should_receive(:send_response).with(331, anything)
65
+ subject.receive_line("USER jh")
66
+ end
67
+
68
+ it "should respond with 500 when called by a logged in user" do
69
+ subject.connection.should_receive(:send_response).with(331, anything).and_return(331)
70
+ subject.connection.should_receive(:send_response).with(230, anything).and_return(230)
71
+ subject.connection.should_receive(:send_response).with(500, anything).and_return(500)
72
+ subject.receive_line("USER test")
73
+ subject.receive_line("PASS 1234")
74
+ subject.receive_line("USER test")
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ describe Raval::Handler, "PASS" do
81
+ context "with an unauthenticated user" do
82
+ subject { handler_with_unauthenticated_user }
83
+
84
+ it "should respond with 202 when called by logged in user" do
85
+ subject.connection.should_receive(:send_response).with(331, anything).and_return(331)
86
+ subject.connection.should_receive(:send_response).with(230, anything).and_return(230)
87
+ subject.connection.should_receive(:send_response).with(202, anything).and_return(202)
88
+ subject.receive_line("USER test")
89
+ subject.receive_line("PASS 1234")
90
+ subject.receive_line("PASS 1234")
91
+ end
92
+
93
+ it "should respond with 553 when called with no param" do
94
+ subject.connection.should_receive(:send_response).with(331, anything).and_return(331)
95
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
96
+ subject.receive_line("USER test")
97
+ subject.receive_line("PASS")
98
+ end
99
+
100
+ it "should respond with 530 when called without first providing a username" do
101
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
102
+ subject.receive_line("PASS 1234")
103
+ end
104
+
105
+ it "should respond with 230 when user is authenticated" do
106
+ subject.connection.should_receive(:send_response).with(331, anything).and_return(331)
107
+ subject.connection.should_receive(:send_response).with(230, anything).and_return(230)
108
+ subject.receive_line("USER test")
109
+ subject.receive_line("PASS 1234")
110
+ end
111
+
112
+ it "should respond with 530 when password is incorrect" do
113
+ subject.connection.should_receive(:send_response).with(331, anything).and_return(331)
114
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
115
+ subject.receive_line("USER test")
116
+ subject.receive_line("PASS 1235")
117
+ end
118
+ end
119
+ end
120
+
121
+ %w(CDUP XCUP).each do |command|
122
+
123
+ describe Raval::Handler, command do
124
+ context "with an unauthenticated user" do
125
+ subject { handler_with_unauthenticated_user }
126
+
127
+ it "should respond with 530 if user is not logged in" do
128
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
129
+ subject.receive_line(command)
130
+ subject.name_prefix.should eql("/")
131
+ end
132
+ end
133
+ context "with an authenticated user" do
134
+ subject { handler_with_authenticated_user }
135
+
136
+ it "should respond with 250 if called from root dir" do
137
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
138
+ subject.receive_line(command)
139
+ subject.name_prefix.should eql("/")
140
+ end
141
+
142
+ it "should respond with 250 if called from inside a dir" do
143
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
144
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
145
+ subject.receive_line("CWD files")
146
+ subject.receive_line(command)
147
+ subject.name_prefix.should eql("/")
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ describe Raval::Handler, "CWD" do
154
+ context "with an unauthenticated user" do
155
+ subject { handler_with_unauthenticated_user }
156
+
157
+ it "should respond with 530 if user is not logged in" do
158
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
159
+ subject.receive_line("CWD")
160
+ end
161
+ end
162
+
163
+ context "with an authenticated user" do
164
+ subject { handler_with_authenticated_user }
165
+
166
+ it "should respond with 250 if called with '..' from users home" do
167
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
168
+ subject.receive_line("CWD ..")
169
+ subject.name_prefix.should eql("/")
170
+ end
171
+
172
+ it "should respond with 250 if called with '.' from users home" do
173
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
174
+ subject.receive_line("CWD .")
175
+ subject.name_prefix.should eql("/")
176
+ end
177
+
178
+ it "should respond with 250 if called with '/' from users home" do
179
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
180
+ subject.receive_line("CWD /")
181
+ subject.name_prefix.should eql("/")
182
+ end
183
+
184
+ it "should respond with 250 if called with 'files' from users home" do
185
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
186
+ subject.receive_line("CWD files")
187
+ subject.name_prefix.should eql("/files")
188
+ end
189
+
190
+ it "should respond with 250 if called with 'files/' from users home" do
191
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
192
+ subject.receive_line("CWD files/")
193
+ subject.name_prefix.should eql("/files")
194
+ end
195
+
196
+ it "should respond with 250 if called with '/files/' from users home" do
197
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
198
+ subject.receive_line("CWD /files/")
199
+ subject.name_prefix.should eql("/files")
200
+ end
201
+
202
+ it "should respond with 250 if called with '..' from the files dir" do
203
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
204
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
205
+ subject.receive_line("CWD files")
206
+ subject.name_prefix.should eql("/files")
207
+ subject.receive_line("CWD ..")
208
+ subject.name_prefix.should eql("/")
209
+ end
210
+
211
+ it "should respond with 250 if called with '/files' from the files dir" do
212
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
213
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
214
+ subject.receive_line("CWD files")
215
+ subject.name_prefix.should eql("/files")
216
+ subject.receive_line("CWD /files")
217
+ subject.name_prefix.should eql("/files")
218
+ end
219
+
220
+ it "should respond with 550 if called with unrecognised dir" do
221
+ subject.connection.should_receive(:send_response).with(550, anything).and_return(550)
222
+ subject.receive_line("CWD test")
223
+ subject.name_prefix.should eql("/")
224
+ end
225
+ end
226
+ end
227
+
228
+ describe Raval::Handler, "DELE" do
229
+ context "with an unauthenticated user" do
230
+ subject { handler_with_unauthenticated_user }
231
+
232
+ it "should respond with 530" do
233
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
234
+ subject.receive_line("DELE x")
235
+ end
236
+ end
237
+
238
+ context "with an authenticated user" do
239
+ subject { handler_with_authenticated_user }
240
+
241
+ it "should respond with 553 when the paramater is omitted" do
242
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
243
+ subject.receive_line("DELE")
244
+ end
245
+
246
+ it "should respond with 250 when the file is deleted" do
247
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
248
+ subject.receive_line("DELE four.txt")
249
+ end
250
+
251
+ it "should respond with 550 when the file is not deleted" do
252
+ subject.connection.should_receive(:send_response).with(550, anything).and_return(550)
253
+ subject.receive_line("DELE one.txt")
254
+ end
255
+ end
256
+ end
257
+
258
+ describe Raval::Handler, "HELP" do
259
+ context "with an unauthenticated user" do
260
+ subject { handler_with_unauthenticated_user }
261
+ it "should always respond with 214 when called" do
262
+ subject.connection.should_receive(:send_response).with("214-", anything).and_return("214-")
263
+ subject.connection.should_receive(:send_response)
264
+ subject.connection.should_receive(:send_response).with(214, anything).and_return(214)
265
+ subject.receive_line("HELP")
266
+ end
267
+ end
268
+ end
269
+
270
+ describe Raval::Handler, "NLST" do
271
+ context "with an unauthenticated user" do
272
+ subject { handler_with_unauthenticated_user }
273
+ it "should respond with 530" do
274
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
275
+ subject.receive_line("NLST")
276
+ end
277
+ end
278
+
279
+ context "with an authenticated user" do
280
+ subject { handler_with_authenticated_user }
281
+
282
+ it "should respond with 150 ...425 when called with no data socket"
283
+
284
+ it "should respond with 150 ... 226 when called in the root dir with no param"
285
+
286
+ it "should respond with 150 ... 226 when called in the files dir with no param"
287
+
288
+ it "should respond with 150 ... 226 when called in the files dir with wildcard (LIST *.txt)"
289
+
290
+ it "should respond with 150 ... 226 when called in the subdir with .. param"
291
+
292
+ it "should respond with 150 ... 226 when called in the subdir with / param"
293
+
294
+ it "should respond with 150 ... 226 when called in the root with files param"
295
+
296
+ it "should respond with 150 ... 226 when called in the root with files/ param"
297
+
298
+ it "should properly list subdirs etc."
299
+ end
300
+ end
301
+
302
+ describe Raval::Handler, "LIST" do
303
+ context "with an unauthenticated user" do
304
+ subject { handler_with_unauthenticated_user }
305
+ it "should respond with 530" do
306
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
307
+ subject.receive_line("LIST")
308
+ end
309
+ end
310
+
311
+ context "with an authenticated user" do
312
+ subject { handler_with_authenticated_user }
313
+
314
+ it "should respond with 150 ...425 when called with no data socket"
315
+
316
+ it "should respond with 150 ... 226 when called in the root dir with no param"
317
+
318
+ it "should respond with 150 ... 226 when called in the files dir with no param"
319
+
320
+ it "should respond with 150 ... 226 when called in the files dir with wildcard (LIST *.txt)"
321
+
322
+ it "should respond with 150 ... 226 when called in the subdir with .. param"
323
+
324
+ it "should respond with 150 ... 226 when called in the subdir with / param"
325
+
326
+ it "should respond with 150 ... 226 when called in the root with files param"
327
+
328
+ it "should respond with 150 ... 226 when called in the root with files/ param"
329
+
330
+ it "should properly list subdirs etc."
331
+ end
332
+ end
333
+
334
+ describe Raval::Handler, "MKD" do
335
+ context "with an unauthenticated user" do
336
+ subject { handler_with_unauthenticated_user }
337
+
338
+ it "should respond with 530 if user is not logged in" do
339
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
340
+ subject.receive_line("MKD x")
341
+ end
342
+ end
343
+
344
+ context "with an authenticated user" do
345
+ subject { handler_with_authenticated_user }
346
+ it "should respond with 553 when the paramater is omitted" do
347
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
348
+ subject.receive_line("MKD")
349
+ end
350
+
351
+ it "should respond with 257 when the directory is created" do
352
+ subject.connection.should_receive(:send_response).with(257, anything).and_return(257)
353
+ subject.receive_line("MKD four")
354
+ end
355
+
356
+ it "should respond with 550 when the directory is not created" do
357
+ subject.connection.should_receive(:send_response).with(550, anything).and_return(550)
358
+ subject.receive_line("MKD five")
359
+ end
360
+ end
361
+ end
362
+
363
+ describe Raval::Handler, "MODE" do
364
+ context "with an unauthenticated user" do
365
+ subject { handler_with_unauthenticated_user }
366
+
367
+ it "should always respond with 530 when called by user not logged in" do
368
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
369
+ subject.receive_line("MODE S")
370
+ end
371
+ end
372
+ context "with an authenticated user" do
373
+ subject { handler_with_authenticated_user }
374
+ it "should respond with 553 when called with no param" do
375
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
376
+ subject.receive_line("MODE")
377
+ end
378
+
379
+ it "should always respond with 200 when called with S param" do
380
+ subject.connection.should_receive(:send_response).with(200, anything).and_return(200)
381
+ subject.receive_line("MODE S")
382
+ end
383
+
384
+ it "should always respond with 504 when called with non-S param" do
385
+ subject.connection.should_receive(:send_response).with(504, anything).and_return(504)
386
+ subject.receive_line("MODE F")
387
+ end
388
+ end
389
+ end
390
+
391
+ describe Raval::Handler, "NOOP" do
392
+ context "with an unauthenticated user" do
393
+ subject { handler_with_unauthenticated_user }
394
+
395
+ it "should always respond with 200" do
396
+ subject.connection.should_receive(:send_response).with(200, anything).and_return(200)
397
+ subject.receive_line("NOOP")
398
+ end
399
+ end
400
+ end
401
+
402
+ describe Raval::Handler, "PASV" do
403
+ context "with an unauthenticated user" do
404
+ subject { handler_with_unauthenticated_user }
405
+
406
+ it "should always respond with 530 when called by user not logged in" do
407
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
408
+ subject.receive_line("PASV")
409
+ end
410
+ end
411
+ context "with an authenticated user" do
412
+ subject { handler_with_authenticated_user }
413
+
414
+ it "should always respond with 227 after opening the new socket"
415
+ end
416
+ end
417
+
418
+ describe Raval::Handler, "EPSV" do
419
+ context "with an unauthenticated user" do
420
+ subject { handler_with_unauthenticated_user }
421
+
422
+ it "should always respond with 530 when called by user not logged in" do
423
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
424
+ subject.receive_line("EPSV")
425
+ end
426
+ end
427
+ context "with an authenticated user" do
428
+ subject { handler_with_authenticated_user }
429
+
430
+ it "should always respond with 229 after opening the new socket"
431
+ end
432
+ end
433
+
434
+ describe Raval::Handler, "PORT" do
435
+ context "with an unauthenticated user" do
436
+ subject { handler_with_unauthenticated_user }
437
+
438
+ it "should always respond with 530 when called by user not logged in" do
439
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
440
+ subject.receive_line("PORT 127,0,0,1,128,12")
441
+ end
442
+ end
443
+ context "with an authenticated user" do
444
+ subject { handler_with_authenticated_user }
445
+
446
+ it "should always respond with 200 after opening the new socket"
447
+ end
448
+ end
449
+
450
+ describe Raval::Handler, "EPRT" do
451
+ context "with an unauthenticated user" do
452
+ subject { handler_with_unauthenticated_user }
453
+
454
+ it "should always respond with 530 when called by user not logged in" do
455
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
456
+ subject.receive_line("EPRT -1-127.0.0.1-32000")
457
+ end
458
+ end
459
+ context "with an authenticated user" do
460
+ subject { handler_with_authenticated_user }
461
+
462
+ it "should always respond with 200 after opening the new socket"
463
+ end
464
+ end
465
+
466
+ %w(PWD XPWD).each do |command|
467
+ describe Raval::Handler, command do
468
+ context "with an unauthenticated user" do
469
+ subject { handler_with_unauthenticated_user }
470
+
471
+ it "should always respond with 550 (permission denied)" do
472
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
473
+ subject.receive_line(command)
474
+ end
475
+ end
476
+
477
+ context "with an authenticated user" do
478
+ subject { handler_with_authenticated_user }
479
+ it 'should always respond with 257 "/" when called from root dir' do
480
+ subject.connection.should_receive(:send_response).with(257, '"/" is the current directory')
481
+ subject.receive_line(command)
482
+ end
483
+
484
+ it 'should always respond with 257 "/files" when called from files dir' do
485
+ connection = subject.connection
486
+ connection.should_receive(:send_response).with(250, anything).and_return(250)
487
+ connection.should_receive(:send_response).with(257, '"/files" is the current directory')
488
+ subject.receive_line("CWD files")
489
+ subject.receive_line(command)
490
+ end
491
+ end
492
+ end
493
+ end
494
+
495
+ describe Raval::Handler, "RETR" do
496
+ context "with an unauthenticated user" do
497
+ subject { handler_with_unauthenticated_user }
498
+
499
+ it "should always respond with 530 when called by user not logged in" do
500
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
501
+ subject.receive_line("RETR blah.txt")
502
+ end
503
+ end
504
+ context "with an authenticated user" do
505
+ subject { handler_with_authenticated_user }
506
+
507
+ it "should respond with 553 when called with no param" do
508
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
509
+ subject.receive_line("RETR")
510
+ end
511
+
512
+ it "should always respond with 551 when called with an invalid file" do
513
+ pending
514
+ subject.receive_line("PASV")
515
+ subject.reset_sent!
516
+ subject.receive_line("RETR blah.txt")
517
+ subject.sent_data.should match(/551.+/)
518
+ end
519
+
520
+ it "should always respond with 150..226 when called with valid file" do
521
+ pending
522
+ subject.receive_line("PASV")
523
+ subject.reset_sent!
524
+ subject.receive_line("RETR one.txt")
525
+ subject.sent_data.should match(/150.+226.+/m)
526
+ end
527
+
528
+ it "should always respond with 150..226 when called outside files dir with appropriate param" do
529
+ pending
530
+ subject.receive_line("PASV")
531
+ subject.receive_line("RETR files/two.txt")
532
+ subject.sent_data.should match(/150.+226.+/m)
533
+ end
534
+ end
535
+ end
536
+
537
+ describe Raval::Handler, "REST" do
538
+ context "with an authenticated user" do
539
+ subject { handler_with_authenticated_user }
540
+
541
+ it "should always respond with 500" do
542
+ subject.connection.should_receive(:send_response).with(500, anything).and_return(500)
543
+ subject.receive_line("REST")
544
+ end
545
+ end
546
+ end
547
+
548
+ %w(RMD XRMD).each do |command|
549
+ describe Raval::Handler, command do
550
+ context "with an unauthenticated user" do
551
+ subject { handler_with_unauthenticated_user }
552
+
553
+ it "should respond with 530 if user is not logged in" do
554
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
555
+ subject.receive_line("#{command} x")
556
+ end
557
+ end
558
+ context "with an authenticated user" do
559
+ subject { handler_with_authenticated_user }
560
+
561
+ it "should respond with 553 when the paramater is omitted" do
562
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
563
+ subject.receive_line("#{command}")
564
+ end
565
+
566
+ it "should respond with 250 when the directory is deleted" do
567
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
568
+ subject.receive_line("#{command} four")
569
+ end
570
+
571
+ it "should respond with 550 when the directory is not deleted" do
572
+ subject.connection.should_receive(:send_response).with(550, anything).and_return(550)
573
+ subject.receive_line("#{command} x")
574
+ end
575
+ end
576
+ end
577
+ end
578
+
579
+ describe Raval::Handler, "RNFR" do
580
+ context "with an unauthenticated user" do
581
+ subject { handler_with_unauthenticated_user }
582
+
583
+ it "should respond with 530" do
584
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
585
+ subject.receive_line("RNFR x")
586
+ end
587
+ end
588
+ context "with an authenticated user" do
589
+ subject { handler_with_authenticated_user }
590
+
591
+ it "should respond with 553 when the paramater is omitted" do
592
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
593
+ subject.receive_line("RNFR")
594
+ end
595
+
596
+ it "should always respond with 350 when called" do
597
+ subject.connection.should_receive(:send_response).with(350, anything).and_return(350)
598
+ subject.receive_line("RNFR x")
599
+ end
600
+ end
601
+ end
602
+
603
+ describe Raval::Handler, "RNTO" do
604
+ context "with an unauthenticated user" do
605
+ subject { handler_with_unauthenticated_user }
606
+
607
+ it "should respond with 530" do
608
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
609
+ subject.receive_line("RNTO x")
610
+ end
611
+ end
612
+ context "with an authenticated user" do
613
+ subject { handler_with_authenticated_user }
614
+ it "should respond with 553 when the paramater is omitted" do
615
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
616
+ subject.receive_line("RNTO")
617
+ end
618
+
619
+ it "should respond with XXX when the RNFR command is omitted"
620
+
621
+ it "should respond with 250 when the file is renamed" do
622
+ subject.connection.should_receive(:send_response).with(350, anything).and_return(350)
623
+ subject.connection.should_receive(:send_response).with(250, anything).and_return(250)
624
+ subject.receive_line("RNFR one.txt")
625
+ subject.receive_line("RNTO two.txt")
626
+ end
627
+
628
+ it "should respond with 550 when the file is not renamed" do
629
+ subject.connection.should_receive(:send_response).with(350, anything).and_return(350)
630
+ subject.connection.should_receive(:send_response).with(550, anything).and_return(550)
631
+ subject.receive_line("RNFR two.txt")
632
+ subject.receive_line("RNTO one.txt")
633
+ end
634
+ end
635
+ end
636
+
637
+ describe Raval::Handler, "QUIT" do
638
+ context "with an unauthenticated user" do
639
+ subject { handler_with_unauthenticated_user }
640
+
641
+ it "should always respond with 221 when called" do
642
+ subject.connection.should_receive(:close)
643
+ subject.connection.should_receive(:send_response).with(221, anything).and_return(221)
644
+ subject.receive_line("QUIT")
645
+ end
646
+ end
647
+ end
648
+
649
+ describe Raval::Handler, "SIZE" do
650
+
651
+ context "with an unauthenticated user" do
652
+ subject { handler_with_unauthenticated_user }
653
+
654
+ it "should always respond with 530" do
655
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
656
+ subject.receive_line("SIZE one.txt")
657
+ end
658
+ end
659
+
660
+ context "with an authenticated user" do
661
+ subject { handler_with_authenticated_user }
662
+ it "should always respond with 553 when called with no param" do
663
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
664
+ subject.receive_line("SIZE")
665
+ end
666
+
667
+ it "should always respond with 450 when called with a directory param" do
668
+ subject.connection.should_receive(:send_response).with(450, anything).and_return(450)
669
+ subject.receive_line("SIZE files")
670
+ end
671
+
672
+ it "should always respond with 450 when called with a non-file param" do
673
+ subject.connection.should_receive(:send_response).with(450, anything).and_return(450)
674
+ subject.receive_line("SIZE blah")
675
+ end
676
+
677
+ it "should always respond with 213 when called with a valid file param" do
678
+ subject.connection.should_receive(:send_response).with(213, 56).and_return(213)
679
+ subject.receive_line("SIZE one.txt")
680
+ end
681
+
682
+ it "should always respond with 213 when called with a valid file param" do
683
+ subject.connection.should_receive(:send_response).with(213, 40).and_return(213)
684
+ subject.receive_line("SIZE files/two.txt")
685
+ end
686
+ end
687
+ end
688
+
689
+ describe Raval::Handler, "MDTM" do
690
+
691
+ context "with an unauthenticated user" do
692
+ subject { handler_with_unauthenticated_user }
693
+
694
+ it "should always respond with 530" do
695
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
696
+ subject.receive_line("MDTM one.txt")
697
+ end
698
+ end
699
+
700
+ context "with an authenticated user" do
701
+ subject { handler_with_authenticated_user }
702
+ it "should always respond with 553 when called with no param" do
703
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
704
+ subject.receive_line("MDTM")
705
+ end
706
+
707
+ it "should always respond with 450 when called with a directory param" do
708
+ subject.connection.should_receive(:send_response).with(450, anything).and_return(450)
709
+ subject.receive_line("MDTM files")
710
+ end
711
+
712
+ it "should always respond with 450 when called with a non-file param" do
713
+ subject.connection.should_receive(:send_response).with(450, anything).and_return(450)
714
+ subject.receive_line("MDTM blah")
715
+ end
716
+
717
+ it "should always respond with 213 when called with a valid file param" do
718
+ subject.connection.should_receive(:send_response).with(213, "20121010101000").and_return(213)
719
+ subject.receive_line("MDTM one.txt")
720
+ end
721
+
722
+ it "should always respond with 213 when called with a valid file param" do
723
+ subject.connection.should_receive(:send_response).with(213, "20121111111100").and_return(213)
724
+ subject.receive_line("MDTM files/two.txt")
725
+ end
726
+ end
727
+ end
728
+
729
+ describe Raval::Handler, "STOR" do
730
+ context "with an unauthenticated user" do
731
+ subject { handler_with_unauthenticated_user }
732
+
733
+ it "should always respond with 530 when called by user not logged in" do
734
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
735
+ subject.receive_line("STOR blah.txt")
736
+ end
737
+ end
738
+ context "with an authenticated user" do
739
+ subject { handler_with_authenticated_user }
740
+
741
+ it "should respond with 553 when called with no param" do
742
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
743
+ subject.receive_line("STOR")
744
+ end
745
+
746
+ it "should respond with 150...206 after a successful transfer"
747
+ end
748
+ end
749
+
750
+ describe Raval::Handler, "STRU" do
751
+ context "with an unauthenticated user" do
752
+ subject { handler_with_unauthenticated_user }
753
+
754
+ it "should always respond with 530" do
755
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
756
+ subject.receive_line("STRU F")
757
+ end
758
+ end
759
+
760
+ context "with an authenticated user" do
761
+ subject { handler_with_authenticated_user }
762
+ it "should respond with 553 when called with no param" do
763
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
764
+ subject.receive_line("STRU")
765
+ end
766
+
767
+ it "should always respond with 200 when called with F param" do
768
+ subject.connection.should_receive(:send_response).with(200, anything).and_return(200)
769
+ subject.receive_line("STRU F")
770
+ end
771
+
772
+ it "should always respond with 504 when called with non-F param" do
773
+ subject.connection.should_receive(:send_response).with(504, anything).and_return(504)
774
+ subject.receive_line("STRU S")
775
+ end
776
+ end
777
+ end
778
+
779
+ describe Raval::Handler, "SYST" do
780
+ context "with an unauthenticated user" do
781
+ subject { handler_with_unauthenticated_user }
782
+
783
+ it "should always respond with 530" do
784
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
785
+ subject.receive_line("SYST")
786
+ end
787
+ end
788
+
789
+ context "with an authenticated user" do
790
+ subject { handler_with_authenticated_user }
791
+
792
+ it "should respond with 215 when called by a logged in user" do
793
+ subject.connection.should_receive(:send_response).with(215, "UNIX Type: L8").and_return(215)
794
+ subject.receive_line("SYST")
795
+ end
796
+ end
797
+ end
798
+
799
+ describe Raval::Handler, "TYPE" do
800
+ context "with an unauthenticated user" do
801
+ subject { handler_with_unauthenticated_user }
802
+
803
+ it "should always respond with 530" do
804
+ subject.connection.should_receive(:send_response).with(530, anything).and_return(530)
805
+ subject.receive_line("TYPE A")
806
+ end
807
+ end
808
+
809
+ context "with an authenticated user" do
810
+ subject { handler_with_authenticated_user }
811
+
812
+ it "should respond with 553 when called with no param" do
813
+ subject.connection.should_receive(:send_response).with(553, anything).and_return(553)
814
+ subject.receive_line("TYPE")
815
+ end
816
+
817
+ it "should respond with 200 when called with 'A' by a logged in user" do
818
+ subject.connection.should_receive(:send_response).with(200, /ASCII/).and_return(200)
819
+ subject.receive_line("TYPE A")
820
+ end
821
+
822
+ it "should respond with 200 when called with 'I' by a logged in user" do
823
+ subject.connection.should_receive(:send_response).with(200, /binary/).and_return(200)
824
+ subject.receive_line("TYPE I")
825
+ end
826
+
827
+ it "should respond with 500 when called by a logged in user with un unrecognised param" do
828
+ subject.connection.should_receive(:send_response).with(500, anything).and_return(500)
829
+ subject.receive_line("TYPE T")
830
+ end
831
+ end
832
+ end