ftpd 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da62814ce49fab3ad30c35b6841810728722bc99
4
- data.tar.gz: fd68f590b35708037f0ca1678dd84936dee7d4e1
3
+ metadata.gz: 637e5bfc2505f89b270ab1a8f89df0dfdb02e84c
4
+ data.tar.gz: c5209641735180ea65417e1c7ed9afeea370c946
5
5
  SHA512:
6
- metadata.gz: a84a4a7d0554c71e71816376b7cac3268d0f6250a58d34ffa0a9bc1f5116468b73e36c5f09a333f28b173cf9c4dd9e7427ac5c9f9cb93cf12f63344ae7bfd09e
7
- data.tar.gz: ffd9e7e5477c2afd9301158be878968ed7e601677433f5c8e2b0dfee050106f580026235368a726e05a84da0f92a25369de649ddf19a1fa03ff8b2e7d591ce98
6
+ metadata.gz: 16be5eac5cfe71218a0bd02bb32585ca86f4d241cb6c5ba80b531d8b388a04375f069d1a2f8af323b6a7e452678ddc9239091037b52dae7284f696f04f288e65
7
+ data.tar.gz: 5dfcee331e594fd2d5a8f7b956d81b71a9b2693ecbf5ffb9603cbd0f83aa1e4df945216eb7f403b37067b2851f7efdedc81bf81b17a495493f84f4213f49e487
data/Changelog.md CHANGED
@@ -2,7 +2,11 @@ This is the change log for the main branch of ftpd, which supports
2
2
  Ruby 1.9 and greater. For ruby 1.8.7, please use the latest version
3
3
  before 0.8.0.
4
4
 
5
- ### Development
5
+ ### 2.0.1 2016-11-02
6
+
7
+ * Redact password in log (thanks ukolovda)
8
+
9
+ ### 2.0.0 2016-06-01
6
10
 
7
11
  Breaking changes:
8
12
 
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ gem 'memoizer', '~> 1.0'
4
4
 
5
5
  group :development do
6
6
  gem 'cucumber', '~> 2.0'
7
- gem 'double-bag-ftps', '~> 0.1'
7
+ gem 'double-bag-ftps', '~> 0.1', '>= 0.1.3'
8
8
  gem 'jeweler', '~> 2.0'
9
9
  gem 'rake', '~> 11.1'
10
10
  gem 'redcarpet', '~> 3.1'
data/Gemfile.lock CHANGED
@@ -3,62 +3,63 @@ GEM
3
3
  specs:
4
4
  addressable (2.4.0)
5
5
  builder (3.2.2)
6
- cucumber (2.3.3)
6
+ cucumber (2.4.0)
7
7
  builder (>= 2.1.2)
8
- cucumber-core (~> 1.4.0)
8
+ cucumber-core (~> 1.5.0)
9
9
  cucumber-wire (~> 0.0.1)
10
10
  diff-lcs (>= 1.1.3)
11
- gherkin (~> 3.2.0)
11
+ gherkin (~> 4.0)
12
12
  multi_json (>= 1.7.5, < 2.0)
13
13
  multi_test (>= 0.1.2)
14
- cucumber-core (1.4.0)
15
- gherkin (~> 3.2.0)
14
+ cucumber-core (1.5.0)
15
+ gherkin (~> 4.0)
16
16
  cucumber-wire (0.0.1)
17
17
  descendants_tracker (0.0.4)
18
18
  thread_safe (~> 0.3, >= 0.3.1)
19
19
  diff-lcs (1.2.5)
20
- double-bag-ftps (0.1.2)
20
+ double-bag-ftps (0.1.3)
21
21
  faraday (0.9.2)
22
22
  multipart-post (>= 1.2, < 3)
23
- gherkin (3.2.0)
23
+ gherkin (4.0.0)
24
24
  git (1.3.0)
