ftpd 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 402da43ab955cc1f7ec47d5080556ae09c85da9d
4
+ data.tar.gz: 6cde23e0be18f61b46e544809127337623ee55e9
5
+ SHA512:
6
+ metadata.gz: 2a434cd8438916a37900e2e6dc37a56c9b87d0628ed979f18e9ed8e1bba4f4a31252c012de9bfaa77b7078440212231ae1b073a848a5d80f5f43819277df0399
7
+ data.tar.gz: a9a3d3875c56cb3d8f5ba701291e9c2c22668b3d42534cdb34eb79fc81f781e49b00c42eb8cab8b5a13f67e47e2b846ddb93e7f8faf38086b4b14205a10118f1
data/Changelog.md CHANGED
@@ -2,6 +2,14 @@ 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
+ ### 0.9.0
6
+
7
+ Enhancements
8
+
9
+ * Added example showing ftp used as a test harness with rspec
10
+ * Ignore LIST/NLST switches such as "-a"
11
+ * Support IPV6
12
+
5
13
  ### 0.8.0
6
14
 
7
15
  Administration
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gem 'memoizer', '~> 1.0.1'
5
5
  group :development do
6
6
  gem 'cucumber'
7
7
  gem 'double-bag-ftps'
8
- gem 'jeweler', '1.8.4'
8
+ gem 'jeweler'
9
9
  gem 'rake'
10
10
  gem 'redcarpet'
11
11
  gem 'rspec'
data/Gemfile.lock CHANGED
@@ -1,6 +1,7 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ addressable (2.3.5)
4
5
  builder (3.2.2)
5
6
  cucumber (1.3.8)
6
7
  builder (>= 2.1.2)
@@ -10,20 +11,49 @@ GEM
10
11
  multi_test (>= 0.0.2)
11
12
  diff-lcs (1.2.4)
12
13
  double-bag-ftps (0.1.1)
14
+ faraday (0.8.8)
15
+ multipart-post (~> 1.2.0)
13
16
  gherkin (2.12.1)
14
17
  multi_json (~> 1.3)
15
- git (1.2.5)
16
- jeweler (1.8.4)
18
+ git (1.2.6)
19
+ github_api (0.10.1)
20
+ addressable
21
+ faraday (~> 0.8.1)
22
+ hashie (>= 1.2)
23
+ multi_json (~> 1.4)
24
+ nokogiri (~> 1.5.2)
25
+ oauth2
26
+ hashie (2.0.5)
27
+ highline (1.6.19)
28
+ httpauth (0.2.0)
29
+ jeweler (1.8.7)
30
+ builder
17
31
  bundler (~> 1.0)
18
32
  git (>= 1.2.5)
33
+ github_api (= 0.10.1)
34
+ highline (>= 1.6.15)
35
+ nokogiri (= 1.5.10)
19
36
  rake
20
37
  rdoc
21
38
  json (1.8.0)
39
+ jwt (0.1.8)
40
+ multi_json (>= 1.5)
22
41
  memoizer (1.0.1)
23
42
  multi_json (1.8.0)
24
43
  multi_test (0.0.2)
44
+ multi_xml (0.5.5)
45
+ multipart-post (1.2.0)
46
+ nokogiri (1.5.10)
47
+ oauth2 (0.9.2)
48
+ faraday (~> 0.8)
49
+ httpauth (~> 0.2)
50
+ jwt (~> 0.1.4)
51
+ multi_json (~> 1.0)
52
+ multi_xml (~> 0.5)
53
+ rack (~> 1.2)
54
+ rack (1.5.2)
25
55
  rake (10.1.0)
26
- rdoc (4.0.0)
56
+ rdoc (4.0.1)
27
57
  json (~> 1.4)
28
58
  redcarpet (3.0.0)
29
59
  rspec (2.14.1)
@@ -43,7 +73,7 @@ PLATFORMS
43
73
  DEPENDENCIES
44
74
  cucumber
45
75
  double-bag-ftps
46
- jeweler (= 1.8.4)
76
+ jeweler
47
77
  memoizer (~> 1.0.1)
48
78
  rake
49
79
  redcarpet
data/README.md CHANGED
@@ -34,32 +34,35 @@ This is examples/hello_world.rb, a bare minimum FTP server. It allows
34
34
  any user/password, and serves files in a temporary directory. It
35
35
  binds to an ephemeral port on the local interface:
36
36
 
