ftpd 0.1.0 → 0.1.1
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 +6 -5
- data/Gemfile.lock +7 -5
- data/README.md +2 -2
- data/VERSION +1 -1
- data/ftpd.gemspec +23 -19
- data/lib/ftpd/exception_translator.rb +3 -0
- data/lib/ftpd/exceptions.rb +14 -6
- data/lib/ftpd/file_system_error_translator.rb +4 -0
- data/lib/ftpd/ftp_server.rb +38 -1
- data/lib/ftpd/insecure_certificate.rb +6 -0
- data/lib/ftpd/server.rb +19 -0
- data/lib/ftpd/temp_dir.rb +7 -0
- data/lib/ftpd/tls_server.rb +21 -5
- data/lib/ftpd/translate_exceptions.rb +25 -1
- data/rake_tasks/yard.rake +3 -0
- metadata +42 -25
data/Gemfile
CHANGED
@@ -3,9 +3,10 @@ source 'http://rubygems.org'
|
|
3
3
|
gem 'memoizer', '~> 1.0.1'
|
4
4
|
|
5
5
|
group :development do
|
6
|
-
gem 'cucumber'
|
7
|
-
gem 'double-bag-ftps'
|
8
|
-
gem 'jeweler'
|
9
|
-
gem 'rake'
|
10
|
-
gem 'rspec'
|
6
|
+
gem 'cucumber'
|
7
|
+
gem 'double-bag-ftps'
|
8
|
+
gem 'jeweler'
|
9
|
+
gem 'rake'
|
10
|
+
gem 'rspec'
|
11
|
+
gem 'yard'
|
11
12
|
end
|
data/Gemfile.lock
CHANGED
@@ -30,14 +30,16 @@ GEM
|
|
30
30
|
rspec-expectations (2.12.1)
|
31
31
|
diff-lcs (~> 1.1.3)
|
32
32
|
rspec-mocks (2.12.2)
|
33
|
+
yard (0.8.4.1)
|
33
34
|
|
34
35
|
PLATFORMS
|
35
36
|
ruby
|
36
37
|
|
37
38
|
DEPENDENCIES
|
38
|
-
cucumber
|
39
|
-
double-bag-ftps
|
40
|
-
jeweler
|
39
|
+
cucumber
|
40
|
+
double-bag-ftps
|
41
|
+
jeweler
|
41
42
|
memoizer (~> 1.0.1)
|
42
|
-
rake
|
43
|
-
rspec
|
43
|
+
rake
|
44
|
+
rspec
|
45
|
+
yard
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# FTPD
|
2
2
|
|
3
3
|
ftpd is a pure Ruby FTP server library. It supports implicit and
|
4
|
-
explicit TLS,
|
5
|
-
FTP daemon.
|
4
|
+
explicit TLS, and is suitable for use by a program such as a test
|
5
|
+
fixture or small FTP daemon.
|
6
6
|
|
7
7
|
## HELLO WORLD
|
8
8
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/ftpd.gemspec
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "ftpd"
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
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-
|
13
|
-
s.description = "ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS,
|
12
|
+
s.date = "2013-02-23"
|
13
|
+
s.description = "ftpd is a pure Ruby FTP server library. It supports implicit and explicit TLS, and is suitable for use by a program such as a test fixture or small FTP daemon."
|
14
14
|
s.email = "wconrad@yagni.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE.md",
|
@@ -102,6 +102,7 @@ Gem::Specification.new do |s|
|
|
102
102
|
"rake_tasks/jeweler.rake",
|
103
103
|
"rake_tasks/spec.rake",
|
104
104
|
"rake_tasks/test.rake",
|
105
|
+
"rake_tasks/yard.rake",
|
105
106
|
"sandbox/em-server.rb",
|
106
107
|
"spec/disk_file_system_spec.rb",
|
107
108
|
"spec/exception_translator_spec.rb",
|
@@ -111,7 +112,7 @@ Gem::Specification.new do |s|
|
|
111
112
|
s.homepage = "http://github.com/wconrad/ftpd"
|
112
113
|
s.licenses = ["MIT"]
|
113
114
|
s.require_paths = ["lib"]
|
114
|
-
s.rubygems_version = "1.8.
|
115
|
+
s.rubygems_version = "1.8.25"
|
115
116
|
s.summary = "Pure Ruby FTP server library"
|
116
117
|
|
117
118
|
if s.respond_to? :specification_version then
|
@@ -119,26 +120,29 @@ Gem::Specification.new do |s|
|
|
119
120
|
|
120
121
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
121
122
|
s.add_runtime_dependency(%q<memoizer>, ["~> 1.0.1"])
|
122
|
-
s.add_development_dependency(%q<cucumber>, ["
|
123
|
-
s.add_development_dependency(%q<double-bag-ftps>, ["
|
124
|
-
s.add_development_dependency(%q<jeweler>, ["
|
125
|
-
s.add_development_dependency(%q<rake>, ["
|
126
|
-
s.add_development_dependency(%q<rspec>, ["
|
123
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
124
|
+
s.add_development_dependency(%q<double-bag-ftps>, [">= 0"])
|
125
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
126
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
127
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
128
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
127
129
|
else
|
128
130
|
s.add_dependency(%q<memoizer>, ["~> 1.0.1"])
|
129
|
-
s.add_dependency(%q<cucumber>, ["
|
130
|
-
s.add_dependency(%q<double-bag-ftps>, ["
|
131
|
-
s.add_dependency(%q<jeweler>, ["
|
132
|
-
s.add_dependency(%q<rake>, ["
|
133
|
-
s.add_dependency(%q<rspec>, ["
|
131
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
132
|
+
s.add_dependency(%q<double-bag-ftps>, [">= 0"])
|
133
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
134
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
135
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
136
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
134
137
|
end
|
135
138
|
else
|
136
139
|
s.add_dependency(%q<memoizer>, ["~> 1.0.1"])
|
137
|
-
s.add_dependency(%q<cucumber>, ["
|
138
|
-
s.add_dependency(%q<double-bag-ftps>, ["
|
139
|
-
s.add_dependency(%q<jeweler>, ["
|
140
|
-
s.add_dependency(%q<rake>, ["
|
141
|
-
s.add_dependency(%q<rspec>, ["
|
140
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
141
|
+
s.add_dependency(%q<double-bag-ftps>, [">= 0"])
|
142
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
143
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
144
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
145
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
142
146
|
end
|
143
147
|
end
|
144
148
|
|
data/lib/ftpd/exceptions.rb
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
module Ftpd
|
2
2
|
|
3
|
+
# All errors (purposefully) generated by this library driver from
|
4
|
+
# this class.
|
5
|
+
|
3
6
|
class FtpServerError < StandardError ; end
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
+
# Any error that send a reply to the client raises a CommandError.
|
9
|
+
# The message is the text to send (e.g. "501 Syntax error"). This
|
10
|
+
# is typically not raised directly, but using the Error mixin.
|
11
|
+
|
12
|
+
class CommandError < FtpServerError ; end
|
13
|
+
|
14
|
+
# Any errors raised by a file system driver are (or are derived
|
15
|
+
# from) this class. See the mixin TranslateExceptions for an easy
|
16
|
+
# way to generate FileSystemError exceptions from other types of
|
17
|
+
# exceptions.
|
8
18
|
|
9
|
-
|
10
|
-
ftp_server_error :DriverError
|
11
|
-
ftp_server_error :FileSystemError
|
19
|
+
class FileSystemError < FtpServerError ; end
|
12
20
|
|
13
21
|
end
|
data/lib/ftpd/ftp_server.rb
CHANGED
@@ -3,10 +3,45 @@
|
|
3
3
|
module Ftpd
|
4
4
|
class FtpServer < TlsServer
|
5
5
|
|
6
|
-
|
6
|
+
# If truthy, emit debug information (such as replies received and
|
7
|
+
# responses sent) to the file named by #debug_path.
|
8
|
+
#
|
9
|
+
# Change to this attribute only take effect for new sessions.
|
10
|
+
|
7
11
|
attr_accessor :debug
|
12
|
+
|
13
|
+
# The path to which to write debug information. Defaults to
|
14
|
+
# '/dev/stdout'
|
15
|
+
#
|
16
|
+
# Change to this attribute only take effect for new sessions.
|
17
|
+
|
18
|
+
attr_accessor :debug_path
|
19
|
+
|
20
|
+
# The number of seconds to delay before replying. This is for
|
21
|
+
# testing, when you need to test, for example, client timeouts.
|
22
|
+
# Defaults to 0 (no delay).
|
23
|
+
#
|
24
|
+
# Change to this attribute only take effect for new sessions.
|
25
|
+
|
8
26
|
attr_accessor :response_delay
|
9
27
|
|
28
|
+
# Create a new FTP server. The server won't start until the
|
29
|
+
# #start method is called.
|
30
|
+
#
|
31
|
+
# @param driver A driver for the server's dynamic behavior such as
|
32
|
+
# authentication and file system access.
|
33
|
+
#
|
34
|
+
# The driver should expose these public methods:
|
35
|
+
#
|
36
|
+
# # Return truthy if the user/password should be allowed to
|
37
|
+
# # log in.
|
38
|
+
# authenticate(user, password)
|
39
|
+
#
|
40
|
+
# # Return the file system to use for a user. The file system
|
41
|
+
# # should expose the same public methods as
|
42
|
+
# # Ftpd::DiskFileSystem.
|
43
|
+
# def file_system(user)
|
44
|
+
|
10
45
|
def initialize(driver)
|
11
46
|
super()
|
12
47
|
@driver = driver
|
@@ -15,6 +50,8 @@ module Ftpd
|
|
15
50
|
@response_delay = 0
|
16
51
|
end
|
17
52
|
|
53
|
+
private
|
54
|
+
|
18
55
|
def session(socket)
|
19
56
|
Session.new(:socket => socket,
|
20
57
|
:driver => @driver,
|
@@ -1,6 +1,12 @@
|
|
1
1
|
module Ftpd
|
2
|
+
|
3
|
+
# This mixin provides an insecure SSL certificate. This certificate
|
4
|
+
# should only be used for testing.
|
5
|
+
|
2
6
|
module InsecureCertificate
|
3
7
|
|
8
|
+
# The path of an insecure SSL certificate.
|
9
|
+
|
4
10
|
def insecure_certfile_path
|
5
11
|
File.expand_path('../../insecure-test-cert.pem',
|
6
12
|
File.dirname(__FILE__))
|
data/lib/ftpd/server.rb
CHANGED
@@ -3,7 +3,17 @@ module Ftpd
|
|
3
3
|
|
4
4
|
include Memoizer
|
5
5
|
|
6
|
+
# The interface to bind to (e.g. "127.0.0.1", "0.0.0.0",
|
7
|
+
# "10.0.0.12", etc.). Defaults to "localhost"
|
8
|
+
# Changes made after #start have no effect.
|
9
|
+
|
6
10
|
attr_accessor :interface
|
11
|
+
|
12
|
+
# The port to bind to. Defaults to 0, which causes an ephemeral
|
13
|
+
# port to be used. When bound to an ephemeral port, use
|
14
|
+
# #bound_port to find out which port was actually bound to.
|
15
|
+
# Changes made after #start have no effect.
|
16
|
+
|
7
17
|
attr_accessor :port
|
8
18
|
|
9
19
|
def initialize
|
@@ -11,15 +21,24 @@ module Ftpd
|
|
11
21
|
@port = 0
|
12
22
|
end
|
13
23
|
|
24
|
+
# The port the server is bound to. Must be called until after
|
25
|
+
# #start is called.
|
26
|
+
|
14
27
|
def bound_port
|
15
28
|
@server_socket.addr[1]
|
16
29
|
end
|
17
30
|
|
31
|
+
# Start the server. This creates the server socket, and the
|
32
|
+
# thread to service it.
|
33
|
+
|
18
34
|
def start
|
19
35
|
@server_socket = make_server_socket
|
20
36
|
@server_thread = make_server_thread
|
21
37
|
end
|
22
38
|
|
39
|
+
# Stop the server. This closes the server socket, which in turn
|
40
|
+
# stops the thread.
|
41
|
+
|
23
42
|
def stop
|
24
43
|
@server_socket.close
|
25
44
|
end
|
data/lib/ftpd/temp_dir.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
module Ftpd
|
2
|
+
|
3
|
+
# Create temporary directories that will be removed when the program
|
4
|
+
# exits.
|
5
|
+
|
2
6
|
module TempDir
|
3
7
|
|
8
|
+
# Create a temporary directory, returning its path. When the
|
9
|
+
# program exists, the directory (and its contents) are removed.
|
10
|
+
|
4
11
|
def make
|
5
12
|
Dir.mktmpdir.tap do |path|
|
6
13
|
at_exit do
|
data/lib/ftpd/tls_server.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
1
|
module Ftpd
|
2
2
|
class TlsServer < Server
|
3
3
|
|
4
|
+
# Whether or not to do TLS, and which flavor.
|
5
|
+
#
|
6
|
+
# One of:
|
7
|
+
# * :off
|
8
|
+
# * :explicit
|
9
|
+
# * :implicit
|
10
|
+
#
|
11
|
+
# Defaults to :off
|
12
|
+
#
|
13
|
+
# Changes made after #start have no effect. If TLS is enabled,
|
14
|
+
# then #certfile_path must be set.
|
15
|
+
|
4
16
|
attr_accessor :tls
|
17
|
+
|
18
|
+
# The path of the SSL certificate to use for TLS.
|
19
|
+
# Changes made after #start have no effect.
|
20
|
+
|
5
21
|
attr_accessor :certfile_path
|
6
22
|
|
23
|
+
# Create a new TLS server.
|
24
|
+
|
7
25
|
def initialize
|
8
26
|
super
|
9
27
|
@tls = :off
|
10
|
-
if tls_enabled?
|
11
|
-
unless @certfile_path
|
12
|
-
raise ArgumentError, ":certfile required if tls enabled"
|
13
|
-
end
|
14
|
-
end
|
15
28
|
end
|
16
29
|
|
17
30
|
private
|
@@ -34,6 +47,9 @@ module Ftpd
|
|
34
47
|
end
|
35
48
|
|
36
49
|
def ssl_context
|
50
|
+
unless @certfile_path
|
51
|
+
raise ArgumentError, ":certfile required if tls enabled"
|
52
|
+
end
|
37
53
|
context = OpenSSL::SSL::SSLContext.new
|
38
54
|
File.open(@certfile_path) do |certfile|
|
39
55
|
context.cert = OpenSSL::X509::Certificate.new(certfile)
|
@@ -1,6 +1,30 @@
|
|
1
1
|
module Ftpd
|
2
2
|
|
3
|
-
# This module
|
3
|
+
# This module tranlates exceptions to FileSystemError exceptions.
|
4
|
+
#
|
5
|
+
# A disk file system (such as Ftpd::DiskFileSystem) is expected to
|
6
|
+
# raise only FileSystemError exceptions, but many common operations
|
7
|
+
# result in other exceptions such as SystemCallError. This module
|
8
|
+
# aids a disk driver in translating exceptions to FileSystemError
|
9
|
+
# exceptions.
|
10
|
+
#
|
11
|
+
# In your file system, driver, include this module:
|
12
|
+
#
|
13
|
+
# module MyDiskDriver
|
14
|
+
# include Ftpd::TranslateExceptions
|
15
|
+
#
|
16
|
+
# in your constructor, register the exceptions that should be translated:
|
17
|
+
#
|
18
|
+
# def initialize
|
19
|
+
# translate_exception SystemCallError
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# And register methods for translation:
|
23
|
+
#
|
24
|
+
# def read(ftp_path)
|
25
|
+
# ...
|
26
|
+
# end
|
27
|
+
# translate_exceptions :read
|
4
28
|
|
5
29
|
module TranslateExceptions
|
6
30
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ftpd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: memoizer
|
@@ -32,83 +32,99 @@ dependencies:
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
|
-
- -
|
35
|
+
- - ! '>='
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version:
|
37
|
+
version: '0'
|
38
38
|
type: :development
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
|
-
- -
|
43
|
+
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version:
|
45
|
+
version: '0'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: double-bag-ftps
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - ! '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 0
|
53
|
+
version: '0'
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
none: false
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0
|
61
|
+
version: '0'
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: jeweler
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
65
65
|
none: false
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ! '>='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: '0'
|
70
70
|
type: :development
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
77
|
+
version: '0'
|
78
78
|
- !ruby/object:Gem::Dependency
|
79
79
|
name: rake
|
80
80
|
requirement: !ruby/object:Gem::Requirement
|
81
81
|
none: false
|
82
82
|
requirements:
|
83
|
-
- -
|
83
|
+
- - ! '>='
|
84
84
|
- !ruby/object:Gem::Version
|
85
|
-
version:
|
85
|
+
version: '0'
|
86
86
|
type: :development
|
87
87
|
prerelease: false
|
88
88
|
version_requirements: !ruby/object:Gem::Requirement
|
89
89
|
none: false
|
90
90
|
requirements:
|
91
|
-
- -
|
91
|
+
- - ! '>='
|
92
92
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
93
|
+
version: '0'
|
94
94
|
- !ruby/object:Gem::Dependency
|
95
95
|
name: rspec
|
96
96
|
requirement: !ruby/object:Gem::Requirement
|
97
97
|
none: false
|
98
98
|
requirements:
|
99
|
-
- -
|
99
|
+
- - ! '>='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
101
|
+
version: '0'
|
102
102
|
type: :development
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
|
-
- -
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: yard
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
108
124
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
125
|
+
version: '0'
|
110
126
|
description: ftpd is a pure Ruby FTP server library. It supports implicit and explicit
|
111
|
-
TLS,
|
127
|
+
TLS, and is suitable for use by a program such as a test fixture or small FTP daemon.
|
112
128
|
email: wconrad@yagni.com
|
113
129
|
executables: []
|
114
130
|
extensions: []
|
@@ -201,6 +217,7 @@ files:
|
|
201
217
|
- rake_tasks/jeweler.rake
|
202
218
|
- rake_tasks/spec.rake
|
203
219
|
- rake_tasks/test.rake
|
220
|
+
- rake_tasks/yard.rake
|
204
221
|
- sandbox/em-server.rb
|
205
222
|
- spec/disk_file_system_spec.rb
|
206
223
|
- spec/exception_translator_spec.rb
|
@@ -221,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
221
238
|
version: '0'
|
222
239
|
segments:
|
223
240
|
- 0
|
224
|
-
hash:
|
241
|
+
hash: 564915969
|
225
242
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
226
243
|
none: false
|
227
244
|
requirements:
|
@@ -230,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
230
247
|
version: '0'
|
231
248
|
requirements: []
|
232
249
|
rubyforge_project:
|
233
|
-
rubygems_version: 1.8.
|
250
|
+
rubygems_version: 1.8.25
|
234
251
|
signing_key:
|
235
252
|
specification_version: 3
|
236
253
|
summary: Pure Ruby FTP server library
|