fake_ftp 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +16 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.rubocop.yml +27 -0
- data/.rubocop_todo.yml +7 -0
- data/.simplecov +6 -0
- data/.travis.yml +27 -2
- data/CHANGELOG.md +136 -0
- data/Gemfile +9 -7
- data/Guardfile +5 -4
- data/LICENSE.md +20 -0
- data/README.md +30 -63
- data/Rakefile +12 -7
- data/fake_ftp.gemspec +16 -17
- data/lib/fake_ftp.rb +3 -2
- data/lib/fake_ftp/file.rb +11 -5
- data/lib/fake_ftp/server.rb +138 -261
- data/lib/fake_ftp/server_commands.rb +6 -0
- data/lib/fake_ftp/server_commands/acct.rb +11 -0
- data/lib/fake_ftp/server_commands/cdup.rb +11 -0
- data/lib/fake_ftp/server_commands/cwd.rb +13 -0
- data/lib/fake_ftp/server_commands/dele.rb +25 -0
- data/lib/fake_ftp/server_commands/list.rb +39 -0
- data/lib/fake_ftp/server_commands/mdtm.rb +17 -0
- data/lib/fake_ftp/server_commands/mkd.rb +11 -0
- data/lib/fake_ftp/server_commands/nlst.rb +32 -0
- data/lib/fake_ftp/server_commands/pass.rb +11 -0
- data/lib/fake_ftp/server_commands/pasv.rb +15 -0
- data/lib/fake_ftp/server_commands/port.rb +23 -0
- data/lib/fake_ftp/server_commands/pwd.rb +11 -0
- data/lib/fake_ftp/server_commands/quit.rb +13 -0
- data/lib/fake_ftp/server_commands/retr.rb +32 -0
- data/lib/fake_ftp/server_commands/rnfr.rb +18 -0
- data/lib/fake_ftp/server_commands/rnto.rb +22 -0
- data/lib/fake_ftp/server_commands/site.rb +11 -0
- data/lib/fake_ftp/server_commands/size.rb +11 -0
- data/lib/fake_ftp/server_commands/stor.rb +30 -0
- data/lib/fake_ftp/server_commands/type.rb +18 -0
- data/lib/fake_ftp/server_commands/user.rb +12 -0
- data/lib/fake_ftp/server_commands/wat.rb +33 -0
- data/lib/fake_ftp/version.rb +3 -1
- data/spec/functional/server_spec.rb +459 -358
- data/spec/integration/server_spec.rb +39 -29
- data/spec/models/fake_ftp/file_spec.rb +22 -21
- data/spec/models/fake_ftp/server_spec.rb +33 -32
- data/spec/spec_helper.rb +98 -14
- metadata +32 -3
data/lib/fake_ftp/version.rb
CHANGED
@@ -1,106 +1,128 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe FakeFtp::Server, 'commands', functional: true do
|
4
|
+
let(:absolute?) { true }
|
5
|
+
let(:data_port) { rand(16_000..19_000) }
|
6
|
+
let(:data_addr_bits) { SpecHelper.local_addr_bits(data_port) }
|
7
|
+
let(:client_port) { rand(19_000..22_000) }
|
8
|
+
let(:client_addr_bits) { SpecHelper.local_addr_bits(client_port) }
|
9
|
+
let(:data_server_port) { rand(22_000..24_000) }
|
10
|
+
|
11
|
+
let(:client) do
|
12
|
+
TCPSocket.open('127.0.0.1', client_port).tap { |s| s.sync = true }
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:server) do
|
16
|
+
FakeFtp::Server.new(
|
17
|
+
client_port, data_port,
|
18
|
+
debug: ENV['DEBUG'] == '1',
|
19
|
+
absolute: absolute?
|
20
|
+
)
|
21
|
+
end
|
2
22
|
|
3
|
-
|
4
|
-
|
5
|
-
|
23
|
+
let(:data_server) do
|
24
|
+
SpecHelper::FakeDataServer.new(data_server_port)
|
25
|
+
end
|
6
26
|
|
7
27
|
before { server.start }
|
8
28
|
|
9
|
-
after
|
29
|
+
after do
|
10
30
|
client.close
|
11
31
|
server.stop
|
12
|
-
|
32
|
+
end
|
13
33
|
|
14
34
|
context 'general' do
|
15
|
-
it
|
16
|
-
expect(
|
35
|
+
it 'should accept connections' do
|
36
|
+
expect(SpecHelper.gets_with_timeout(client))
|
37
|
+
.to eql("220 Can has FTP?\r\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should get unknown command response when nothing is sent' do
|
41
|
+
SpecHelper.gets_with_timeout(client)
|
42
|
+
client.write("\r\n")
|
43
|
+
expect(SpecHelper.gets_with_timeout(client))
|
44
|
+
.to match(/^500 Unknown command "[^"]*"\r\n/)
|
17
45
|
end
|
18
46
|
|
19
|
-
it
|
20
|
-
client
|
21
|
-
client.
|
22
|
-
expect(client
|
47
|
+
it 'accepts QUIT' do
|
48
|
+
SpecHelper.gets_with_timeout(client)
|
49
|
+
client.write("QUIT\r\n")
|
50
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("221 OMG bye!\r\n")
|
23
51
|
end
|
24
52
|
|
25
|
-
it
|
26
|
-
client
|
27
|
-
client.
|
28
|
-
|
53
|
+
it 'should accept multiple commands in one session' do
|
54
|
+
SpecHelper.gets_with_timeout(client)
|
55
|
+
client.write("USER thing\r\n")
|
56
|
+
SpecHelper.gets_with_timeout(client)
|
57
|
+
client.write("PASS thing\r\n")
|
58
|
+
SpecHelper.gets_with_timeout(client)
|
59
|
+
client.write("ACCT thing\r\n")
|
60
|
+
SpecHelper.gets_with_timeout(client)
|
61
|
+
client.write("USER thing\r\n")
|
29
62
|
end
|
30
63
|
|
31
|
-
it
|
32
|
-
client
|
33
|
-
client.
|
34
|
-
client.
|
35
|
-
client.puts "PASS thing"
|
36
|
-
client.gets
|
37
|
-
client.puts "ACCT thing"
|
38
|
-
client.gets
|
39
|
-
client.puts "USER thing"
|
64
|
+
it 'should accept SITE command' do
|
65
|
+
SpecHelper.gets_with_timeout(client)
|
66
|
+
client.write("SITE umask\r\n")
|
67
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql "200 umask\r\n"
|
40
68
|
end
|
41
69
|
end
|
42
70
|
|
43
71
|
context 'passive' do
|
44
|
-
it
|
72
|
+
it 'accepts PASV' do
|
45
73
|
expect(server.mode).to eql(:active)
|
46
|
-
client
|
47
|
-
client.
|
48
|
-
expect(
|
74
|
+
SpecHelper.gets_with_timeout(client)
|
75
|
+
client.write("PASV\r\n")
|
76
|
+
expect(SpecHelper.gets_with_timeout(client))
|
77
|
+
.to eql("227 Entering Passive Mode (#{data_addr_bits})\r\n")
|
49
78
|
expect(server.mode).to eql(:passive)
|
50
79
|
end
|
51
80
|
|
52
|
-
it
|
81
|
+
it 'responds with correct PASV port' do
|
53
82
|
server.stop
|
54
|
-
server.passive_port =
|
83
|
+
server.passive_port = 21_111
|
55
84
|
server.start
|
56
|
-
client
|
57
|
-
client.
|
58
|
-
|
85
|
+
SpecHelper.gets_with_timeout(client)
|
86
|
+
client.write("PASV\r\n")
|
87
|
+
addr_bits = SpecHelper.local_addr_bits(21_111)
|
88
|
+
expect(SpecHelper.gets_with_timeout(client))
|
89
|
+
.to eql("227 Entering Passive Mode (#{addr_bits})\r\n")
|
59
90
|
end
|
60
91
|
|
61
|
-
it
|
92
|
+
it 'does not accept PASV if no port set' do
|
62
93
|
server.stop
|
63
94
|
server.passive_port = nil
|
64
95
|
server.start
|
65
|
-
client
|
66
|
-
client.
|
67
|
-
expect(
|
96
|
+
SpecHelper.gets_with_timeout(client)
|
97
|
+
client.write("PASV\r\n")
|
98
|
+
expect(SpecHelper.gets_with_timeout(client))
|
99
|
+
.to eql("502 Aww hell no, use Active\r\n")
|
68
100
|
end
|
69
101
|
end
|
70
102
|
|
71
103
|
context 'active' do
|
72
|
-
let!(:data_server) { ::TCPServer.new('127.0.0.1', 21216) }
|
73
|
-
|
74
104
|
before :each do
|
75
|
-
client
|
76
|
-
|
77
|
-
@data_connection = Thread.new do
|
78
|
-
@server_client = data_server.accept
|
79
|
-
expect(@server_client).to_not be_nil
|
80
|
-
end
|
105
|
+
SpecHelper.gets_with_timeout(client)
|
106
|
+
data_server.start
|
81
107
|
end
|
82
108
|
|
83
109
|
after :each do
|
84
|
-
data_server.
|
110
|
+
data_server.stop
|
85
111
|
end
|
86
112
|
|
87
|
-
it
|
88
|
-
client.
|
89
|
-
expect(client
|
90
|
-
|
91
|
-
@data_connection.join
|
113
|
+
it 'accepts PORT and connects to port' do
|
114
|
+
client.write("PORT #{data_server.addr_bits}\r\n")
|
115
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("200 Okay\r\n")
|
92
116
|
end
|
93
117
|
|
94
|
-
it
|
118
|
+
it 'should switch to :active on port command' do
|
95
119
|
expect(server.mode).to eql(:active)
|
96
|
-
client.
|
97
|
-
client
|
120
|
+
client.write("PASV\r\n")
|
121
|
+
SpecHelper.gets_with_timeout(client)
|
98
122
|
expect(server.mode).to eql(:passive)
|
99
123
|
|
100
|
-
client.
|
101
|
-
expect(client
|
102
|
-
|
103
|
-
@data_connection.join
|
124
|
+
client.write("PORT #{data_server.addr_bits}\r\n")
|
125
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("200 Okay\r\n")
|
104
126
|
|
105
127
|
expect(server.mode).to eql(:active)
|
106
128
|
end
|
@@ -108,377 +130,456 @@ describe FakeFtp::Server, 'commands' do
|
|
108
130
|
|
109
131
|
context 'authentication commands' do
|
110
132
|
before :each do
|
111
|
-
client
|
133
|
+
SpecHelper.gets_with_timeout(client)
|
112
134
|
end
|
113
135
|
|
114
|
-
it
|
115
|
-
client.
|
116
|
-
expect(
|
136
|
+
it 'accepts USER' do
|
137
|
+
client.write("USER some_dude\r\n")
|
138
|
+
expect(SpecHelper.gets_with_timeout(client))
|
139
|
+
.to eql("331 send your password\r\n")
|
117
140
|
end
|
118
141
|
|
119
|
-
it
|
120
|
-
client.
|
121
|
-
expect(client
|
142
|
+
it 'accepts anonymous USER' do
|
143
|
+
client.write("USER anonymous\r\n")
|
144
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("230 logged in\r\n")
|
122
145
|
end
|
123
146
|
|
124
|
-
it
|
125
|
-
client.
|
126
|
-
expect(client
|
147
|
+
it 'accepts PASS' do
|
148
|
+
client.write("PASS password\r\n")
|
149
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("230 logged in\r\n")
|
127
150
|
end
|
128
151
|
|
129
|
-
it
|
130
|
-
client.
|
131
|
-
expect(client
|
152
|
+
it 'accepts ACCT' do
|
153
|
+
client.write("ACCT\r\n")
|
154
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("230 WHATEVER!\r\n")
|
132
155
|
end
|
133
156
|
end
|
134
157
|
|
135
|
-
|
136
|
-
|
137
|
-
client.gets ## connection successful response
|
138
|
-
end
|
139
|
-
|
140
|
-
it "returns directory on PWD" do
|
141
|
-
client.puts "PWD"
|
142
|
-
expect(client.gets).to eql("257 \"/pub\" is current directory\r\n")
|
143
|
-
end
|
144
|
-
|
145
|
-
it "says OK to any CWD, CDUP, without doing anything" do
|
146
|
-
client.puts "CWD somewhere/else"
|
147
|
-
expect(client.gets).to eql("250 OK!\r\n")
|
148
|
-
client.puts "CDUP"
|
149
|
-
expect(client.gets).to eql("250 OK!\r\n")
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
context 'file commands' do
|
154
|
-
before :each do
|
155
|
-
client.gets ## connection successful response
|
156
|
-
end
|
157
|
-
|
158
|
-
it "accepts TYPE ascii" do
|
159
|
-
client.puts "TYPE A"
|
160
|
-
expect(client.gets).to eql("200 Type set to A.\r\n")
|
161
|
-
end
|
162
|
-
|
163
|
-
it "accepts TYPE image" do
|
164
|
-
client.puts "TYPE I"
|
165
|
-
expect(client.gets).to eql("200 Type set to I.\r\n")
|
166
|
-
end
|
167
|
-
|
168
|
-
it "does not accept TYPEs other than ascii or image" do
|
169
|
-
client.puts "TYPE E"
|
170
|
-
expect(client.gets).to eql("504 We don't allow those\r\n")
|
171
|
-
client.puts "TYPE N"
|
172
|
-
expect(client.gets).to eql("504 We don't allow those\r\n")
|
173
|
-
client.puts "TYPE T"
|
174
|
-
expect(client.gets).to eql("504 We don't allow those\r\n")
|
175
|
-
client.puts "TYPE C"
|
176
|
-
expect(client.gets).to eql("504 We don't allow those\r\n")
|
177
|
-
client.puts "TYPE L"
|
178
|
-
expect(client.gets).to eql("504 We don't allow those\r\n")
|
179
|
-
end
|
180
|
-
|
181
|
-
context 'passive' do
|
182
|
-
let(:data_client) { TCPSocket.open('127.0.0.1', 21213) }
|
158
|
+
[true, false].each do |absolute|
|
159
|
+
let(:absolute?) { absolute }
|
183
160
|
|
161
|
+
context "directory commands with absolute=#{absolute}" do
|
184
162
|
before :each do
|
185
|
-
client
|
186
|
-
expect(client.gets).to eql("227 Entering Passive Mode (127,0,0,1,82,221)\r\n")
|
163
|
+
SpecHelper.gets_with_timeout(client)
|
187
164
|
end
|
188
165
|
|
189
|
-
it
|
190
|
-
client.
|
191
|
-
expect(
|
192
|
-
|
193
|
-
data_client.close
|
194
|
-
expect(client.gets).to eql("226 Did it!\r\n")
|
195
|
-
expect(server.files).to include('some_file')
|
196
|
-
expect(server.file('some_file').bytes).to eql(10)
|
197
|
-
expect(server.file('some_file').data).to eql("1234567890")
|
166
|
+
it 'returns directory on PWD' do
|
167
|
+
client.write("PWD\r\n")
|
168
|
+
expect(SpecHelper.gets_with_timeout(client))
|
169
|
+
.to eql("257 \"/pub\" is current directory\r\n")
|
198
170
|
end
|
199
171
|
|
200
|
-
it
|
201
|
-
client.
|
202
|
-
client.
|
203
|
-
|
204
|
-
|
205
|
-
data_client.close
|
206
|
-
expect(client.gets).to eql("226 Did it!\r\n")
|
207
|
-
expect(server.files).to include('some_file')
|
208
|
-
expect(server.file('some_file').bytes).to eql(11)
|
209
|
-
expect(server.file('some_file').data).to eql("1234567890\n")
|
210
|
-
end
|
211
|
-
|
212
|
-
it "accepts STOR with filename and long file" do
|
213
|
-
client.puts "STOR some_file"
|
214
|
-
expect(client.gets).to eql("125 Do it!\r\n")
|
215
|
-
data_client.puts("1234567890" * 10_000)
|
216
|
-
data_client.close
|
217
|
-
expect(client.gets).to eql("226 Did it!\r\n")
|
218
|
-
expect(server.files).to include('some_file')
|
172
|
+
it 'says OK to any CWD, CDUP, without doing anything' do
|
173
|
+
client.write("CWD somewhere/else\r\n")
|
174
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("250 OK!\r\n")
|
175
|
+
client.write("CDUP\r\n")
|
176
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("250 OK!\r\n")
|
219
177
|
end
|
178
|
+
end
|
179
|
+
end
|
220
180
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
data_client.flush
|
226
|
-
data_client.write "1234567890"
|
227
|
-
data_client.flush
|
228
|
-
data_client.close
|
229
|
-
expect(client.gets).to eql("226 Did it!\r\n")
|
230
|
-
expect(server.file('some_file').data).to eql("12345678901234567890")
|
231
|
-
end
|
181
|
+
[true, false].each do |absolute|
|
182
|
+
context "file commands with absolute=#{absolute}" do
|
183
|
+
let(:absolute?) { absolute }
|
184
|
+
let(:file_prefix) { absolute ? '/pub/' : '' }
|
232
185
|
|
233
|
-
|
234
|
-
client
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
client
|
240
|
-
|
241
|
-
end
|
186
|
+
before :each do
|
187
|
+
SpecHelper.gets_with_timeout(client)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'accepts TYPE ascii' do
|
191
|
+
client.write("TYPE A\r\n")
|
192
|
+
expect(SpecHelper.gets_with_timeout(client))
|
193
|
+
.to eql("200 Type set to A.\r\n")
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'accepts TYPE image' do
|
197
|
+
client.write("TYPE I\r\n")
|
198
|
+
expect(SpecHelper.gets_with_timeout(client))
|
199
|
+
.to eql("200 Type set to I.\r\n")
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'does not accept TYPEs other than ascii or image' do
|
203
|
+
client.write("TYPE E\r\n")
|
204
|
+
expect(SpecHelper.gets_with_timeout(client))
|
205
|
+
.to eql("504 We don't allow those\r\n")
|
206
|
+
client.write("TYPE N\r\n")
|
207
|
+
expect(SpecHelper.gets_with_timeout(client))
|
208
|
+
.to eql("504 We don't allow those\r\n")
|
209
|
+
client.write("TYPE T\r\n")
|
210
|
+
expect(SpecHelper.gets_with_timeout(client))
|
211
|
+
.to eql("504 We don't allow those\r\n")
|
212
|
+
client.write("TYPE C\r\n")
|
213
|
+
expect(SpecHelper.gets_with_timeout(client))
|
214
|
+
.to eql("504 We don't allow those\r\n")
|
215
|
+
client.write("TYPE L\r\n")
|
216
|
+
expect(SpecHelper.gets_with_timeout(client))
|
217
|
+
.to eql("504 We don't allow those\r\n")
|
218
|
+
end
|
219
|
+
|
220
|
+
context 'passive' do
|
221
|
+
let(:data_client) do
|
222
|
+
TCPSocket.open('127.0.0.1', data_port).tap { |c| c.sync = true }
|
223
|
+
end
|
242
224
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
data_client.close
|
249
|
-
expect(data).to eql('1234567890')
|
250
|
-
expect(client.gets).to eql("226 File transferred\r\n")
|
251
|
-
end
|
225
|
+
before :each do
|
226
|
+
client.write("PASV\r\n")
|
227
|
+
expect(SpecHelper.gets_with_timeout(client))
|
228
|
+
.to eql("227 Entering Passive Mode (#{data_addr_bits})\r\n")
|
229
|
+
end
|
252
230
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
231
|
+
it 'accepts STOR with filename' do
|
232
|
+
client.write("STOR some_file\r\n")
|
233
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("125 Do it!\r\n")
|
234
|
+
data_client.write('1234567890')
|
235
|
+
data_client.close
|
236
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("226 Did it!\r\n")
|
237
|
+
expect(server.files).to include(file_prefix + 'some_file')
|
238
|
+
expect(server.file(file_prefix + 'some_file').bytes).to eql(10)
|
239
|
+
expect(server.file(file_prefix + 'some_file').data)
|
240
|
+
.to eql('1234567890')
|
241
|
+
end
|
259
242
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
243
|
+
it 'accepts STOR with filename and trailing newline' do
|
244
|
+
client.write("STOR some_file\r\n")
|
245
|
+
SpecHelper.gets_with_timeout(client)
|
246
|
+
data_client.write("1234567890\n")
|
247
|
+
data_client.close
|
248
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("226 Did it!\r\n")
|
249
|
+
expect(server.files).to include(file_prefix + 'some_file')
|
250
|
+
expect(server.file(file_prefix + 'some_file').bytes).to eql(11)
|
251
|
+
expect(server.file(file_prefix + 'some_file').data)
|
252
|
+
.to eql("1234567890\n")
|
253
|
+
end
|
264
254
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
"-rw-r--r--\t1\towner\tgroup\t10\t#{server.file('some_file').created.strftime('%b %d %H:%M')}\tsome_file\n",
|
274
|
-
"-rw-r--r--\t1\towner\tgroup\t10\t#{server.file('another_file').created.strftime('%b %d %H:%M')}\tanother_file\n",
|
275
|
-
].join)
|
276
|
-
expect(client.gets).to eql("226 List information transferred\r\n")
|
277
|
-
end
|
255
|
+
it 'accepts STOR with filename and long file' do
|
256
|
+
client.write("STOR some_file\r\n")
|
257
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("125 Do it!\r\n")
|
258
|
+
data_client.write('1234567890' * 10_000)
|
259
|
+
data_client.close
|
260
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("226 Did it!\r\n")
|
261
|
+
expect(server.files).to include(file_prefix + 'some_file')
|
262
|
+
end
|
278
263
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
264
|
+
it 'accepts STOR with streams' do
|
265
|
+
client.puts 'STOR some_file'
|
266
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("125 Do it!\r\n")
|
267
|
+
data_client.write '1234567890'
|
268
|
+
data_client.flush
|
269
|
+
data_client.write '1234567890'
|
270
|
+
data_client.flush
|
271
|
+
data_client.close
|
272
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("226 Did it!\r\n")
|
273
|
+
expect(server.file(file_prefix + 'some_file').data)
|
274
|
+
.to eql('12345678901234567890')
|
283
275
|
end
|
284
276
|
|
285
|
-
|
286
|
-
|
277
|
+
it 'does not accept RETR without a filename' do
|
278
|
+
client.write("RETR\r\n")
|
279
|
+
expect(SpecHelper.gets_with_timeout(client))
|
280
|
+
.to eql("501 No filename given\r\n")
|
281
|
+
end
|
287
282
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
end
|
293
|
-
expect(client.gets).to eql("226 List information transferred\r\n")
|
294
|
-
end
|
283
|
+
it 'does not serve files that do not exist' do
|
284
|
+
client.write("RETR some_file\r\n")
|
285
|
+
expect(SpecHelper.gets_with_timeout(client))
|
286
|
+
.to eql("550 File not found\r\n")
|
287
|
+
end
|
295
288
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
289
|
+
it 'accepts RETR with a filename' do
|
290
|
+
server.add_file(file_prefix + 'some_file', '1234567890')
|
291
|
+
client.write("RETR #{file_prefix}some_file\r\n")
|
292
|
+
expect(SpecHelper.gets_with_timeout(client))
|
293
|
+
.to eql("150 File status ok, about to open data connection\r\n")
|
294
|
+
data = SpecHelper.gets_with_timeout(data_client, endwith: "\0")
|
295
|
+
data_client.close
|
296
|
+
expect(data).to eql('1234567890')
|
297
|
+
expect(SpecHelper.gets_with_timeout(client))
|
298
|
+
.to eql("226 File transferred\r\n")
|
300
299
|
end
|
301
300
|
|
302
|
-
|
303
|
-
|
301
|
+
it 'accepts DELE with a filename' do
|
302
|
+
server.add_file('some_file', '1234567890')
|
303
|
+
client.write("DELE some_file\r\n")
|
304
|
+
expect(SpecHelper.gets_with_timeout(client))
|
305
|
+
.to eql("250 Delete operation successful.\r\n")
|
306
|
+
expect(server.files).to_not include('some_file')
|
307
|
+
end
|
304
308
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
end
|
309
|
+
it 'gives error message when trying to delete a file ' \
|
310
|
+
'that does not exist' do
|
311
|
+
client.write("DELE non_existing_file\r\n")
|
312
|
+
expect(SpecHelper.gets_with_timeout(client))
|
313
|
+
.to eql("550 Delete operation failed.\r\n")
|
314
|
+
end
|
312
315
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
316
|
+
it 'accepts a LIST command' do
|
317
|
+
server.add_file(file_prefix + 'some_file', '1234567890')
|
318
|
+
server.add_file(file_prefix + 'another_file', '1234567890')
|
319
|
+
client.puts("LIST\r\n")
|
320
|
+
expect(SpecHelper.gets_with_timeout(client))
|
321
|
+
.to eql("150 Listing status ok, about to open data connection\r\n")
|
322
|
+
data = SpecHelper.gets_with_timeout(data_client, endwith: "\0")
|
323
|
+
data_client.close
|
324
|
+
expect(data).to eql([
|
325
|
+
SpecHelper.statline(server.file(file_prefix + 'some_file')),
|
326
|
+
SpecHelper.statline(server.file(file_prefix + 'another_file'))
|
327
|
+
].join("\n"))
|
328
|
+
expect(SpecHelper.gets_with_timeout(client))
|
329
|
+
.to eql("226 List information transferred\r\n")
|
330
|
+
end
|
323
331
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
332
|
+
it 'accepts a LIST command with a wildcard argument' do
|
333
|
+
infiles = %w[test.jpg test-2.jpg test.txt].map do |f|
|
334
|
+
"#{file_prefix}#{f}"
|
335
|
+
end
|
336
|
+
infiles.each do |f|
|
337
|
+
server.add_file(f, '1234567890')
|
338
|
+
end
|
339
|
+
|
340
|
+
client.write("LIST *.jpg\r\n")
|
341
|
+
expect(SpecHelper.gets_with_timeout(client))
|
342
|
+
.to eql("150 Listing status ok, about to open data connection\r\n")
|
343
|
+
|
344
|
+
data = SpecHelper.gets_with_timeout(data_client, endwith: "\0")
|
345
|
+
data_client.close
|
346
|
+
expect(data).to eql(
|
347
|
+
infiles[0, 2].map do |f|
|
348
|
+
SpecHelper.statline(server.file(f))
|
349
|
+
end.join("\n")
|
350
|
+
)
|
351
|
+
expect(SpecHelper.gets_with_timeout(client))
|
352
|
+
.to eql("226 List information transferred\r\n")
|
328
353
|
end
|
329
354
|
|
330
|
-
|
355
|
+
it 'accepts a LIST command with multiple wildcard arguments' do
|
356
|
+
infiles = %w[test.jpg test.gif test.txt].map do |f|
|
357
|
+
"#{file_prefix}#{f}"
|
358
|
+
end
|
359
|
+
infiles.each do |file|
|
360
|
+
server.add_file(file, '1234567890')
|
361
|
+
end
|
362
|
+
|
363
|
+
client.write("LIST *.jpg *.gif\r\n")
|
364
|
+
expect(SpecHelper.gets_with_timeout(client))
|
365
|
+
.to eql("150 Listing status ok, about to open data connection\r\n")
|
366
|
+
|
367
|
+
data = SpecHelper.gets_with_timeout(data_client, endwith: "\0")
|
368
|
+
data_client.close
|
369
|
+
expect(data).to eql(
|
370
|
+
infiles[0, 2].map do |f|
|
371
|
+
SpecHelper.statline(server.file(f))
|
372
|
+
end.join("\n")
|
373
|
+
)
|
374
|
+
expect(SpecHelper.gets_with_timeout(client))
|
375
|
+
.to eql("226 List information transferred\r\n")
|
376
|
+
end
|
331
377
|
|
332
|
-
|
333
|
-
|
334
|
-
|
378
|
+
it 'accepts an NLST command' do
|
379
|
+
server.add_file('some_file', '1234567890')
|
380
|
+
server.add_file('another_file', '1234567890')
|
381
|
+
client.write("NLST\r\n")
|
382
|
+
expect(SpecHelper.gets_with_timeout(client))
|
383
|
+
.to eql("150 Listing status ok, about to open data connection\r\n")
|
384
|
+
data = SpecHelper.gets_with_timeout(data_client, endwith: "\0")
|
385
|
+
data_client.close
|
386
|
+
expect(data).to eql("some_file\nanother_file\n")
|
387
|
+
expect(SpecHelper.gets_with_timeout(client))
|
388
|
+
.to eql("226 List information transferred\r\n")
|
389
|
+
end
|
335
390
|
|
336
|
-
|
337
|
-
|
338
|
-
|
391
|
+
it 'accepts an NLST command with wildcard arguments' do
|
392
|
+
files = ['test.jpg', 'test.txt', 'test2.jpg']
|
393
|
+
files.each do |file|
|
394
|
+
server.add_file(file, '1234567890')
|
395
|
+
end
|
339
396
|
|
340
|
-
|
341
|
-
filename = "file.txt"
|
342
|
-
now = Time.now
|
343
|
-
server.add_file(filename, "some dummy content", now)
|
344
|
-
client.puts "MDTM #{filename}"
|
345
|
-
expect(client.gets).to eql("213 #{now.strftime("%Y%m%d%H%M%S")}\r\n")
|
346
|
-
end
|
347
|
-
end
|
397
|
+
client.write("NLST *.jpg\r\n")
|
348
398
|
|
349
|
-
|
350
|
-
|
399
|
+
expect(SpecHelper.gets_with_timeout(client))
|
400
|
+
.to eql("150 Listing status ok, about to open data connection\r\n")
|
401
|
+
data = SpecHelper.gets_with_timeout(data_client, endwith: "\0")
|
402
|
+
data_client.close
|
351
403
|
|
352
|
-
|
353
|
-
|
354
|
-
|
404
|
+
expect(data).to eql("test.jpg\ntest2.jpg\n")
|
405
|
+
expect(SpecHelper.gets_with_timeout(client))
|
406
|
+
.to eql("226 List information transferred\r\n")
|
355
407
|
end
|
356
|
-
end
|
357
408
|
|
358
|
-
|
359
|
-
|
360
|
-
|
409
|
+
it 'should allow mdtm' do
|
410
|
+
filename = file_prefix + 'file.txt'
|
411
|
+
now = Time.now
|
412
|
+
server.add_file(filename, 'some dummy content', now)
|
413
|
+
client.write("MDTM #{filename}\r\n")
|
414
|
+
expect(SpecHelper.gets_with_timeout(client))
|
415
|
+
.to eql("213 #{now.strftime('%Y%m%d%H%M%S')}\r\n")
|
416
|
+
end
|
361
417
|
end
|
362
418
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
419
|
+
context 'active' do
|
420
|
+
before :each do
|
421
|
+
data_server.start
|
422
|
+
end
|
367
423
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
client.puts "PWD"
|
372
|
-
expect(client.gets).to eql("257 \"/somewhere/else\" is current directory\r\n")
|
373
|
-
end
|
424
|
+
after :each do
|
425
|
+
data_server.stop
|
426
|
+
end
|
374
427
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
expect(client.gets).to eql("257 \"/somewhere/else\" is current directory\r\n")
|
380
|
-
end
|
428
|
+
it 'creates a directory on MKD' do
|
429
|
+
client.write("MKD some_dir\r\n")
|
430
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("257 OK!\r\n")
|
431
|
+
end
|
381
432
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
433
|
+
it 'should save the directory after you CWD' do
|
434
|
+
client.write("CWD /somewhere/else\r\n")
|
435
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("250 OK!\r\n")
|
436
|
+
client.write("PWD\r\n")
|
437
|
+
expect(SpecHelper.gets_with_timeout(client))
|
438
|
+
.to eql("257 \"/somewhere/else\" is current directory\r\n")
|
439
|
+
end
|
388
440
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
441
|
+
it 'CWD should add a / to the beginning of the directory' do
|
442
|
+
client.write("CWD somewhere/else\r\n")
|
443
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("250 OK!\r\n")
|
444
|
+
client.write("PWD\r\n")
|
445
|
+
expect(SpecHelper.gets_with_timeout(client))
|
446
|
+
.to eql("257 \"/somewhere/else\" is current directory\r\n")
|
447
|
+
end
|
393
448
|
|
394
|
-
|
395
|
-
|
396
|
-
|
449
|
+
it 'should not change the directory on CDUP' do
|
450
|
+
client.write("CDUP\r\n")
|
451
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("250 OK!\r\n")
|
452
|
+
client.write("PWD\r\n")
|
453
|
+
expect(SpecHelper.gets_with_timeout(client))
|
454
|
+
.to eql("257 \"/pub\" is current directory\r\n")
|
455
|
+
end
|
397
456
|
|
398
|
-
|
399
|
-
|
457
|
+
it 'sends error message if no PORT received' do
|
458
|
+
client.write("STOR some_file\r\n")
|
459
|
+
expect(SpecHelper.gets_with_timeout(client))
|
460
|
+
.to eql("425 Ain't no data port!\r\n")
|
461
|
+
end
|
400
462
|
|
401
|
-
|
402
|
-
|
403
|
-
|
463
|
+
it 'accepts STOR with filename' do
|
464
|
+
client.write("PORT #{data_server.addr_bits}\r\n")
|
465
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("200 Okay\r\n")
|
404
466
|
|
405
|
-
|
406
|
-
|
407
|
-
expect(server.file('some_other_file').bytes).to eql(5)
|
408
|
-
end
|
467
|
+
client.write("STOR some_other_file\r\n")
|
468
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("125 Do it!\r\n")
|
409
469
|
|
410
|
-
|
411
|
-
|
412
|
-
expect(client.gets).to eql("200 Okay\r\n")
|
470
|
+
data_server.handler_sock.print('12345')
|
471
|
+
data_server.handler_sock.close
|
413
472
|
|
414
|
-
|
415
|
-
|
416
|
-
|
473
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("226 Did it!\r\n")
|
474
|
+
expect(server.files).to include(file_prefix + 'some_other_file')
|
475
|
+
expect(server.file(file_prefix + 'some_other_file').bytes).to eql(5)
|
476
|
+
end
|
417
477
|
|
418
|
-
|
419
|
-
|
420
|
-
|
478
|
+
it 'accepts RETR with a filename' do
|
479
|
+
client.write("PORT #{data_server.addr_bits}\r\n")
|
480
|
+
data_server.handler_sock
|
481
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("200 Okay\r\n")
|
421
482
|
|
422
|
-
|
423
|
-
|
424
|
-
|
483
|
+
server.add_file(file_prefix + 'some_file', '1234567890')
|
484
|
+
client.write("RETR #{file_prefix}some_file\r\n")
|
485
|
+
expect(SpecHelper.gets_with_timeout(client))
|
486
|
+
.to eql("150 File status ok, about to open data connection\r\n")
|
425
487
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
488
|
+
data = SpecHelper.gets_with_timeout(
|
489
|
+
data_server.handler_sock, endwith: "\0"
|
490
|
+
)
|
491
|
+
data_server.handler_sock.close
|
430
492
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
493
|
+
expect(data).to eql('1234567890')
|
494
|
+
expect(SpecHelper.gets_with_timeout(client))
|
495
|
+
.to eql("226 File transferred\r\n")
|
496
|
+
end
|
435
497
|
|
436
|
-
|
437
|
-
|
438
|
-
|
498
|
+
it 'accepts RNFR without filename' do
|
499
|
+
client.write("RNFR\r\n")
|
500
|
+
expect(SpecHelper.gets_with_timeout(client))
|
501
|
+
.to eql("501 Send path name.\r\n")
|
502
|
+
end
|
439
503
|
|
440
|
-
|
441
|
-
|
442
|
-
|
504
|
+
it 'accepts RNTO without RNFR' do
|
505
|
+
client.write("RNTO some_other_file\r\n")
|
506
|
+
expect(SpecHelper.gets_with_timeout(client))
|
507
|
+
.to eql("503 Send RNFR first.\r\n")
|
508
|
+
end
|
443
509
|
|
444
|
-
|
445
|
-
|
446
|
-
|
510
|
+
it 'accepts RNTO and RNFR without filename' do
|
511
|
+
client.write("RNFR from_file\r\n")
|
512
|
+
expect(SpecHelper.gets_with_timeout(client))
|
513
|
+
.to eql("350 Send RNTO to complete rename.\r\n")
|
447
514
|
|
448
|
-
|
449
|
-
|
450
|
-
|
515
|
+
client.write("RNTO\r\n")
|
516
|
+
expect(SpecHelper.gets_with_timeout(client))
|
517
|
+
.to eql("501 Send path name.\r\n")
|
518
|
+
end
|
451
519
|
|
452
|
-
|
453
|
-
|
520
|
+
it 'accepts RNTO and RNFR for not existing file' do
|
521
|
+
client.write("RNFR from_file\r\n")
|
522
|
+
expect(SpecHelper.gets_with_timeout(client))
|
523
|
+
.to eql("350 Send RNTO to complete rename.\r\n")
|
454
524
|
|
455
|
-
|
456
|
-
|
525
|
+
client.write("RNTO to_file\r\n")
|
526
|
+
expect(SpecHelper.gets_with_timeout(client))
|
527
|
+
.to eql("550 File not found.\r\n")
|
528
|
+
end
|
457
529
|
|
458
|
-
|
459
|
-
|
530
|
+
it 'accepts RNTO and RNFR' do
|
531
|
+
server.add_file(file_prefix + 'from_file', '1234567890')
|
460
532
|
|
461
|
-
|
462
|
-
|
463
|
-
|
533
|
+
client.write("RNFR from_file\r\n")
|
534
|
+
expect(SpecHelper.gets_with_timeout(client))
|
535
|
+
.to eql("350 Send RNTO to complete rename.\r\n")
|
464
536
|
|
465
|
-
|
466
|
-
|
467
|
-
|
537
|
+
client.write("RNTO to_file\r\n")
|
538
|
+
expect(SpecHelper.gets_with_timeout(client))
|
539
|
+
.to eql("250 Path renamed.\r\n")
|
468
540
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
expect(client.gets).to eql("150 Listing status ok, about to open data connection\r\n")
|
541
|
+
expect(server.files).to include(file_prefix + 'to_file')
|
542
|
+
expect(server.files).to_not include(file_prefix + 'from_file')
|
543
|
+
end
|
473
544
|
|
474
|
-
|
475
|
-
|
476
|
-
|
545
|
+
it 'accepts an NLST command' do
|
546
|
+
client.write("PORT #{data_server.addr_bits}\r\n")
|
547
|
+
data_server.handler_sock
|
548
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("200 Okay\r\n")
|
549
|
+
|
550
|
+
server.add_file('some_file', '1234567890')
|
551
|
+
server.add_file('another_file', '1234567890')
|
552
|
+
client.write("NLST\r\n")
|
553
|
+
expect(SpecHelper.gets_with_timeout(client))
|
554
|
+
.to eql("150 Listing status ok, about to open data connection\r\n")
|
555
|
+
|
556
|
+
data = SpecHelper.gets_with_timeout(
|
557
|
+
data_server.handler_sock, endwith: "\0"
|
558
|
+
)
|
559
|
+
data_server.handler_sock.close
|
560
|
+
|
561
|
+
expect(data).to eql("some_file\nanother_file\n")
|
562
|
+
expect(SpecHelper.gets_with_timeout(client))
|
563
|
+
.to eql("226 List information transferred\r\n")
|
564
|
+
end
|
477
565
|
|
478
|
-
|
479
|
-
|
566
|
+
it 'has a flavor' do
|
567
|
+
client.write("PORT #{data_server.addr_bits}\r\n")
|
568
|
+
data_server.handler_sock
|
569
|
+
expect(SpecHelper.gets_with_timeout(client)).to eql("200 Okay\r\n")
|
570
|
+
|
571
|
+
client.write("WAT\r\n")
|
572
|
+
expect(SpecHelper.gets_with_timeout(client))
|
573
|
+
.to eql("418 Pizza Party\r\n")
|
574
|
+
|
575
|
+
data = SpecHelper.gets_with_timeout(
|
576
|
+
data_server.handler_sock, endwith: "\0"
|
577
|
+
)
|
578
|
+
data_server.handler_sock.close
|
579
|
+
expect(data).to_not be_nil
|
580
|
+
expect(data).to_not be_empty
|
581
|
+
end
|
480
582
|
end
|
481
583
|
end
|
482
584
|
end
|
483
585
|
end
|
484
|
-
|