ftpd 0.0.1.pre → 0.1.0
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 +3 -1
- data/Gemfile.lock +14 -14
- data/README.md +71 -23
- data/Rakefile +9 -3
- data/VERSION +1 -1
- data/examples/example.rb +132 -53
- data/examples/hello_world.rb +32 -0
- data/features/example/example.feature +18 -0
- data/features/example/step_definitions/example_server.rb +3 -0
- data/features/{command_errors.feature → ftp_server/command_errors.feature} +3 -0
- data/features/ftp_server/concurrent_sessions.feature +14 -0
- data/features/ftp_server/debug.feature +15 -0
- data/features/{delete.feature → ftp_server/delete.feature} +23 -2
- data/features/{directory_navigation.feature → ftp_server/directory_navigation.feature} +17 -4
- data/features/ftp_server/file_structure.feature +43 -0
- data/features/{get.feature → ftp_server/get.feature} +21 -10
- data/features/ftp_server/get_tls.feature +18 -0
- data/features/{list.feature → ftp_server/list.feature} +24 -30
- data/features/ftp_server/list_tls.feature +21 -0
- data/features/{login.feature → ftp_server/login.feature} +4 -2
- data/features/ftp_server/mode.feature +43 -0
- data/features/{name_list.feature → ftp_server/name_list.feature} +25 -31
- data/features/ftp_server/name_list_tls.feature +22 -0
- data/features/{noop.feature → ftp_server/noop.feature} +3 -0
- data/features/{port.feature → ftp_server/port.feature} +3 -0
- data/features/{put.feature → ftp_server/put.feature} +19 -11
- data/features/ftp_server/put_tls.feature +18 -0
- data/features/{quit.feature → ftp_server/quit.feature} +3 -0
- data/features/ftp_server/step_definitions/debug.rb +8 -0
- data/features/ftp_server/step_definitions/test_server.rb +12 -0
- data/features/{syntax_errors.feature → ftp_server/syntax_errors.feature} +3 -0
- data/features/ftp_server/type.feature +56 -0
- data/features/step_definitions/error.rb +10 -3
- data/features/step_definitions/list.rb +21 -4
- data/features/step_definitions/login.rb +0 -2
- data/features/step_definitions/server_files.rb +4 -0
- data/features/step_definitions/stop_server.rb +3 -0
- data/features/support/example_server.rb +58 -0
- data/features/support/test_client.rb +1 -1
- data/features/support/test_server.rb +106 -24
- data/features/support/test_server_files.rb +30 -0
- data/ftpd.gemspec +56 -25
- data/lib/ftpd.rb +22 -4
- data/lib/ftpd/disk_file_system.rb +137 -0
- data/lib/ftpd/error.rb +9 -0
- data/lib/ftpd/exception_translator.rb +29 -0
- data/lib/ftpd/exceptions.rb +13 -0
- data/lib/ftpd/file_system_error_translator.rb +21 -0
- data/lib/ftpd/ftp_server.rb +8 -645
- data/lib/ftpd/insecure_certificate.rb +10 -0
- data/lib/ftpd/server.rb +15 -12
- data/lib/ftpd/session.rb +569 -0
- data/lib/ftpd/temp_dir.rb +10 -11
- data/lib/ftpd/tls_server.rb +27 -15
- data/lib/ftpd/translate_exceptions.rb +44 -0
- data/rake_tasks/cucumber.rake +4 -2
- data/rake_tasks/default.rake +1 -0
- data/rake_tasks/spec.rake +3 -0
- data/rake_tasks/test.rake +2 -0
- data/sandbox/em-server.rb +37 -0
- data/spec/disk_file_system_spec.rb +239 -0
- data/spec/exception_translator_spec.rb +35 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/translate_exceptions_spec.rb +40 -0
- metadata +143 -115
- data/features/concurrent_sessions.feature +0 -11
- data/features/file_structure.feature +0 -40
- data/features/mode.feature +0 -40
- data/features/step_definitions/server.rb +0 -7
- data/features/type.feature +0 -53
@@ -1,51 +1,133 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'forwardable'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
require File.expand_path('test_server_files',
|
6
|
+
File.dirname(__FILE__))
|
7
|
+
|
8
|
+
class TestServer
|
9
|
+
class TestServerDriver
|
10
|
+
|
11
|
+
def initialize(temp_dir)
|
12
|
+
@temp_dir = temp_dir
|
13
|
+
end
|
14
|
+
|
15
|
+
USER = 'user'
|
16
|
+
PASSWORD = 'password'
|
17
|
+
|
18
|
+
def authenticate(user, password)
|
19
|
+
user == USER && password == PASSWORD
|
20
|
+
end
|
21
|
+
|
22
|
+
def file_system(user)
|
23
|
+
TestServerFileSystem.new(@temp_dir)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TestServer
|
30
|
+
class TestServerFileSystem < Ftpd::DiskFileSystem
|
31
|
+
|
32
|
+
def accessible?(ftp_path)
|
33
|
+
return false if force_access_denied?(ftp_path)
|
34
|
+
return true if force_file_system_error?(ftp_path)
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def exists?(ftp_path)
|
39
|
+
return true if force_file_system_error?(ftp_path)
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def directory?(ftp_path)
|
44
|
+
return true if force_file_system_error?(ftp_path)
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete(ftp_path)
|
49
|
+
force_file_system_error(ftp_path)
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def read(ftp_path)
|
54
|
+
force_file_system_error(ftp_path)
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
def write(ftp_path, contents)
|
59
|
+
force_file_system_error(ftp_path)
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def force_file_system_error(ftp_path)
|
66
|
+
if force_file_system_error?(ftp_path)
|
67
|
+
raise Ftpd::FileSystemError, 'Unable to do it'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def force_access_denied?(ftp_path)
|
72
|
+
ftp_path =~ /forbidden/
|
73
|
+
end
|
74
|
+
|
75
|
+
def force_file_system_error?(ftp_path)
|
76
|
+
ftp_path =~ /unable/
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
3
81
|
|
4
82
|
class TestServer
|
5
83
|
|
6
|
-
extend Forwardable
|
7
84
|
include FileUtils
|
85
|
+
include TestServerFiles
|
86
|
+
include Ftpd::InsecureCertificate
|
8
87
|
|
9
|
-
def initialize
|
10
|
-
|
11
|
-
@
|
88
|
+
def initialize(opts = {})
|
89
|
+
tls = opts[:tls] || :off
|
90
|
+
@temp_dir = Ftpd::TempDir.make
|
91
|
+
@debug_file = Tempfile.new('ftp-server-debug-output')
|
92
|
+
@debug_file.close
|
93
|
+
driver = TestServerDriver.new(@temp_dir)
|
94
|
+
@server = Ftpd::FtpServer.new(driver)
|
95
|
+
@server.tls = tls
|
96
|
+
@server.certfile_path = insecure_certfile_path
|
97
|
+
@server.debug_path = @debug_file.path
|
98
|
+
@server.debug = opts[:debug]
|
12
99
|
@templates = TestFileTemplates.new
|
100
|
+
@server.start
|
101
|
+
end
|
102
|
+
|
103
|
+
def wrote_debug_output?
|
104
|
+
File.size(@debug_file.path) > 0
|
13
105
|
end
|
14
106
|
|
15
|
-
def
|
16
|
-
@server.
|
107
|
+
def stop
|
108
|
+
@server.stop
|
17
109
|
end
|
18
110
|
|
19
111
|
def host
|
20
112
|
'localhost'
|
21
113
|
end
|
22
114
|
|
23
|
-
def
|
24
|
-
|
25
|
-
mkdir_p File.dirname(full_path)
|
26
|
-
File.open(full_path, 'wb') do |file|
|
27
|
-
file.puts @templates[File.basename(full_path)]
|
28
|
-
end
|
115
|
+
def user
|
116
|
+
TestServerDriver::USER
|
29
117
|
end
|
30
118
|
|
31
|
-
def
|
32
|
-
|
33
|
-
File.exists?(full_path)
|
119
|
+
def password
|
120
|
+
TestServerDriver::PASSWORD
|
34
121
|
end
|
35
122
|
|
36
|
-
def
|
37
|
-
|
38
|
-
File.open(full_path, 'rb', &:read)
|
123
|
+
def port
|
124
|
+
@server.bound_port
|
39
125
|
end
|
40
126
|
|
41
|
-
def_delegator :@server, :password
|
42
|
-
def_delegator :@server, :port
|
43
|
-
def_delegator :@server, :user
|
44
|
-
|
45
127
|
private
|
46
128
|
|
47
|
-
def
|
48
|
-
|
129
|
+
def temp_dir
|
130
|
+
@temp_dir
|
49
131
|
end
|
50
132
|
|
51
133
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module TestServerFiles
|
2
|
+
|
3
|
+
def add_file(path)
|
4
|
+
full_path = temp_path(path)
|
5
|
+
mkdir_p File.dirname(full_path)
|
6
|
+
File.open(full_path, 'wb') do |file|
|
7
|
+
file.puts @templates[File.basename(full_path)]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_directory(path)
|
12
|
+
full_path = temp_path(path)
|
13
|
+
mkdir_p full_path
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_file?(path)
|
17
|
+
full_path = temp_path(path)
|
18
|
+
File.exists?(full_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def file_contents(path)
|
22
|
+
full_path = temp_path(path)
|
23
|
+
File.open(full_path, 'rb', &:read)
|
24
|
+
end
|
25
|
+
|
26
|
+
def temp_path(path)
|
27
|
+
File.expand_path(path, temp_dir)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/ftpd.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "ftpd"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new("
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Wayne Conrad"]
|
12
|
-
s.date = "2013-02-
|
12
|
+
s.date = "2013-02-22"
|
13
13
|
s.description = "ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS, suitlble for use by a program such as a test fixture or FTP daemon."
|
14
14
|
s.email = "wconrad@yagni.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,20 +24,32 @@ Gem::Specification.new do |s|
|
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
26
|
"examples/example.rb",
|
27
|
-
"
|
28
|
-
"features/
|
29
|
-
"features/
|
30
|
-
"features/
|
31
|
-
"features/
|
32
|
-
"features/
|
33
|
-
"features/
|
34
|
-
"features/
|
35
|
-
"features/
|
36
|
-
"features/
|
37
|
-
"features/
|
38
|
-
"features/
|
39
|
-
"features/
|
40
|
-
"features/
|
27
|
+
"examples/hello_world.rb",
|
28
|
+
"features/example/example.feature",
|
29
|
+
"features/example/step_definitions/example_server.rb",
|
30
|
+
"features/ftp_server/command_errors.feature",
|
31
|
+
"features/ftp_server/concurrent_sessions.feature",
|
32
|
+
"features/ftp_server/debug.feature",
|
33
|
+
"features/ftp_server/delete.feature",
|
34
|
+
"features/ftp_server/directory_navigation.feature",
|
35
|
+
"features/ftp_server/file_structure.feature",
|
36
|
+
"features/ftp_server/get.feature",
|
37
|
+
"features/ftp_server/get_tls.feature",
|
38
|
+
"features/ftp_server/list.feature",
|
39
|
+
"features/ftp_server/list_tls.feature",
|
40
|
+
"features/ftp_server/login.feature",
|
41
|
+
"features/ftp_server/mode.feature",
|
42
|
+
"features/ftp_server/name_list.feature",
|
43
|
+
"features/ftp_server/name_list_tls.feature",
|
44
|
+
"features/ftp_server/noop.feature",
|
45
|
+
"features/ftp_server/port.feature",
|
46
|
+
"features/ftp_server/put.feature",
|
47
|
+
"features/ftp_server/put_tls.feature",
|
48
|
+
"features/ftp_server/quit.feature",
|
49
|
+
"features/ftp_server/step_definitions/debug.rb",
|
50
|
+
"features/ftp_server/step_definitions/test_server.rb",
|
51
|
+
"features/ftp_server/syntax_errors.feature",
|
52
|
+
"features/ftp_server/type.feature",
|
41
53
|
"features/step_definitions/client.rb",
|
42
54
|
"features/step_definitions/client_and_server_files.rb",
|
43
55
|
"features/step_definitions/client_files.rb",
|
@@ -58,56 +70,75 @@ Gem::Specification.new do |s|
|
|
58
70
|
"features/step_definitions/port.rb",
|
59
71
|
"features/step_definitions/put.rb",
|
60
72
|
"features/step_definitions/quit.rb",
|
61
|
-
"features/step_definitions/server.rb",
|
62
73
|
"features/step_definitions/server_files.rb",
|
74
|
+
"features/step_definitions/stop_server.rb",
|
63
75
|
"features/step_definitions/type.rb",
|
64
76
|
"features/support/env.rb",
|
77
|
+
"features/support/example_server.rb",
|
65
78
|
"features/support/file_templates/ascii_unix",
|
66
79
|
"features/support/file_templates/ascii_windows",
|
67
80
|
"features/support/file_templates/binary",
|
68
81
|
"features/support/test_client.rb",
|
69
82
|
"features/support/test_file_templates.rb",
|
70
83
|
"features/support/test_server.rb",
|
71
|
-
"features/
|
72
|
-
"features/type.feature",
|
84
|
+
"features/support/test_server_files.rb",
|
73
85
|
"ftpd.gemspec",
|
74
86
|
"insecure-test-cert.pem",
|
75
87
|
"lib/ftpd.rb",
|
88
|
+
"lib/ftpd/disk_file_system.rb",
|
89
|
+
"lib/ftpd/error.rb",
|
90
|
+
"lib/ftpd/exception_translator.rb",
|
91
|
+
"lib/ftpd/exceptions.rb",
|
92
|
+
"lib/ftpd/file_system_error_translator.rb",
|
76
93
|
"lib/ftpd/ftp_server.rb",
|
94
|
+
"lib/ftpd/insecure_certificate.rb",
|
77
95
|
"lib/ftpd/server.rb",
|
96
|
+
"lib/ftpd/session.rb",
|
78
97
|
"lib/ftpd/temp_dir.rb",
|
79
98
|
"lib/ftpd/tls_server.rb",
|
99
|
+
"lib/ftpd/translate_exceptions.rb",
|
80
100
|
"rake_tasks/cucumber.rake",
|
81
|
-
"rake_tasks/
|
101
|
+
"rake_tasks/default.rake",
|
102
|
+
"rake_tasks/jeweler.rake",
|
103
|
+
"rake_tasks/spec.rake",
|
104
|
+
"rake_tasks/test.rake",
|
105
|
+
"sandbox/em-server.rb",
|
106
|
+
"spec/disk_file_system_spec.rb",
|
107
|
+
"spec/exception_translator_spec.rb",
|
108
|
+
"spec/spec_helper.rb",
|
109
|
+
"spec/translate_exceptions_spec.rb"
|
82
110
|
]
|
83
111
|
s.homepage = "http://github.com/wconrad/ftpd"
|
84
112
|
s.licenses = ["MIT"]
|
85
113
|
s.require_paths = ["lib"]
|
86
|
-
s.rubygems_version = "1.8.
|
114
|
+
s.rubygems_version = "1.8.24"
|
87
115
|
s.summary = "Pure Ruby FTP server library"
|
88
116
|
|
89
117
|
if s.respond_to? :specification_version then
|
90
118
|
s.specification_version = 3
|
91
119
|
|
92
120
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
121
|
+
s.add_runtime_dependency(%q<memoizer>, ["~> 1.0.1"])
|
93
122
|
s.add_development_dependency(%q<cucumber>, ["~> 1.2.1"])
|
94
123
|
s.add_development_dependency(%q<double-bag-ftps>, ["~> 0.1.0"])
|
95
124
|
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
96
125
|
s.add_development_dependency(%q<rake>, ["~> 10.0.3"])
|
97
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.0
|
126
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.12.0"])
|
98
127
|
else
|
128
|
+
s.add_dependency(%q<memoizer>, ["~> 1.0.1"])
|
99
129
|
s.add_dependency(%q<cucumber>, ["~> 1.2.1"])
|
100
130
|
s.add_dependency(%q<double-bag-ftps>, ["~> 0.1.0"])
|
101
131
|
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
102
132
|
s.add_dependency(%q<rake>, ["~> 10.0.3"])
|
103
|
-
s.add_dependency(%q<rspec>, ["~> 2.0
|
133
|
+
s.add_dependency(%q<rspec>, ["~> 2.12.0"])
|
104
134
|
end
|
105
135
|
else
|
136
|
+
s.add_dependency(%q<memoizer>, ["~> 1.0.1"])
|
106
137
|
s.add_dependency(%q<cucumber>, ["~> 1.2.1"])
|
107
138
|
s.add_dependency(%q<double-bag-ftps>, ["~> 0.1.0"])
|
108
139
|
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
109
140
|
s.add_dependency(%q<rake>, ["~> 10.0.3"])
|
110
|
-
s.add_dependency(%q<rspec>, ["~> 2.0
|
141
|
+
s.add_dependency(%q<rspec>, ["~> 2.12.0"])
|
111
142
|
end
|
112
143
|
end
|
113
144
|
|
data/lib/ftpd.rb
CHANGED
@@ -1,4 +1,22 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
1
|
+
require 'fileutils'
|
2
|
+
require 'memoizer'
|
3
|
+
require 'openssl'
|
4
|
+
require 'pathname'
|
5
|
+
require 'socket'
|
6
|
+
require 'tmpdir'
|
7
|
+
|
8
|
+
module Ftpd
|
9
|
+
autoload :DiskFileSystem, 'ftpd/disk_file_system'
|
10
|
+
autoload :Error, 'ftpd/error'
|
11
|
+
autoload :ExceptionTranslator, 'ftpd/exception_translator'
|
12
|
+
autoload :FileSystemErrorTranslator, 'ftpd/file_system_error_translator'
|
13
|
+
autoload :FtpServer, 'ftpd/ftp_server'
|
14
|
+
autoload :InsecureCertificate, 'ftpd/insecure_certificate'
|
15
|
+
autoload :Server, 'ftpd/server'
|
16
|
+
autoload :Session, 'ftpd/session'
|
17
|
+
autoload :TempDir, 'ftpd/temp_dir'
|
18
|
+
autoload :TlsServer, 'ftpd/tls_server'
|
19
|
+
autoload :TranslateExceptions, 'ftpd/translate_exceptions'
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'ftpd/exceptions'
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Ftpd
|
2
|
+
|
3
|
+
# An FTP file system mapped to a disk directory. This can serve as
|
4
|
+
# a template for creating your own specialized driver.
|
5
|
+
#
|
6
|
+
# Some methods may raise FileSystemError; some may not. The
|
7
|
+
# predicates (methods ending with a question mark) may not; other
|
8
|
+
# methods may. FileSystemError is the _only_ exception which a file
|
9
|
+
# system driver may raise.
|
10
|
+
|
11
|
+
class DiskFileSystem
|
12
|
+
|
13
|
+
include TranslateExceptions
|
14
|
+
|
15
|
+
# Make a new instance to serve a directory. data_dir should be
|
16
|
+
# fully qualified.
|
17
|
+
|
18
|
+
def initialize(data_dir)
|
19
|
+
@data_dir = data_dir
|
20
|
+
translate_exception SystemCallError
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return true if the path is accessible to the user. This will be
|
24
|
+
# called for put, get and directory lists, so the file or
|
25
|
+
# directory named by the path may not exist.
|
26
|
+
|
27
|
+
def accessible?(ftp_path)
|
28
|
+
# The server should never try to access a path outside of the
|
29
|
+
# directory (such as '../foo'), but if it does, we'll catch it
|
30
|
+
# here.
|
31
|
+
expand_ftp_path(ftp_path).start_with?(@data_dir)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return true if the file or directory path exists.
|
35
|
+
|
36
|
+
def exists?(ftp_path)
|
37
|
+
File.exists?(expand_ftp_path(ftp_path))
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return true if the path exists and is a directory.
|
41
|
+
|
42
|
+
def directory?(ftp_path)
|
43
|
+
File.directory?(expand_ftp_path(ftp_path))
|
44
|
+
end
|
45
|
+
|
46
|
+
# Remove a file. Can raise FileSystemError.
|
47
|
+
|
48
|
+
def delete(ftp_path)
|
49
|
+
FileUtils.rm expand_ftp_path(ftp_path)
|
50
|
+
end
|
51
|
+
translate_exceptions :delete
|
52
|
+
|
53
|
+
# Read a file into memory. Can raise FileSystemError.
|
54
|
+
|
55
|
+
def read(ftp_path)
|
56
|
+
File.open(expand_ftp_path(ftp_path), 'rb', &:read)
|
57
|
+
end
|
58
|
+
translate_exceptions :read
|
59
|
+
|
60
|
+
# Write a file to disk. Can raise FileSystemError.
|
61
|
+
|
62
|
+
def write(ftp_path, contents)
|
63
|
+
File.open(expand_ftp_path(ftp_path), 'wb') do |file|
|
64
|
+
file.write contents
|
65
|
+
end
|
66
|
+
end
|
67
|
+
translate_exceptions :write
|
68
|
+
|
69
|
+
# Get a file list, long form. Can raise FileSystemError. This
|
70
|
+
# returns a long-form directory listing. The FTP standard does
|
71
|
+
# not specify the format of the listing, but many systems emit a
|
72
|
+
# *nix style directory listing:
|
73
|
+
#
|
74
|
+
# -rw-r--r-- 1 wayne wayne 4 Feb 18 18:36 a
|
75
|
+
# -rw-r--r-- 1 wayne wayne 8 Feb 18 18:36 b
|
76
|
+
#
|
77
|
+
# some emit a Windows style listing. Some emit EPLF (Easily
|
78
|
+
# Parsed List Format):
|
79
|
+
#
|
80
|
+
# +i8388621.48594,m825718503,r,s280, djb.html
|
81
|
+
# +i8388621.50690,m824255907,/, 514
|
82
|
+
# +i8388621.48598,m824253270,r,s612, 514.html
|
83
|
+
#
|
84
|
+
# EPLF is a draft internet standard for the output of LIST:
|
85
|
+
#
|
86
|
+
# http://cr.yp.to/ftp/list/eplf.html
|
87
|
+
#
|
88
|
+
# Some FTP clients know how to parse EPLF; those clients will
|
89
|
+
# display the EPLF in a more user-friendly format. Clients that
|
90
|
+
# don't recognize EPLF will display it raw. The advantages of
|
91
|
+
# EPLF are that it's easier for clients to parse, and the client
|
92
|
+
# can display the LIST output in any format it likes.
|
93
|
+
#
|
94
|
+
# This class emits a *nix style listing. It does so by shelling
|
95
|
+
# to the "ls" command, so it won't run on Windows at all.
|
96
|
+
|
97
|
+
def list_long(ftp_path)
|
98
|
+
ls(ftp_path, '-l')
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get a file list, short form. Can raise FileSystemError.
|
102
|
+
#
|
103
|
+
# This returns one filename per line, and nothing else
|
104
|
+
|
105
|
+
def list_short(ftp_path)
|
106
|
+
ls(ftp_path, '-1')
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def ls(ftp_path, option)
|
112
|
+
path = expand_ftp_path(ftp_path)
|
113
|
+
dirname = File.dirname(path)
|
114
|
+
filename = File.basename(path)
|
115
|
+
command = [
|
116
|
+
'ls',
|
117
|
+
option,
|
118
|
+
filename,
|
119
|
+
'2>&1',
|
120
|
+
].compact.join(' ')
|
121
|
+
if File.exists?(dirname)
|
122
|
+
list = Dir.chdir(dirname) do
|
123
|
+
`#{command}`
|
124
|
+
end
|
125
|
+
else
|
126
|
+
list = ''
|
127
|
+
end
|
128
|
+
list = "" if $? != 0
|
129
|
+
list = list.gsub(/^total \d+\n/, '')
|
130
|
+
end
|
131
|
+
|
132
|
+
def expand_ftp_path(ftp_path)
|
133
|
+
File.expand_path(File.join(@data_dir, ftp_path))
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|