ftpd 2.0.1 → 2.0.2
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/Changelog.md +6 -0
- data/Gemfile +2 -15
- data/Gemfile.lock +12 -51
- data/README.md +12 -0
- data/VERSION +1 -1
- data/bin/ftpdrb +17 -12
- data/examples/write_only.rb +61 -0
- data/ftpd.gemspec +59 -261
- data/lib/ftpd/session.rb +51 -51
- metadata +11 -153
- data/.travis.yml +0 -6
- data/features/example/eplf.feature +0 -14
- data/features/example/example.feature +0 -18
- data/features/example/read_only.feature +0 -63
- data/features/example/step_definitions/example_server.rb +0 -13
- data/features/ftp_server/abort.feature +0 -13
- data/features/ftp_server/allo.feature +0 -33
- data/features/ftp_server/append.feature +0 -94
- data/features/ftp_server/cdup.feature +0 -36
- data/features/ftp_server/command_errors.feature +0 -13
- data/features/ftp_server/concurrent_sessions.feature +0 -14
- data/features/ftp_server/delay_after_failed_login.feature +0 -23
- data/features/ftp_server/delete.feature +0 -60
- data/features/ftp_server/directory_navigation.feature +0 -59
- data/features/ftp_server/disconnect_after_failed_logins.feature +0 -25
- data/features/ftp_server/eprt.feature +0 -56
- data/features/ftp_server/epsv.feature +0 -37
- data/features/ftp_server/features.feature +0 -38
- data/features/ftp_server/file_structure.feature +0 -43
- data/features/ftp_server/get.feature +0 -80
- data/features/ftp_server/get_ipv6.feature +0 -46
- data/features/ftp_server/get_tls.feature +0 -23
- data/features/ftp_server/help.feature +0 -21
- data/features/ftp_server/implicit_tls.feature +0 -23
- data/features/ftp_server/invertability.feature +0 -15
- data/features/ftp_server/list.feature +0 -94
- data/features/ftp_server/list_tls.feature +0 -29
- data/features/ftp_server/logging.feature +0 -11
- data/features/ftp_server/login_auth_level_account.feature +0 -51
- data/features/ftp_server/login_auth_level_password.feature +0 -59
- data/features/ftp_server/login_auth_level_user.feature +0 -31
- data/features/ftp_server/max_connections.feature +0 -39
- data/features/ftp_server/mdtm.feature +0 -53
- data/features/ftp_server/mkdir.feature +0 -70
- data/features/ftp_server/mode.feature +0 -43
- data/features/ftp_server/name_list.feature +0 -77
- data/features/ftp_server/name_list_tls.feature +0 -30
- data/features/ftp_server/noop.feature +0 -17
- data/features/ftp_server/options.feature +0 -17
- data/features/ftp_server/pasv.feature +0 -30
- data/features/ftp_server/port.feature +0 -49
- data/features/ftp_server/put.feature +0 -79
- data/features/ftp_server/put_tls.feature +0 -23
- data/features/ftp_server/put_unique.feature +0 -56
- data/features/ftp_server/quit.feature +0 -23
- data/features/ftp_server/reinitialize.feature +0 -13
- data/features/ftp_server/rename.feature +0 -97
- data/features/ftp_server/rmdir.feature +0 -71
- data/features/ftp_server/site.feature +0 -13
- data/features/ftp_server/size.feature +0 -69
- data/features/ftp_server/status.feature +0 -18
- data/features/ftp_server/step_definitions/logging.rb +0 -10
- data/features/ftp_server/step_definitions/test_server.rb +0 -71
- data/features/ftp_server/structure_mount.feature +0 -13
- data/features/ftp_server/syntax_errors.feature +0 -18
- data/features/ftp_server/syst.feature +0 -18
- data/features/ftp_server/timeout.feature +0 -26
- data/features/ftp_server/type.feature +0 -59
- data/features/step_definitions/append.rb +0 -17
- data/features/step_definitions/client.rb +0 -27
- data/features/step_definitions/client_and_server_files.rb +0 -26
- data/features/step_definitions/client_files.rb +0 -16
- data/features/step_definitions/command.rb +0 -7
- data/features/step_definitions/connect.rb +0 -39
- data/features/step_definitions/delete.rb +0 -17
- data/features/step_definitions/directory_navigation.rb +0 -28
- data/features/step_definitions/error_replies.rb +0 -117
- data/features/step_definitions/features.rb +0 -23
- data/features/step_definitions/file_structure.rb +0 -18
- data/features/step_definitions/generic_send.rb +0 -11
- data/features/step_definitions/get.rb +0 -18
- data/features/step_definitions/help.rb +0 -20
- data/features/step_definitions/invalid_commands.rb +0 -13
- data/features/step_definitions/ipv6.rb +0 -11
- data/features/step_definitions/line_endings.rb +0 -9
- data/features/step_definitions/list.rb +0 -75
- data/features/step_definitions/login.rb +0 -84
- data/features/step_definitions/mkdir.rb +0 -11
- data/features/step_definitions/mode.rb +0 -17
- data/features/step_definitions/mtime.rb +0 -25
- data/features/step_definitions/noop.rb +0 -17
- data/features/step_definitions/options.rb +0 -11
- data/features/step_definitions/passive.rb +0 -10
- data/features/step_definitions/pending.rb +0 -5
- data/features/step_definitions/port.rb +0 -7
- data/features/step_definitions/put.rb +0 -31
- data/features/step_definitions/quit.rb +0 -17
- data/features/step_definitions/rename.rb +0 -13
- data/features/step_definitions/rmdir.rb +0 -11
- data/features/step_definitions/server_files.rb +0 -63
- data/features/step_definitions/server_title.rb +0 -14
- data/features/step_definitions/size.rb +0 -22
- data/features/step_definitions/status.rb +0 -11
- data/features/step_definitions/success_replies.rb +0 -9
- data/features/step_definitions/system.rb +0 -12
- data/features/step_definitions/timing.rb +0 -21
- data/features/step_definitions/type.rb +0 -17
- data/features/support/env.rb +0 -6
- data/features/support/example_server.rb +0 -69
- data/features/support/file_templates/ascii_unix +0 -4
- data/features/support/file_templates/ascii_windows +0 -4
- data/features/support/file_templates/binary +0 -0
- data/features/support/test_client.rb +0 -258
- data/features/support/test_file_templates.rb +0 -35
- data/features/support/test_server.rb +0 -304
- data/features/support/test_server_files.rb +0 -59
- data/rake_tasks/cucumber.rake +0 -9
- data/rake_tasks/default.rake +0 -1
- data/rake_tasks/jeweler.rake +0 -52
- data/rake_tasks/spec.rake +0 -3
- data/rake_tasks/test.rake +0 -2
- data/rake_tasks/yard.rake +0 -3
- data/spec/command_sequence_checker_spec.rb +0 -85
- data/spec/connection_throttle_spec.rb +0 -101
- data/spec/connection_tracker_spec.rb +0 -99
- data/spec/data_server_factory_spec.rb +0 -104
- data/spec/disk_file_system_spec.rb +0 -322
- data/spec/exception_translator_spec.rb +0 -38
- data/spec/file_info_spec.rb +0 -61
- data/spec/ftp_server_error_spec.rb +0 -15
- data/spec/list_format/eplf_spec.rb +0 -63
- data/spec/list_format/ls_spec.rb +0 -272
- data/spec/list_path_spec.rb +0 -23
- data/spec/null_logger_spec.rb +0 -26
- data/spec/protocols_spec.rb +0 -159
- data/spec/server_spec.rb +0 -83
- data/spec/spec_helper.rb +0 -17
- data/spec/telnet_spec.rb +0 -77
- data/spec/translate_exceptions_spec.rb +0 -42
- data/testlib/network.rb +0 -17
data/features/support/env.rb
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
require 'forwardable'
|
|
5
|
-
require File.expand_path('test_server_files',
|
|
6
|
-
File.dirname(__FILE__))
|
|
7
|
-
|
|
8
|
-
class ExampleServer
|
|
9
|
-
|
|
10
|
-
extend Forwardable
|
|
11
|
-
include FileUtils
|
|
12
|
-
include TestServerFiles
|
|
13
|
-
|
|
14
|
-
def initialize(args = nil)
|
|
15
|
-
command = [
|
|
16
|
-
File.expand_path('../../examples/example.rb',
|
|
17
|
-
File.dirname(__FILE__)),
|
|
18
|
-
args,
|
|
19
|
-
].join(' ')
|
|
20
|
-
@io = IO.popen(command, 'r+')
|
|
21
|
-
@output = read_output
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def stop
|
|
25
|
-
@io.close
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def host
|
|
29
|
-
@output[/Host: (.*)$/, 1]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def port
|
|
33
|
-
@output[/Port: (.*)$/, 1].to_i
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def user
|
|
37
|
-
@output[/User: "(.*)"$/, 1]
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def password
|
|
41
|
-
@output[/Pass: "(.*)"$/, 1]
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def account
|
|
45
|
-
@output[/Account: "(.*)"$/, 1]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def auth_level
|
|
49
|
-
Ftpd::AUTH_PASSWORD
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
private
|
|
53
|
-
|
|
54
|
-
def read_output
|
|
55
|
-
output = ''.dup
|
|
56
|
-
loop do
|
|
57
|
-
line = @io.gets
|
|
58
|
-
break if line.nil?
|
|
59
|
-
output << line
|
|
60
|
-
break if line =~ /FTP server started/
|
|
61
|
-
end
|
|
62
|
-
output
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def temp_dir
|
|
66
|
-
@output[/Directory: (.*)$/, 1]
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
end
|
|
Binary file
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'double_bag_ftps'
|
|
4
|
-
require 'net/ftp'
|
|
5
|
-
|
|
6
|
-
class TestClient
|
|
7
|
-
|
|
8
|
-
extend Forwardable
|
|
9
|
-
include FileUtils
|
|
10
|
-
|
|
11
|
-
attr_accessor :tls_mode
|
|
12
|
-
|
|
13
|
-
def initialize
|
|
14
|
-
@tls_mode = :off
|
|
15
|
-
@temp_dir = Ftpd::TempDir.make
|
|
16
|
-
@templates = TestFileTemplates.new
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def start
|
|
20
|
-
@ftp = make_ftp
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def close
|
|
24
|
-
return unless @ftp
|
|
25
|
-
ftp.close
|
|
26
|
-
@ftp = nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def_delegators :ftp,
|
|
30
|
-
:chdir,
|
|
31
|
-
:connect,
|
|
32
|
-
:delete,
|
|
33
|
-
:getbinaryfile,
|
|
34
|
-
:gettextfile,
|
|
35
|
-
:help,
|
|
36
|
-
:login,
|
|
37
|
-
:ls,
|
|
38
|
-
:mkdir,
|
|
39
|
-
:nlst,
|
|
40
|
-
:noop,
|
|
41
|
-
:passive=,
|
|
42
|
-
:pwd,
|
|
43
|
-
:quit,
|
|
44
|
-
:rename,
|
|
45
|
-
:rmdir,
|
|
46
|
-
:status,
|
|
47
|
-
:system
|
|
48
|
-
|
|
49
|
-
# Make a connection from a specific IP. Net::FTP doesn't have a way
|
|
50
|
-
# to force the local IP, so fake it here.
|
|
51
|
-
|
|
52
|
-
def connect_from(source_ip, host, port)
|
|
53
|
-
in_addr = Socket.pack_sockaddr_in(0, source_ip)
|
|
54
|
-
out_addr = Socket.pack_sockaddr_in(port, host)
|
|
55
|
-
socket = Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
|
56
|
-
socket.bind(in_addr)
|
|
57
|
-
socket.connect(out_addr)
|
|
58
|
-
decorate_socket socket
|
|
59
|
-
@ftp = make_ftp
|
|
60
|
-
ftp.set_socket(socket)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def raw(*command)
|
|
64
|
-
ftp.sendcmd command.compact.join(' ')
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def get(mode, remote_path)
|
|
68
|
-
method = "get#{mode}file"
|
|
69
|
-
ftp.send method, remote_path, local_path(remote_path)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def put(mode, remote_path)
|
|
73
|
-
method = "put#{mode}file"
|
|
74
|
-
ftp.send method, local_path(remote_path), remote_path
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def get_size(mode, remote_path)
|
|
78
|
-
raise unless ['binary', 'text'].include?(mode)
|
|
79
|
-
ftp.binary = mode == 'binary'
|
|
80
|
-
override_with_binary do
|
|
81
|
-
ftp.size(remote_path)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def get_mtime(remote_path)
|
|
86
|
-
ftp.mtime(remote_path)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def add_file(path)
|
|
90
|
-
full_path = temp_path(path)
|
|
91
|
-
mkdir_p File.dirname(full_path)
|
|
92
|
-
File.open(full_path, 'wb') do |file|
|
|
93
|
-
file.write @templates[File.basename(full_path)]
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def template(path)
|
|
98
|
-
@templates[File.basename(path)]
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def file_contents(path)
|
|
102
|
-
File.open(temp_path(path), 'rb', &:read)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def xpwd
|
|
106
|
-
response = ftp.sendcmd('XPWD')
|
|
107
|
-
response[/"(.+)"/, 1]
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def store_unique(local_path, remote_path)
|
|
111
|
-
command = ['STOU', remote_path].compact.join(' ')
|
|
112
|
-
File.open(temp_path(local_path), 'rb') do |file|
|
|
113
|
-
ftp.storbinary command, file, Net::FTP::DEFAULT_BLOCKSIZE
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def append_binary(local_path, remote_path)
|
|
118
|
-
command = ['APPE', remote_path].compact.join(' ')
|
|
119
|
-
File.open(temp_path(local_path), 'rb') do |file|
|
|
120
|
-
ftp.storbinary command, file, Net::FTP::DEFAULT_BLOCKSIZE
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def append_text(local_path, remote_path)
|
|
125
|
-
command = ['APPE', remote_path].compact.join(' ')
|
|
126
|
-
File.open(temp_path(local_path), 'rb') do |file|
|
|
127
|
-
ftp.storlines command, file
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def connected?
|
|
132
|
-
begin
|
|
133
|
-
ftp.noop
|
|
134
|
-
true
|
|
135
|
-
rescue Net::FTPTempError => e
|
|
136
|
-
!!e.to_s =~ /^421/
|
|
137
|
-
rescue EOFError
|
|
138
|
-
false
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def set_option(option)
|
|
143
|
-
ftp.sendcmd "OPTS #{option}"
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
private
|
|
147
|
-
|
|
148
|
-
RAW_METHOD_REGEX = /^send_(.*)$/
|
|
149
|
-
|
|
150
|
-
def ftp
|
|
151
|
-
raise "Not started" unless @ftp
|
|
152
|
-
@ftp
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def local_path(remote_path)
|
|
156
|
-
temp_path(File.basename(remote_path))
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def temp_path(path)
|
|
160
|
-
File.expand_path(path, @temp_dir)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def make_ftp
|
|
164
|
-
ftp =
|
|
165
|
-
case @tls_mode
|
|
166
|
-
when :off
|
|
167
|
-
make_non_tls_ftp
|
|
168
|
-
when :implicit
|
|
169
|
-
make_tls_ftp(:implicit)
|
|
170
|
-
when :explicit
|
|
171
|
-
make_tls_ftp(:explicit)
|
|
172
|
-
else
|
|
173
|
-
raise "Unknown TLS mode: #{@tls_mode}"
|
|
174
|
-
end
|
|
175
|
-
allow_size_in_ascii_mode ftp
|
|
176
|
-
ftp
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def make_tls_ftp(ftps_mode)
|
|
180
|
-
ftp = DoubleBagFTPS.new
|
|
181
|
-
context_opts = {
|
|
182
|
-
:verify_mode => OpenSSL::SSL::VERIFY_NONE
|
|
183
|
-
}
|
|
184
|
-
ftp.ssl_context = DoubleBagFTPS.create_ssl_context(context_opts)
|
|
185
|
-
ftp.ftps_mode = ftps_mode
|
|
186
|
-
ftp
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
def make_non_tls_ftp
|
|
190
|
-
Net::FTP.new
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
# Ruby FTP client forces binary mode when doing a SIZE command. Our
|
|
194
|
-
# tests want to check that the server's SIZE command works correctly
|
|
195
|
-
# in ASCII mode as well, so we'll monkey-patch the FTP client.
|
|
196
|
-
|
|
197
|
-
def allow_size_in_ascii_mode(ftp)
|
|
198
|
-
|
|
199
|
-
class << ftp
|
|
200
|
-
|
|
201
|
-
attr_accessor :override_with_binary
|
|
202
|
-
|
|
203
|
-
alias :orig_with_binary :with_binary
|
|
204
|
-
|
|
205
|
-
def with_binary(*args, &block)
|
|
206
|
-
if @override_with_binary
|
|
207
|
-
block.call
|
|
208
|
-
else
|
|
209
|
-
return orig_with_binary(*args, &block)
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def override_with_binary
|
|
218
|
-
orig = ftp.override_with_binary
|
|
219
|
-
begin
|
|
220
|
-
ftp.override_with_binary = true
|
|
221
|
-
yield
|
|
222
|
-
ensure
|
|
223
|
-
ftp.override_with_binary = orig
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# Ruby 2.0's Ftp class is expecting a TCPSocket, not a Socket. The
|
|
228
|
-
# trouble comes with Ftp#close, which closes sockets by first doing
|
|
229
|
-
# a shutdown, setting the read timeout, and doing a read. Plain
|
|
230
|
-
# Socket doesn't have those methods, so fake it.
|
|
231
|
-
#
|
|
232
|
-
# Plain socket _does_ have #close, but we short-circuit it, too,
|
|
233
|
-
# because it takes a few seconds. We're in a hurry when running
|
|
234
|
-
# tests, and can afford to be a little sloppy when cleaning up.
|
|
235
|
-
|
|
236
|
-
def decorate_socket(sock)
|
|
237
|
-
|
|
238
|
-
def sock.shutdown(how)
|
|
239
|
-
@shutdown = true
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def sock.read_timeout=(seconds)
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
# Skip read after shutdown. Prevents 2.0 from hanging in
|
|
246
|
-
# Ftp#close
|
|
247
|
-
|
|
248
|
-
def sock.read(*args)
|
|
249
|
-
return if @shutdown
|
|
250
|
-
super(*args)
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def close
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class TestFileTemplates
|
|
4
|
-
|
|
5
|
-
def [](filename)
|
|
6
|
-
if have_template?(filename)
|
|
7
|
-
read_template filename
|
|
8
|
-
else
|
|
9
|
-
default_template filename
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
-
def have_template?(filename)
|
|
16
|
-
File.exists?(template_path(filename))
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def read_template(filename)
|
|
20
|
-
File.open(template_path(filename), 'rb', &:read)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def template_path(filename)
|
|
24
|
-
File.expand_path(filename, templates_path)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def templates_path
|
|
28
|
-
File.expand_path('file_templates', File.dirname(__FILE__))
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def default_template(filename)
|
|
32
|
-
"Contents of #{filename}"
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
end
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
require 'forwardable'
|
|
5
|
-
require 'stringio'
|
|
6
|
-
require 'tempfile'
|
|
7
|
-
|
|
8
|
-
require File.expand_path('test_server_files',
|
|
9
|
-
File.dirname(__FILE__))
|
|
10
|
-
|
|
11
|
-
class TestServer
|
|
12
|
-
class TestServerDriver
|
|
13
|
-
|
|
14
|
-
extend Forwardable
|
|
15
|
-
|
|
16
|
-
USER = 'user'
|
|
17
|
-
PASSWORD = 'password'
|
|
18
|
-
ACCOUNT = 'account'
|
|
19
|
-
|
|
20
|
-
attr_accessor :append
|
|
21
|
-
attr_accessor :delete
|
|
22
|
-
attr_accessor :list
|
|
23
|
-
attr_accessor :mkdir
|
|
24
|
-
attr_accessor :read
|
|
25
|
-
attr_accessor :rename
|
|
26
|
-
attr_accessor :rmdir
|
|
27
|
-
attr_accessor :write
|
|
28
|
-
|
|
29
|
-
def initialize(temp_dir)
|
|
30
|
-
@temp_dir = temp_dir
|
|
31
|
-
@append = true
|
|
32
|
-
@delete = true
|
|
33
|
-
@list = true
|
|
34
|
-
@mkdir = true
|
|
35
|
-
@read = true
|
|
36
|
-
@rename = true
|
|
37
|
-
@rmdir = true
|
|
38
|
-
@write = true
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def authenticate(user, password, account)
|
|
42
|
-
user == USER &&
|
|
43
|
-
(password.nil? || password == PASSWORD) &&
|
|
44
|
-
(account.nil? || account == ACCOUNT)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def file_system(user)
|
|
48
|
-
TestServerFileSystem.new(@temp_dir,
|
|
49
|
-
:append => @append,
|
|
50
|
-
:delete => @delete,
|
|
51
|
-
:list => @list,
|
|
52
|
-
:mkdir => @mkdir,
|
|
53
|
-
:read => @read,
|
|
54
|
-
:rename => @rename,
|
|
55
|
-
:rmdir => @rmdir,
|
|
56
|
-
:write => @write)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
class TestServer
|
|
63
|
-
|
|
64
|
-
module ForcesAccessDenied
|
|
65
|
-
|
|
66
|
-
def self.included(includer)
|
|
67
|
-
includer.extend ClassMethods
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
module ClassMethods
|
|
71
|
-
|
|
72
|
-
def return_false_on_force_access_denied(method_name)
|
|
73
|
-
original_method = instance_method(method_name)
|
|
74
|
-
define_method method_name do |*args|
|
|
75
|
-
ftp_path = args.first
|
|
76
|
-
return false if force_access_denied?(ftp_path)
|
|
77
|
-
original_method.bind(self).call *args
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def force_access_denied?(ftp_path)
|
|
84
|
-
ftp_path =~ /forbidden/
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
class TestServer
|
|
92
|
-
|
|
93
|
-
module ForcesFileSystemError
|
|
94
|
-
|
|
95
|
-
def self.included(includer)
|
|
96
|
-
includer.extend ClassMethods
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
module ClassMethods
|
|
100
|
-
|
|
101
|
-
def raise_on_file_system_error(method_name)
|
|
102
|
-
original_method = instance_method(method_name)
|
|
103
|
-
define_method method_name do |*args, &block|
|
|
104
|
-
ftp_path = args.first
|
|
105
|
-
if force_file_system_error?(ftp_path)
|
|
106
|
-
raise Ftpd::PermanentFileSystemError, 'Unable to do it'
|
|
107
|
-
end
|
|
108
|
-
original_method.bind(self).call *args, &block
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def return_true_on_file_system_error(method_name)
|
|
113
|
-
original_method = instance_method(method_name)
|
|
114
|
-
define_method method_name do |*args, &block|
|
|
115
|
-
ftp_path = args.first
|
|
116
|
-
return true if force_file_system_error?(ftp_path)
|
|
117
|
-
original_method.bind(self).call *args, &block
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def force_file_system_error?(ftp_path)
|
|
124
|
-
ftp_path =~ /unable/
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
class TestServer
|
|
131
|
-
class TestServerFileSystem
|
|
132
|
-
|
|
133
|
-
# In order to test ftpd's ability to adapt itself to the driver's
|
|
134
|
-
# signature, we create a new, anonymous instance of the file
|
|
135
|
-
# system class for each test. The option flags determine whether
|
|
136
|
-
# or not to mix in certain behavior such as writing files, reading
|
|
137
|
-
# files, etc.
|
|
138
|
-
|
|
139
|
-
def self.new(data_dir, opts)
|
|
140
|
-
Class.new do
|
|
141
|
-
|
|
142
|
-
include ForcesAccessDenied
|
|
143
|
-
include ForcesFileSystemError
|
|
144
|
-
|
|
145
|
-
include Ftpd::DiskFileSystem::Base
|
|
146
|
-
|
|
147
|
-
if opts[:append]
|
|
148
|
-
include Ftpd::DiskFileSystem::Append
|
|
149
|
-
raise_on_file_system_error :append
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
if opts[:delete]
|
|
153
|
-
include Ftpd::DiskFileSystem::Delete
|
|
154
|
-
raise_on_file_system_error :delete
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
if opts[:list]
|
|
158
|
-
include Ftpd::DiskFileSystem::List
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
if opts[:mkdir]
|
|
162
|
-
include Ftpd::DiskFileSystem::Mkdir
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
if opts[:read]
|
|
166
|
-
include Ftpd::DiskFileSystem::Read
|
|
167
|
-
raise_on_file_system_error :read
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
if opts[:rename]
|
|
171
|
-
include Ftpd::DiskFileSystem::Rename
|
|
172
|
-
raise_on_file_system_error :rename
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
if opts[:rmdir]
|
|
176
|
-
include Ftpd::DiskFileSystem::Rmdir
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
if opts[:write]
|
|
180
|
-
include Ftpd::DiskFileSystem::Write
|
|
181
|
-
raise_on_file_system_error :write
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def initialize(data_dir)
|
|
185
|
-
set_data_dir data_dir
|
|
186
|
-
translate_exception SystemCallError
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
return_false_on_force_access_denied :accessible?
|
|
190
|
-
|
|
191
|
-
return_true_on_file_system_error :accessible?
|
|
192
|
-
return_true_on_file_system_error :exists?
|
|
193
|
-
return_true_on_file_system_error :directory?
|
|
194
|
-
|
|
195
|
-
end.new(data_dir)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
class TestServer
|
|
202
|
-
|
|
203
|
-
extend Forwardable
|
|
204
|
-
include FileUtils
|
|
205
|
-
include Ftpd::InsecureCertificate
|
|
206
|
-
include TestServerFiles
|
|
207
|
-
|
|
208
|
-
attr_writer :logging
|
|
209
|
-
|
|
210
|
-
def initialize
|
|
211
|
-
@temp_dir = Ftpd::TempDir.make
|
|
212
|
-
@log_device = StringIO.new
|
|
213
|
-
@driver = TestServerDriver.new(@temp_dir)
|
|
214
|
-
@templates = TestFileTemplates.new
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def_delegator :server, :'allow_low_data_ports='
|
|
218
|
-
def_delegator :server, :'auth_level'
|
|
219
|
-
def_delegator :server, :'auth_level='
|
|
220
|
-
def_delegator :server, :'failed_login_delay='
|
|
221
|
-
def_delegator :server, :'interface='
|
|
222
|
-
def_delegator :server, :'max_connections='
|
|
223
|
-
def_delegator :server, :'max_connections_per_ip='
|
|
224
|
-
def_delegator :server, :'max_failed_logins='
|
|
225
|
-
def_delegator :server, :'nat_ip='
|
|
226
|
-
def_delegator :server, :'server_name'
|
|
227
|
-
def_delegator :server, :'server_name='
|
|
228
|
-
def_delegator :server, :'session_timeout='
|
|
229
|
-
def_delegator :server, :'tls='
|
|
230
|
-
|
|
231
|
-
def_delegator :@driver, :'append='
|
|
232
|
-
def_delegator :@driver, :'delete='
|
|
233
|
-
def_delegator :@driver, :'list='
|
|
234
|
-
def_delegator :@driver, :'mkdir='
|
|
235
|
-
def_delegator :@driver, :'rmdir='
|
|
236
|
-
def_delegator :@driver, :'read='
|
|
237
|
-
def_delegator :@driver, :'rename='
|
|
238
|
-
def_delegator :@driver, :'write='
|
|
239
|
-
|
|
240
|
-
def log_output
|
|
241
|
-
@log_device.string
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def start
|
|
245
|
-
server.log = make_log
|
|
246
|
-
server.start
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
def stop
|
|
250
|
-
server.stop
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def host
|
|
254
|
-
server.interface
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def user
|
|
258
|
-
TestServerDriver::USER
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def password
|
|
262
|
-
TestServerDriver::PASSWORD
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
def account
|
|
266
|
-
TestServerDriver::ACCOUNT
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
def port
|
|
270
|
-
server.bound_port
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
def template(path)
|
|
274
|
-
@templates[File.basename(path)]
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
private
|
|
278
|
-
|
|
279
|
-
def server
|
|
280
|
-
@server ||= make_server
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
def make_server
|
|
284
|
-
s = Ftpd::FtpServer.new(@driver)
|
|
285
|
-
s.certfile_path = insecure_certfile_path
|
|
286
|
-
s.tls = :off
|
|
287
|
-
s
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def temp_dir
|
|
291
|
-
@temp_dir
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def make_log
|
|
295
|
-
if @logging
|
|
296
|
-
Logger.new(@log_device)
|
|
297
|
-
elsif ENV['FTPD_DEBUG'].to_i != 0
|
|
298
|
-
Logger.new($stdout)
|
|
299
|
-
else
|
|
300
|
-
nil
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
end
|