25
- github_api (0.13.1)
26
- addressable (~> 2.4.0)
27
- descendants_tracker (~> 0.0.4)
25
+ github_api (0.11.3)
26
+ addressable (~> 2.3)
27
+ descendants_tracker (~> 0.0.1)
28
28
  faraday (~> 0.8, < 0.10)
29
- hashie (>= 3.4)
29
+ hashie (>= 1.2)
30
30
  multi_json (>= 1.7.5, < 2.0)
31
+ nokogiri (~> 1.6.0)
31
32
  oauth2
32
- hashie (3.4.3)
33
+ hashie (3.4.6)
33
34
  highline (1.7.8)
34
- jeweler (2.1.1)
35
+ jeweler (2.1.2)
35
36
  builder
36
37
  bundler (>= 1.0)
37
38
  git (>= 1.2.5)
38
- github_api
39
+ github_api (~> 0.11.0)
39
40
  highline (>= 1.6.15)
40
41
  nokogiri (>= 1.5.10)
41
42
  rake
42
43
  rdoc
43
44
  semver
44
45
  json (1.8.3)
45
- jwt (1.5.1)
46
+ jwt (1.5.6)
46
47
  memoizer (1.0.1)
47
- mini_portile2 (2.0.0)
48
- multi_json (1.11.2)
48
+ mini_portile2 (2.1.0)
49
+ multi_json (1.12.1)
49
50
  multi_test (0.1.2)
50
51
  multi_xml (0.5.5)
51
52
  multipart-post (2.0.0)
52
- nokogiri (1.6.7.2)
53
- mini_portile2 (~> 2.0.0.rc2)
54
- oauth2 (1.1.0)
53
+ nokogiri (1.6.8.1)
54
+ mini_portile2 (~> 2.1.0)
55
+ oauth2 (1.2.0)
55
56
  faraday (>= 0.8, < 0.10)
56
- jwt (~> 1.0, < 1.5.2)
57
+ jwt (~> 1.0)
57
58
  multi_json (~> 1.3)
58
59
  multi_xml (~> 0.5)
59
60
  rack (>= 1.2, < 3)
60
- rack (1.6.4)
61
- rake (11.1.2)
61
+ rack (2.0.1)
62
+ rake (11.3.0)
62
63
  rdoc (4.2.2)
63
64
  json (~> 1.4)
64
65
  redcarpet (3.3.4)
@@ -88,7 +89,7 @@ PLATFORMS
88
89
 
89
90
  DEPENDENCIES
90
91
  cucumber (~> 2.0)
91
- double-bag-ftps (~> 0.1)
92
+ double-bag-ftps (~> 0.1, >= 0.1.3)
92
93
  jeweler (~> 2.0)
93
94
  memoizer (~> 1.0)
94
95
  rake (~> 11.1)
@@ -99,4 +100,4 @@ DEPENDENCIES
99
100
  yard (~> 0.8.7)
100
101
 
101
102
  BUNDLED WITH
102
- 1.11.2
103
+ 1.13.1
data/README.md CHANGED
@@ -174,8 +174,8 @@ server.start
174
174
 
175
175
  You can set any of these attributes before starting the server:
176
176
 
