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.
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +202 -0
- data/examples/fake.rb +110 -0
- data/examples/fog.rb +183 -0
- data/lib/raval.rb +8 -0
- data/lib/raval/active_socket.rb +49 -0
- data/lib/raval/app.rb +137 -0
- data/lib/raval/connection.rb +50 -0
- data/lib/raval/handler.rb +549 -0
- data/lib/raval/list_formatter.rb +48 -0
- data/lib/raval/passive_socket.rb +86 -0
- data/lib/raval/server.rb +45 -0
- data/spec/handler_spec.rb +832 -0
- data/spec/list_formatter_spec.rb +71 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/test_driver.rb +88 -0
- metadata +165 -0
@@ -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
|
data/lib/raval/server.rb
ADDED
@@ -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
|