ftpd 0.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ftpd might be problematic. Click here for more details.
- data/Gemfile +9 -0
- data/Gemfile.lock +43 -0
- data/LICENSE.md +9 -0
- data/README.md +57 -0
- data/Rakefile +8 -0
- data/VERSION +1 -0
- data/features/command_errors.feature +33 -0
- data/features/delete.feature +34 -0
- data/features/directory_navigation.feature +34 -0
- data/features/file_structure.feature +40 -0
- data/features/get.feature +62 -0
- data/features/list.feature +77 -0
- data/features/login.feature +54 -0
- data/features/mode.feature +40 -0
- data/features/name_list.feature +77 -0
- data/features/noop.feature +14 -0
- data/features/port.feature +25 -0
- data/features/put.feature +65 -0
- data/features/quit.feature +20 -0
- data/features/step_definitions/client_and_server_files.rb +24 -0
- data/features/step_definitions/client_files.rb +9 -0
- data/features/step_definitions/command.rb +5 -0
- data/features/step_definitions/connect.rb +15 -0
- data/features/step_definitions/delete.rb +15 -0
- data/features/step_definitions/directories.rb +22 -0
- data/features/step_definitions/error.rb +87 -0
- data/features/step_definitions/file_structure.rb +16 -0
- data/features/step_definitions/get.rb +16 -0
- data/features/step_definitions/invalid_commands.rb +11 -0
- data/features/step_definitions/line_endings.rb +7 -0
- data/features/step_definitions/list.rb +46 -0
- data/features/step_definitions/login.rb +69 -0
- data/features/step_definitions/mode.rb +15 -0
- data/features/step_definitions/noop.rb +13 -0
- data/features/step_definitions/passive.rb +3 -0
- data/features/step_definitions/port.rb +5 -0
- data/features/step_definitions/put.rb +16 -0
- data/features/step_definitions/quit.rb +15 -0
- data/features/step_definitions/server.rb +7 -0
- data/features/step_definitions/server_files.rb +18 -0
- data/features/step_definitions/type.rb +15 -0
- data/features/support/env.rb +4 -0
- data/features/support/file_templates/ascii_unix +4 -0
- data/features/support/file_templates/ascii_windows +4 -0
- data/features/support/file_templates/binary +0 -0
- data/features/support/test_client.rb +89 -0
- data/features/support/test_file_templates.rb +33 -0
- data/features/support/test_server.rb +52 -0
- data/features/syntax_errors.feature +15 -0
- data/features/type.feature +53 -0
- data/ftpd.gemspec +112 -0
- data/insecure-test-cert.pem +29 -0
- data/lib/ftpd.rb +6 -0
- data/lib/ftpd/FakeFtpServer.rb +736 -0
- data/lib/ftpd/FakeServer.rb +57 -0
- data/lib/ftpd/FakeTlsServer.rb +52 -0
- data/lib/ftpd/ObjectUtil.rb +66 -0
- data/lib/ftpd/TempDir.rb +54 -0
- data/lib/ftpd/q.rb +92 -0
- data/rake_tasks/cucumber.rake +7 -0
- data/rake_tasks/jeweler.rake +25 -0
- metadata +164 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
When /^the client successfully gets (text|binary) "(.*?)"$/ \
|
2
|
+
do |mode, remote_path|
|
3
|
+
@client.get mode, remote_path
|
4
|
+
end
|
5
|
+
|
6
|
+
When /^the client gets (\S+) "(.*?)"$/ do |mode, path|
|
7
|
+
capture_error do
|
8
|
+
step %Q(the client successfully gets #{mode} "#{path}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^the client gets with no path$/ do
|
13
|
+
capture_error do
|
14
|
+
@client.raw 'RETR'
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class FileList
|
2
|
+
|
3
|
+
def initialize(lines)
|
4
|
+
@lines = lines
|
5
|
+
end
|
6
|
+
|
7
|
+
def filenames
|
8
|
+
@lines.map(&:split).map(&:last)
|
9
|
+
end
|
10
|
+
|
11
|
+
def long_form?
|
12
|
+
@lines.all? do |line|
|
13
|
+
line =~ /^[rwxSst-]{10}/
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def short_form?
|
18
|
+
!long_form?
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^the client lists the directory(?: "(.*?)")?$/ do |directory|
|
24
|
+
capture_error do
|
25
|
+
@list = FileList.new(@client.ls(*[directory].compact))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
When /^the client name lists the directory(?: "(.*?)")?$/ do |directory|
|
30
|
+
capture_error do
|
31
|
+
@list = FileList.new(@client.nlst(*[directory].compact))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Then /^the file list should( not)? contain "(.*?)"$/ do |neg, filename|
|
36
|
+
matcher = if neg
|
37
|
+
:should_not
|
38
|
+
else
|
39
|
+
:should
|
40
|
+
end
|
41
|
+
@list.filenames.send(matcher, include(filename))
|
42
|
+
end
|
43
|
+
|
44
|
+
Then /^the file list should be in (long|short) form$/ do |form|
|
45
|
+
@list.should send("be_#{form}_form")
|
46
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
def logged_in?
|
2
|
+
step "the client lists the directory"
|
3
|
+
@error.nil?
|
4
|
+
end
|
5
|
+
|
6
|
+
def login(user, password)
|
7
|
+
capture_error do
|
8
|
+
@client.login user, password
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Given /^a successful connection( with TLS)?$/ do |with_tls|
|
13
|
+
step 'the server is started'
|
14
|
+
step "the client connects#{with_tls}"
|
15
|
+
end
|
16
|
+
|
17
|
+
Given /^a successful login( with TLS)?$/ do |with_tls|
|
18
|
+
step "a successful connection#{with_tls}"
|
19
|
+
step 'the client logs in'
|
20
|
+
end
|
21
|
+
|
22
|
+
Given /^a failed login$/ do
|
23
|
+
step 'the server is started'
|
24
|
+
step 'the client connects'
|
25
|
+
step 'the client logs in with a bad password'
|
26
|
+
end
|
27
|
+
|
28
|
+
When /^the client logs in$/ do
|
29
|
+
login @server.user, @server.password
|
30
|
+
end
|
31
|
+
|
32
|
+
When /^the client logs in with a bad password$/ do
|
33
|
+
login @server.user, 'the-wrong-password'
|
34
|
+
end
|
35
|
+
|
36
|
+
When /^the client logs in with a bad user$/ do
|
37
|
+
login 'the-wrong-user', @server.password
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^the client should( not)? be logged in$/ do |neg|
|
41
|
+
matcher_method = if neg
|
42
|
+
:be_false
|
43
|
+
else
|
44
|
+
:be_true
|
45
|
+
end
|
46
|
+
logged_in?.should send(matcher_method)
|
47
|
+
end
|
48
|
+
|
49
|
+
When /^the client sends a password( with no parameter)?$/ do |no_param|
|
50
|
+
capture_error do
|
51
|
+
args = if no_param
|
52
|
+
[]
|
53
|
+
else
|
54
|
+
[@server.password]
|
55
|
+
end
|
56
|
+
@client.raw 'PASS', *args
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
When /^the client sends a user( with no parameter)?$/ do |no_param|
|
61
|
+
capture_error do
|
62
|
+
args = if no_param
|
63
|
+
[]
|
64
|
+
else
|
65
|
+
[@server.user]
|
66
|
+
end
|
67
|
+
@client.raw 'USER', *args
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
When /^the client successfully sets mode "(.*?)"$/ do |mode|
|
2
|
+
@client.raw 'MODE', mode
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^the client sets mode "(.*?)"$/ do |mode|
|
6
|
+
capture_error do
|
7
|
+
step %Q'the client successfully sets mode "#{mode}"'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
When /^the client sets mode with no parameter$/ do
|
12
|
+
capture_error do
|
13
|
+
@client.raw 'MODE'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
When /^the client successfully does nothing( with a parameter)?$/ do |with_param|
|
2
|
+
if with_param
|
3
|
+
@client.raw 'NOOP', 'foo'
|
4
|
+
else
|
5
|
+
@client.noop
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^the client does nothing( with a parameter)?$/ do |with_param|
|
10
|
+
capture_error do
|
11
|
+
step "the client successfully does nothing#{with_param}"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
When /^the client successfully puts (text|binary) "(.*?)"$/ do
|
2
|
+
|mode, local_path|
|
3
|
+
@client.put mode, local_path
|
4
|
+
end
|
5
|
+
|
6
|
+
When /^the client puts (\S+) "(.*?)"$/ do |mode, path|
|
7
|
+
capture_error do
|
8
|
+
step %Q(the client successfully puts #{mode} "#{path}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^the client puts with no path$/ do
|
13
|
+
capture_error do
|
14
|
+
@client.raw 'STOR'
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
When /^the client successfully quits$/ do
|
2
|
+
@client.quit
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^the client quits$/ do
|
6
|
+
capture_error do
|
7
|
+
step 'the client successfully quits'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
When /^the client quits with a parameter$/ do
|
12
|
+
capture_error do
|
13
|
+
@client.raw 'QUIT', 'foo'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Given /^the server has file "(.*?)"$/ do |remote_path|
|
2
|
+
@server.add_file remote_path
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^the server should( not)? have file "(.*?)"$/ do |neg, path|
|
6
|
+
matcher = if neg
|
7
|
+
:be_false
|
8
|
+
else
|
9
|
+
:be_true
|
10
|
+
end
|
11
|
+
@server.has_file?(path).should send(matcher)
|
12
|
+
end
|
13
|
+
|
14
|
+
Then /^the remote file "(.*?)" should have (unix|windows) line endings$/ do
|
15
|
+
|remote_path, line_ending_type|
|
16
|
+
line_ending_type(@server.file_contents(remote_path)).should ==
|
17
|
+
line_ending_type.to_sym
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
When /^the client successfully sets type "(.*?)"$/ do |type|
|
2
|
+
@client.raw 'TYPE', type
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^the client sets type "(.*?)"$/ do |type|
|
6
|
+
capture_error do
|
7
|
+
step %Q'the client successfully sets type "#{type}"'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
When /^the client sets type with no parameter$/ do
|
12
|
+
capture_error do
|
13
|
+
@client.raw 'TYPE'
|
14
|
+
end
|
15
|
+
end
|
Binary file
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'double_bag_ftps'
|
2
|
+
require 'net/ftp'
|
3
|
+
|
4
|
+
class TestClient
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
include FileUtils
|
8
|
+
|
9
|
+
def initialize(opts = {})
|
10
|
+
@temp_dir = Dir.mktmpdir
|
11
|
+
@ftp = make_ftp(opts)
|
12
|
+
@templates = TestFileTemplates.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def_delegators :@ftp,
|
16
|
+
:chdir,
|
17
|
+
:connect,
|
18
|
+
:delete,
|
19
|
+
:getbinaryfile,
|
20
|
+
:gettextfile,
|
21
|
+
:login,
|
22
|
+
:ls,
|
23
|
+
:nlst,
|
24
|
+
:noop,
|
25
|
+
:passive=,
|
26
|
+
:pwd,
|
27
|
+
:quit
|
28
|
+
|
29
|
+
def raw(*command)
|
30
|
+
@ftp.sendcmd *command.compact.join(' ')
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(mode, remote_path)
|
34
|
+
method = "get#{mode}file"
|
35
|
+
@ftp.send method, remote_path, local_path(remote_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def put(mode, remote_path)
|
39
|
+
method = "put#{mode}file"
|
40
|
+
@ftp.send method, local_path(remote_path), remote_path
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_file(path)
|
44
|
+
full_path = temp_path(path)
|
45
|
+
mkdir_p File.dirname(full_path)
|
46
|
+
File.open(full_path, 'wb') do |file|
|
47
|
+
file.puts @templates[File.basename(full_path)]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def file_contents(path)
|
52
|
+
File.open(temp_path(path), 'rb', &:read)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
RAW_METHOD_REGEX = /^send_(.*)$/
|
58
|
+
|
59
|
+
def local_path(remote_path)
|
60
|
+
temp_path(File.basename(remote_path))
|
61
|
+
end
|
62
|
+
|
63
|
+
def temp_path(path)
|
64
|
+
File.expand_path(path, @temp_dir)
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_ftp(opts)
|
68
|
+
tls = opts[:tls]
|
69
|
+
if tls
|
70
|
+
make_tls_ftp
|
71
|
+
else
|
72
|
+
make_non_tls_ftp
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def make_tls_ftp
|
77
|
+
ftp = DoubleBagFTPS.new
|
78
|
+
context_opts = {
|
79
|
+
:verify_mode => OpenSSL::SSL::VERIFY_NONE
|
80
|
+
}
|
81
|
+
ftp.ssl_context = DoubleBagFTPS.create_ssl_context(context_opts)
|
82
|
+
ftp
|
83
|
+
end
|
84
|
+
|
85
|
+
def make_non_tls_ftp
|
86
|
+
Net::FTP.new
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class TestFileTemplates
|
2
|
+
|
3
|
+
def [](filename)
|
4
|
+
if have_template?(filename)
|
5
|
+
read_template filename
|
6
|
+
else
|
7
|
+
default_template filename
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def have_template?(filename)
|
14
|
+
File.exists?(template_path(filename))
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_template(filename)
|
18
|
+
File.open(template_path(filename), 'r', &:read)
|
19
|
+
end
|
20
|
+
|
21
|
+
def template_path(filename)
|
22
|
+
File.expand_path(filename, templates_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def templates_path
|
26
|
+
File.expand_path('file_templates', File.dirname(__FILE__))
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_template(filename)
|
30
|
+
"Contents of #{filename}"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
class TestServer
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
include FileUtils
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@temp_dir = Dir.mktmpdir
|
11
|
+
@server = FakeFtpServer.new(@temp_dir)
|
12
|
+
@templates = TestFileTemplates.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def close
|
16
|
+
@server.close
|
17
|
+
rm_rf @temp_dir
|
18
|
+
end
|
19
|
+
|
20
|
+
def host
|
21
|
+
'localhost'
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_file(path)
|
25
|
+
full_path = temp_path(path)
|
26
|
+
mkdir_p File.dirname(full_path)
|
27
|
+
File.open(full_path, 'wb') do |file|
|
28
|
+
file.puts @templates[File.basename(full_path)]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_file?(path)
|
33
|
+
full_path = temp_path(path)
|
34
|
+
File.exists?(full_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def file_contents(path)
|
38
|
+
full_path = temp_path(path)
|
39
|
+
File.open(full_path, 'rb', &:read)
|
40
|
+
end
|
41
|
+
|
42
|
+
def_delegator :@server, :password
|
43
|
+
def_delegator :@server, :port
|
44
|
+
def_delegator :@server, :user
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def temp_path(path)
|
49
|
+
File.expand_path(path, @temp_dir)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|