177
- # {Ftpd::FtpServer#nat_ip}
178
- # {Ftpd::FtpServer#passive_ports}
177
+ * {Ftpd::FtpServer#nat_ip}
178
+ * {Ftpd::FtpServer#passive_ports}
179
179
  * {Ftpd::FtpServer#allow_low_data_ports}
180
180
  * {Ftpd::FtpServer#auth_level}
181
181
  * {Ftpd::FtpServer#exception_handler}
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.0.1
data/bin/ftpdrb ADDED
@@ -0,0 +1,289 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Ftpd is a pure Ruby FTP server library. CLI version
4
+ #
5
+ unless $:.include?(File.dirname(__FILE__) + '/../lib')
6
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
7
+ end
8
+
9
+ require 'ftpd'
10
+ require 'ipaddr'
11
+ require 'optparse'
12
+
13
+ module FTPDrb
14
+
15
+ # Command-line option parser
16
+ class Arguments
17
+
18
+ attr_reader :account
19
+ attr_reader :auth_level
20
+ attr_reader :debug
21
+ attr_reader :eplf
22
+ attr_reader :interface
23
+ attr_reader :nat_ip
24
+ attr_reader :passive_ports
25
+ attr_reader :password
26
+ attr_reader :port
27
+ attr_reader :path
28
+ attr_reader :read_only
29
+ attr_reader :session_timeout
30
+ attr_reader :servername
31
+ attr_reader :tls
32
+ attr_reader :user
33
+
34
+ def initialize(argv)
35
+ @interface = '127.0.0.1'
36
+ @tls = :explicit
37
+ @port = 0
38
+ @auth_level = 'password'
39
+ @user = ENV['LOGNAME']
40
+ @password = ''
41
+ @account = ''
42
+ @path = nil
43
+ @session_timeout = default_session_timeout
44
+ @servername = 'FTPd - Pure Ruby FTP server'
45
+ @log = nil
46
+ @nat_ip = nil
47
+ @passive_ports = nil
48
+
49
+ opts = option_parser
50
+ opts.parse!(argv)
51
+ rescue OptionParser::ParseError => e
52
+ $stderr.puts e
53
+ exit(1)
54
+ end
55
+
56
+ private
57
+
58
+ def option_parser
59
+ opts = OptionParser.new do |op|
60
+ op.banner = "ftpdrb - Pure Ruby FTP Server. (ftpd ruby gem)\n\nUsage: #{__FILE__} [OPTIONS]"
61
+
62
+ op.separator "Help menu:"
63
+ op.on('-p', '--port N', Integer, 'Bind to a specific port') do |t|
64
+ @port = t
65
+ end
66
+ op.on('-i', '--interface IP', 'Bind to a specific interface. (Use "0.0.0.0" for all interfaces)') do |t|
67
+ @interface = t
68
+ end
69
+ op.on('-s', '--servername SERVERNAME', 'Set servername for FTP server)',
70
+ "\tdefault = FTPd - Pure Ruby FTP server") do |t|
71
+ @servername = t
72
+ end
73
+ op.on('--tls [TYPE]', [:off, :explicit, :implicit],
74
+ 'Select TLS support (off, explicit, implicit)',
75
+ "\tdefault = off") do |t|
76
+ @tls = t
77
+ end
78
+ op.on('--eplf', 'LIST uses EPLF format') do |t|
79
+ @eplf = t
80
+ end
81
+ op.on('--read-only', 'Prohibit put, delete, rmdir, etc.') do |t|
82
+ @read_only = t
83
+ end
84
+ op.on('--auth [LEVEL]', [:user, :password, :account],
85
+ 'Set authorization level (user, password, account)',
86
+ "\tdefault = password") do |t|
87
+ @auth_level = t
88
+ end
89
+ op.on('-U', '--user NAME', 'User for authentication',
90
+ "\tdefaults to current user") do |t|
91
+ @user = t
92
+ end
93
+ op.on('-P', '--password PASS', 'Password for authentication',
94
+ "\tdefaults to empty string") do |t|
95
+ @password = t
96
+ end
97
+ op.on('-A', '--account P', 'Account for authentication',
98
+ "\tdefaults to empty string") do |t|
99
+ @account = t
100
+ end
101
+ op.on('--path PATH', 'Directory path for share',
102
+ "\tdefaults to random temp directory") do |t|
103
+ @path = t
104
+ end
105
+ op.on('--timeout SEC', Integer, 'Session idle timeout',
106
+ "\tdefaults to #{default_session_timeout}") do |t|
107
+ @session_timeout = t
108
+ end
109
+ op.on('-d', '--debug', 'Write server debug log to stdout') do |t|
110
+ @debug = t
111
+ end
112
+ op.on('--nat-ip IP', 'Set advertised passive mode IP') do |t|
113
+ @nat_ip = t
114
+ end
115
+ op.on('--ports MIN..MAX', 'Port numbers for passive mode sockets') do |v|
116
+ @passive_ports = Range.new(*v.split(/\.\./).map(&:to_i))
117
+ end
118
+ op.on("-h","--help","Show the full help screen.") do
119
+ puts opts
120
+ puts "\nExample:"
121
+ puts " #{__FILE__} -i 0.0.0.0 -p 21 -U ftp -P ftp --path /tmp/\n\n"
122
+ exit 0
123
+ end
124
+ end
125
+ end
126
+
127
+ def default_session_timeout
128
+ Ftpd::FtpServer::DEFAULT_SESSION_TIMEOUT
129
+ end
130
+
131
+ end
132
+ end
133
+
134
+ module FTPDrb
135
+
136
+ # The FTP server requires and instance of a _driver_ which can
137
+ # authenticate users and create a file system drivers for a given
138
+ # user.
139
+
140
+ class Driver
141
+
142
+ # Your driver's initialize method can be anything you need. Ftpd
143
+ # does not create an instance of your driver.
144
+
145
+ def initialize(user, password, account, data_dir, read_only)
146
+ @user = user
147
+ @password = password
148
+ @account = account
149
+ @data_dir = data_dir
150
+ @read_only = read_only
151
+ end
152
+
153
+ # Return true if the user should be allowed to log in.
154
+ # @param user [String]
155
+ # @param password [String]
156
+ # @param account [String]
157
+ # @return [Boolean]
158
+ #
159
+ # Depending upon the server's auth_level, some of these parameters
160
+ # may be nil. A parameter with a nil value is not required for
161
+ # authentication. Here are the parameters that are non-nil for
162
+ # each auth_level:
163
+ # * :user (user)
164
+ # * :password (user, password)
165
+ # * :account (user, password, account)
166
+
167
+ def authenticate(user, password, account)
168
+ user == @user &&
169
+ (password.nil? || password == @password) &&
170
+ (account.nil? || account == @account)
171
+ end
172
+
173
+ # Return the file system to use for a user.
174
+ # @param user [String]
175
+ # @return A file system driver that quacks like {Ftpd::DiskFileSystem}
176
+
177
+ def file_system(user)
178
+ if @read_only
179
+ Ftpd::ReadOnlyDiskFileSystem
180
+ else
181
+ Ftpd::DiskFileSystem.new(@data_dir)
182
+ end
183
+ end
184
+
185
+ end
186
+ end
187
+
188
+ module FTPDrb
189
+ class Server
190
+
191
+ include Ftpd::InsecureCertificate
192
+
193
+ def initialize(argv)
194
+ @args = Arguments.new(argv)
195
+ # Create a temp directory if path argument is not assigned
196
+ @args.path.nil? ? @data_dir = Ftpd::TempDir.make : @data_dir = @args.path
197
+ create_files if @args.path.nil?
198
+ @driver = Driver.new(user, password, account,
199
+ @data_dir, @args.read_only)
200
+ @server = Ftpd::FtpServer.new(@driver)
201
+ configure_server
202
+ @server.start
203
+ end
204
+
205
+ def run
206
+ puts "FTP server started. (Press CRL+C to stop it)"
207
+ connection_info.each do |key, value|
208
+ puts "#{key}: #{value}"
209
+ end
210
+ $stdout.flush
211
+ begin
212
+ loop{}
213
+ rescue Interrupt
214
+ puts "\nClosing FTP server..."
215
+ end
216
+ end
217
+
218
+ private
219
+ def configure_server
220
+ @server.server_name = @args.servername
221
+ @server.interface = @args.interface
222
+ @server.port = @args.port
223
+ @server.tls = @args.tls
224
+ @server.passive_ports = @args.passive_ports
225
+ @server.certfile_path = insecure_certfile_path
226
+ if @args.eplf
227
+ @server.list_formatter = Ftpd::ListFormat::Eplf
228
+ end
229
+ @server.auth_level = auth_level
230
+ @server.session_timeout = @args.session_timeout
231
+ @server.log = make_log
232
+ @server.nat_ip = @args.nat_ip
233
+ end
234
+
235
+ def auth_level
236
+ Ftpd.const_get("AUTH_#{@args.auth_level.upcase}")
237
+ end
238
+
239
+ def create_files
240
+ create_file 'README',
241
+ "Automatically created by FTPdrb.\n" +
242
+ "This file, and the directory it is in, will be deleted\n" +
243
+ "When ftpd server exits. Make sure your data is backed-up.\n"
244
+ end
245
+
246
+ def create_file(path, contents)
247
+ full_path = File.expand_path(path, @data_dir)
248
+ FileUtils.mkdir_p File.dirname(full_path)
249
+ File.open(full_path, 'w') do |file|
250
+ file.write contents
251
+ end
252
+ end
253
+
254
+ def connection_info
255
+ {
256
+ "Servername"=> @server.server_name,
257
+ "Interface" => @server.interface,
258
+ "Port" => @server.bound_port,
259
+ "User" => user,
260
+ "Pass" => "#{password if auth_level >= Ftpd::AUTH_PASSWORD}",
261
+ "Account" => "#{account if auth_level >= Ftpd::AUTH_ACCOUNT}",
262
+ "TLS" => @args.tls,
263
+ "Directory" => @data_dir,
264
+ "URI" => "ftp://#{user}:#{password}@#{IPAddr.new(@server.interface)}:#{@server.bound_port}",
265
+ "PID" => $$
266
+ }
267
+ end
268
+
269
+ def user
270
+ @args.user
271
+ end
272
+
273
+ def password
274
+ @args.password
275
+ end
276
+
277
+ def account
278
+ @args.account
279
+ end
280
+
281
+ def make_log
282
+ @args.debug && Logger.new($stdout)
283
+ end
284
+
285
+ end
286
+ end
287
+
288
+ FTPDrb::Server.new(ARGV).run if $0 == __FILE__
289
+
data/ftpd.gemspec CHANGED
@@ -2,18 +2,19 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: ftpd 2.0.0 ruby lib
5
+ # stub: ftpd 2.0.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "ftpd"
9
- s.version = "2.0.0"
9
+ s.version = "2.0.1"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Wayne Conrad"]
14
- s.date = "2016-06-02"
14
+ s.date = "2016-11-02"
15
15
  s.description = "ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS, IPV6, passive and active mode, and is unconditionally compliant per RFC-1123. It can be used as part of a test fixture or embedded in a program."
16
16
  s.email = "wconrad@yagni.com"
17
+ s.executables = ["ftpdrb"]
17
18
  s.extra_rdoc_files = [
18
19
  "LICENSE.md",
19
20
  "README.md"
@@ -28,6 +29,7 @@ Gem::Specification.new do |s|
28
29
  "README.md",
29
30
  "Rakefile",
30
31
  "VERSION",
32
+ "bin/ftpdrb",
31
33
  "doc/benchmarks.md",
32
34
  "doc/references.md",
33
35
  "doc/rfc-compliance.md",
@@ -244,7 +246,7 @@ Gem::Specification.new do |s|
244
246
  ]
245
247
  s.homepage = "http://github.com/wconrad/ftpd"
246
248
  s.licenses = ["MIT"]
247
- s.rubygems_version = "2.4.8"
249
+ s.rubygems_version = "2.5.1"
248
250
  s.summary = "Pure Ruby FTP server library"
249
251
 
250
252
  if s.respond_to? :specification_version then
@@ -253,7 +255,7 @@ Gem::Specification.new do |s|
253
255
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
254
256
  s.add_runtime_dependency(%q<memoizer>, ["~> 1.0"])
255
257
  s.add_development_dependency(%q<cucumber>, ["~> 2.0"])
256
- s.add_development_dependency(%q<double-bag-ftps>, ["~> 0.1"])
258
+ s.add_development_dependency(%q<double-bag-ftps>, [">= 0.1.3", "~> 0.1"])
257
259
  s.add_development_dependency(%q<jeweler>, ["~> 2.0"])
258
260
  s.add_development_dependency(%q<rake>, ["~> 11.1"])
259
261
  s.add_development_dependency(%q<redcarpet>, ["~> 3.1"])
@@ -264,7 +266,7 @@ Gem::Specification.new do |s|
264
266
  else
265
267
  s.add_dependency(%q<memoizer>, ["~> 1.0"])
266
268
  s.add_dependency(%q<cucumber>, ["~> 2.0"])
267
- s.add_dependency(%q<double-bag-ftps>, ["~> 0.1"])
269
+ s.add_dependency(%q<double-bag-ftps>, [">= 0.1.3", "~> 0.1"])
268
270
  s.add_dependency(%q<jeweler>, ["~> 2.0"])
269
271
  s.add_dependency(%q<rake>, ["~> 11.1"])
270
272
  s.add_dependency(%q<redcarpet>, ["~> 3.1"])
@@ -276,7 +278,7 @@ Gem::Specification.new do |s|
276
278
  else
277
279
  s.add_dependency(%q<memoizer>, ["~> 1.0"])
278
280
  s.add_dependency(%q<cucumber>, ["~> 2.0"])
279
- s.add_dependency(%q<double-bag-ftps>, ["~> 0.1"])
281
+ s.add_dependency(%q<double-bag-ftps>, [">= 0.1.3", "~> 0.1"])
280
282
  s.add_dependency(%q<jeweler>, ["~> 2.0"])
281
283
  s.add_dependency(%q<rake>, ["~> 11.1"])
282
284
  s.add_dependency(%q<redcarpet>, ["~> 3.1"])
@@ -54,7 +54,7 @@ module Ftpd
54
54
  s = gets_with_timeout(socket)
55
55
  throw :done if s.nil?
56
56
  s = s.chomp
57
- config.log.debug s
57
+ config.log.debug s.sub(/^PASS .*/, 'PASS **FILTERED**') # Filter real password
58
58
  s
59
59
  end
60
60
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ftpd
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wayne Conrad
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-02 00:00:00.000000000 Z
11
+ date: 2016-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: memoizer
@@ -42,6 +42,9 @@ dependencies:
42
42
  name: double-bag-ftps
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.3
45
48
  - - "~>"
46
49
  - !ruby/object:Gem::Version
47
50
  version: '0.1'
@@ -49,6 +52,9 @@ dependencies:
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 0.1.3
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
60
  version: '0.1'
@@ -154,7 +160,8 @@ description: ftpd is a pure Ruby FTP server library. It supports implicit and e
154
160
  TLS, IPV6, passive and active mode, and is unconditionally compliant per RFC-1123. It
155
161
  can be used as part of a test fixture or embedded in a program.
156
162
  email: wconrad@yagni.com
157
- executables: []
163
+ executables:
164
+ - ftpdrb
158
165
  extensions: []
159
166
  extra_rdoc_files:
160
167
  - LICENSE.md
@@ -169,6 +176,7 @@ files:
169
176
  - README.md
170
177
  - Rakefile
171
178
  - VERSION
179
+ - bin/ftpdrb
172
180
  - doc/benchmarks.md
173
181
  - doc/references.md
174
182
  - doc/rfc-compliance.md
@@ -402,7 +410,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
402
410
  version: '0'
403
411
  requirements: []
404
412
  rubyforge_project:
405
- rubygems_version: 2.4.8
413
+ rubygems_version: 2.5.1
406
414
  signing_key:
407
415
  specification_version: 4
408
416
  summary: Pure Ruby FTP server library