37
- require 'ftpd'
38
- require 'tmpdir'
37
+ ```ruby
38
+ require 'ftpd'
39
+ require 'tmpdir'
39
40
 
40
- class Driver
41
+ class Driver
41
42
 
42
- def initialize(temp_dir)
43
- @temp_dir = temp_dir
44
- end
43
+ def initialize(temp_dir)
44
+ @temp_dir = temp_dir
45
+ end
45
46
 
46
- def authenticate(user, password)
47
- true
48
- end
47
+ def authenticate(user, password)
48
+ true
49
+ end
49
50
 
50
- def file_system(user)
51
- Ftpd::DiskFileSystem.new(@temp_dir)
52
- end
51
+ def file_system(user)
52
+ Ftpd::DiskFileSystem.new(@temp_dir)
53
+ end
53
54
 
54
- end
55
+ end
55
56
 
56
- Dir.mktmpdir do |temp_dir|
57
- driver = Driver.new(temp_dir)
58
- server = Ftpd::FtpServer.new(driver)
59
- server.start
60
- puts "Server listening on port #{server.bound_port}"
61
- gets
62
- end
57
+ Dir.mktmpdir do |temp_dir|
58
+ driver = Driver.new(temp_dir)
59
+ server = Ftpd::FtpServer.new(driver)
60
+ server.interface = '127.0.0.1'
61
+ server.start
62
+ puts "Server listening on port #{server.bound_port}"
63
+ gets
64
+ end
65
+ ```
63
66
 
64
67
  A more full-featured example that allows TLS and takes options is in
65
68
  examples/example.rb
@@ -115,15 +118,17 @@ Here are the methods a file system may expose:
115
118
 
116
119
  Ftpd includes a disk based file system:
117
120
 
118
- class Driver
121
+ ```ruby
122
+ class Driver
119
123
 
120
- ...
124
+ ...
121
125
 
122
- def file_system(user)
123
- Ftpd::DiskFileSystem.new('/var/lib/ftp')
124
- end
126
+ def file_system(user)
127
+ Ftpd::DiskFileSystem.new('/var/lib/ftp')
128
+ end
125
129
 
126
- end
130
+ end
131
+ ```
127
132
 
128
133
  **Warning**: The DiskFileSystem allows file and directory modification
129
134
  including writing, renaming, deleting, etc. If you want a read-only
@@ -146,25 +151,30 @@ allows only the operations you want, or which mixes the predefined
146
151
  modules with your customizations, as in this silly example that allows
147
152
  uploads but then throws them away.
148
153
 
149
- class BlackHole
150
- def write(ftp_path, contents)
151
- end
152
- end
154
+ ```ruby
155
+ class BlackHole
156
+ def write(ftp_path, contents)
157
+ end
158
+ end
153
159
 
154
- class CustomDiskFileSystem
155
- include DiskFileSystem::Base
156
- include DiskFileSystem::Read
157
- include BlackHole
158
- end
160
+ class CustomDiskFileSystem
161
+ include DiskFileSystem::Base
162
+ include DiskFileSystem::Read
163
+ include BlackHole
164
+ end
165
+ ```
159
166
 
160
167
  ## Configuration
161
168
 
162
169
  Configuration is done via accessors on {Ftpd::FtpServer}. For
163
170
  example, to set the session timeout to 10 minutes:
164
171
 
165
- server = Ftpd::FtpServer.new(driver)
166
- server.session_timeout = 10 * 60
167
- server.start
172
+ ```ruby
173
+ server = Ftpd::FtpServer.new(driver)
174
+ server.session_timeout = 10 * 60
175
+ server.interface = '127.0.0.1'
176
+ server.start
177
+ ```
168
178
 
169
179
  You can set any of these attributes before starting the server:
170
180
 
@@ -233,6 +243,9 @@ To log to a file:
233
243
  * Implements [RFC-2389](http://tools.ietf.org/rfc/rfc2389.txt)
234
244
  (Feature negotiation mechanism for the File Transfer Protocol)
235
245
 
246
+ * Implements [RFC-2428](http://tools.ietf.org/rfc/rfc2428.txt) (FTP
247
+ Extensions for IPv6 and NATs)
248
+
236
249
  * Implements enough of
237
250
  [RFC-4217](http://tools.ietf.org/rfc/rfc4217.txt) (Securing FTP with
238
251
  TLS) to get by.
@@ -244,8 +257,8 @@ See [RFC Compliance](doc/rfc-compliance.md) for details
244
257
  The tests pass with these Rubies:
245
258
 
246
259
  * ruby-1.8.7-p371
247
- * ruby-1.9.3-p392
248
- * ruby-2.0.0-p0
260
+ * ruby-1.9.3-p448
261
+ * ruby-2.0.0-p247
249
262
 
250
263
  For Ruby 1.8, use an ftpd version before 0.8. In your Gemfile:
251
264
 
@@ -312,6 +325,17 @@ Wayne Conrad <wconrad@yagni.com>
312
325
  Thanks to Databill, LLC, which supported the creation of this library,
313
326
  and granted permission to donate it to the community.
314
327
 
328
+ ### Contributors
329
+
330
+ Among those who have improved ftpd are:
331
+
332
+ * Alfonso Cora
333
+ * Bjoern B. Dorra
334
+ * Larry. W. Cashdollar
335
+ * Michael de Silva
336
+
337
+ Thank you!
338
+
315
339
  ## See also
316
340
 
317
341
  * [Changelog](Changelog.md)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.0
1
+ 0.9.0
@@ -213,8 +213,8 @@ use over various network protocols, and the new response codes 522 and
213
213
  * [link](http://tools.ietf.org/rfc/rfc2428.txt)
214
214
 
215
215
  <pre>
216
- EPRT No --- Set active data connection over IPv4 or IPv6
217
- EPSV No --- Set passive data connection over IPv4 or IPv6
216
+ EPRT Yes 0.9.0 Set active data connection over IPv4 or IPv6
217
+ EPSV Yes 0.9.0 Set passive data connection over IPv4 or IPv6
218
218
  </pre>
219
219
 
220
220
  ##RFC-2577 - FTP Security Considerations
data/examples/example.rb CHANGED
@@ -5,6 +5,7 @@ unless $:.include?(File.dirname(__FILE__) + '/../lib')
5
5
  end
6
6
 
7
7
  require 'ftpd'
8
+ require 'ipaddr'
8
9
  require 'optparse'
9
10
 
10
11
  module Example
@@ -26,7 +27,7 @@ module Example
26
27
  attr_reader :user
27
28
 
28
29
  def initialize(argv)
29
- @interface = 'localhost'
30
+ @interface = '127.0.0.1'
30
31
  @tls = :explicit
31
32
  @port = 0
32
33
  @auth_level = 'password'
@@ -185,8 +186,6 @@ module Example
185
186
 
186
187
  private
187
188
 
188
- HOST = 'localhost'
189
-
190
189
  def auth_level
191
190
  Ftpd.const_get("AUTH_#{@args.auth_level.upcase}")
192
191
  end
@@ -210,10 +209,10 @@ module Example
210
209
  puts "Port: #{@server.bound_port}"
211
210
  puts "User: #{user.inspect}"
212
211
  puts "Pass: #{password.inspect}" if auth_level >= Ftpd::AUTH_PASSWORD
213
- puts "Acctount: #{account.inspect}" if auth_level >= Ftpd::AUTH_ACCOUNT
212
+ puts "Account: #{account.inspect}" if auth_level >= Ftpd::AUTH_ACCOUNT
214
213
  puts "TLS: #{@args.tls}"
215
214
  puts "Directory: #{@data_dir}"
216
- puts "URI: ftp://#{HOST}:#{@server.bound_port}"
215
+ puts "URI: ftp://#{connection_host}:#{@server.bound_port}"
217
216
  puts "PID: #{$$}"
218
217
  end
219
218
 
@@ -221,7 +220,7 @@ module Example
221
220
  command_path = '/tmp/connect-to-example-ftp-server.sh'
222
221
  File.open(command_path, 'w') do |file|
223
222
  file.puts "#!/bin/bash"
224
- file.puts "ftp $FTP_ARGS #{HOST} #{@server.bound_port}"
223
+ file.puts "ftp $FTP_ARGS #{connection_host} #{@server.bound_port}"
225
224
  end
226
225
  system("chmod +x #{command_path}")
227
226
  puts "Connection script written to #{command_path}"
@@ -253,6 +252,15 @@ module Example
253
252
  @args.debug && Logger.new($stdout)
254
253
  end
255
254
 
255
+ def connection_host
256
+ addr = IPAddr.new(@server.interface)
257
+ if addr.ipv6?
258
+ '::1'
259
+ else
260
+ '127.0.0.1'
261
+ end
262
+ end
263
+
256
264
  end
257
265
  end
258
266
 
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+
3
+ # Authour: Michael de Silva <michael@mwdesilva.com>
4
+ # CEO @ http://omakaselabs.com / Mentor @ http://railsphd.com
5
+ # https://twitter.com/bsodmike / https://github.com/bsodmike
6
+
7
+ # This is an example for using Ftpd as a means for spec driving
8
+ # interaction with a 'dummy' ftp server via RSpec. In this example we
9
+ # assume the client is implemented via `Fetcher::FTPFetcher`.
10
+
11
+ unless $:.include?(File.dirname(__FILE__) + '/../lib')
12
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
13
+ end
14
+
15
+ require 'net/ftp'
16
+ require 'ftpd'
17
+ require 'tmpdir'
18
+
19
+ # This is an example client spec driven via the use of Ftpd within the
20
+ # specs. The specs spawn a 'dummy' Ftpd server and ensure this client
21
+ # operates as expected.
22
+
23
+ module Fetcher
24
+
25
+ # This is the code under test, a simple fetcher that logs into an
26
+ # FTP site, changes to a directlry, and gets a list of files.
27
+
28
+ class FTPFetcher
29
+
30
+ # @param host [String] ftp host to connect to.
31
+ # @param user [String] username.
32
+ # @param pwd [String] password.
33
+ # @param dir [String] remote directory to change to.
34
+
35
+ def initialize(host, user, pwd, dir)
36
+ @host = host
37
+ @user = user
38
+ @pwd = pwd
39
+ @dir = dir
40
+ @ftp = Net::FTP.new
41
+ end
42
+
43
+ # @param port [Fixnum] port to connect to, 21 by default.
44
+ # @return [Array] list of files in the current directory.
45
+
46
+ def connect_and_list(port = 21)
47
+ @ftp.debug_mode = true if ENV['DEBUG'] == "true"
48
+ @ftp.passive = true
49
+ @ftp.connect @host, port
50
+ @ftp.login @user, @pwd
51
+ @ftp.chdir @dir
52
+ @ftp.nlst
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ describe Fetcher::FTPFetcher do
59
+
60
+ # This `Driver` tells Ftpd how to authenticate and how to interact
61
+ # with the file systme. In this example, the file system is
62
+ # read-only and contains a single file.
63
+
64
+ class Driver
65
+ def initialize
66
+ @data_dir = Dir.mktmpdir
67
+ at_exit {FileUtils.rm_rf(@data_dir)}
68
+ FileUtils.touch File.expand_path('report.txt', @data_dir)
69
+ end
70
+ def authenticate(user, pwd); true; end
71
+ def file_system(user); Ftpd::ReadOnlyDiskFileSystem.new(@data_dir); end
72
+ end
73
+
74
+ let(:server) do
75
+ server = Ftpd::FtpServer.new(Driver.new)
76
+ server.interface = "127.0.0.1"
77
+ server.start
78
+ server
79
+ end
80
+
81
+ let(:subject) do
82
+ Fetcher::FTPFetcher.new('127.0.0.1', 'user', 'password', '/')
83
+ end
84
+
85
+ describe "#connect_and_list" do
86
+
87
+ it "should connect to the FTP server and find 'report.txt' in the Array returned" do
88
+ result = subject.connect_and_list(server.bound_port)
89
+ expect(result).to include('report.txt')
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,54 @@
1
+ Feature: EPRT
2
+
3
+ As a programmer
4
+ I want good error messages
5
+ So that I can correct problems
6
+
7
+ Background:
8
+ Given the test server is started
9
+
10
+ Scenario: Port 1024
11
+ Given a successful login
12
+ Then the client successfully sends "EPRT |1|1.2.3.4|1024|"
13
+
14
+ Scenario: Port 1023; low ports disallowed
15
+ Given the test server disallows low data ports
16
+ And a successful login
17
+ When the client sends "EPRT |1|2.3.4.3|255|"
18
+ Then the server returns an unimplemented parameter error
19
+
20
+ Scenario: Port out of range
21
+ Given a successful login
22
+ When the client sends "EPRT |1|2.3.4.5|65536|"
23
+ Then the server returns an unimplemented parameter error
24
+
25
+ Scenario: Port 1023; low ports allowed
26
+ Given the test server allows low data ports
27
+ And a successful login
28
+ Then the client successfully sends "EPRT |1|2.3.4.3|255|"
29
+
30
+ Scenario: Not logged in
31
+ Given a successful connection
32
+ When the client sends "EPRT |1|2.3.4.5|6|"
33
+ Then the server returns a not logged in error
34
+
35
+ Scenario: Too few parts
36
+ Given a successful login
37
+ When the client sends "EPRT |1|2.3.4|"
38
+ Then the server returns a syntax error
39
+
40
+ Scenario: Too many parts
41
+ Given a successful login
42
+ When the client sends "EPRT |1|2.3.4|5|6|"
43
+ Then the server returns a syntax error
44
+
45
+ Scenario: Unknown network protocol
46
+ Given a successful login
47
+ When the client sends "EPRT |3|2.3.4.5|6|"
48
+ Then the server returns a network protocol not supported error
49
+
50
+ Scenario: After "EPSV ALL"
51
+ Given a successful login
52
+ Given the client successfully sends "EPSV ALL"
53
+ When the client sends "EPRT |1|2.3.4.5|6|"
54
+ Then the server sends a not allowed after epsv all error