ftpd 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -0
- data/Changelog.md +25 -1
- data/README.md +91 -10
- data/VERSION +1 -1
- data/doc/rfc-compliance.md +20 -20
- data/examples/example.rb +69 -11
- data/features/example/read_only.feature +63 -0
- data/features/ftp_server/append.feature +94 -0
- data/features/ftp_server/command_errors.feature +0 -2
- data/features/ftp_server/concurrent_sessions.feature +1 -1
- data/features/ftp_server/debug.feature +4 -2
- data/features/ftp_server/delete.feature +1 -1
- data/features/ftp_server/get.feature +1 -1
- data/features/ftp_server/get_tls.feature +2 -1
- data/features/ftp_server/implicit_tls.feature +2 -1
- data/features/ftp_server/invertability.feature +15 -0
- data/features/ftp_server/list.feature +1 -1
- data/features/ftp_server/list_tls.feature +2 -1
- data/features/ftp_server/login_auth_level_account.feature +51 -0
- data/features/ftp_server/{login.feature → login_auth_level_password.feature} +4 -8
- data/features/ftp_server/login_auth_level_user.feature +31 -0
- data/features/ftp_server/mkdir.feature +1 -1
- data/features/ftp_server/name_list.feature +1 -1
- data/features/ftp_server/name_list_tls.feature +2 -1
- data/features/ftp_server/put.feature +1 -1
- data/features/ftp_server/put_tls.feature +2 -1
- data/features/ftp_server/put_unique.feature +1 -1
- data/features/ftp_server/rename.feature +1 -1
- data/features/ftp_server/rmdir.feature +1 -1
- data/features/ftp_server/step_definitions/debug.rb +1 -1
- data/features/ftp_server/step_definitions/test_server.rb +16 -15
- data/features/ftp_server/type.feature +11 -8
- data/features/step_definitions/append.rb +15 -0
- data/features/step_definitions/client_and_server_files.rb +2 -2
- data/features/step_definitions/client_files.rb +5 -0
- data/features/step_definitions/connect.rb +1 -1
- data/features/step_definitions/error_replies.rb +0 -4
- data/features/step_definitions/login.rb +30 -20
- data/features/step_definitions/server_files.rb +20 -7
- data/features/support/example_server.rb +10 -2
- data/features/support/test_client.rb +18 -0
- data/features/support/test_file_templates.rb +1 -1
- data/features/support/test_server.rb +25 -4
- data/ftpd.gemspec +11 -3
- data/lib/ftpd.rb +2 -0
- data/lib/ftpd/auth_levels.rb +9 -0
- data/lib/ftpd/disk_file_system.rb +48 -19
- data/lib/ftpd/ftp_server.rb +13 -10
- data/lib/ftpd/read_only_disk_file_system.rb +22 -0
- data/lib/ftpd/session.rb +62 -29
- data/spec/disk_file_system_spec.rb +30 -0
- metadata +12 -4
data/.yardopts
CHANGED
data/Changelog.md
CHANGED
@@ -1,8 +1,32 @@
|
|
1
|
-
### 0.
|
1
|
+
### 0.4.0
|
2
2
|
|
3
3
|
Enhancements
|
4
4
|
|
5
5
|
* Improved driver and file-system documentation.
|
6
|
+
* Added {Ftpd::ReadOnlyDiskFileSystem read only disk file system}
|
7
|
+
* Example can be run with a read-only file system
|
8
|
+
* Supports three different levels of authentication:
|
9
|
+
* User
|
10
|
+
* User + Password
|
11
|
+
* User + Password + Account
|
12
|
+
* Added --auth switch to the example to select the authentication
|
13
|
+
level.
|
14
|
+
* Support APPE
|
15
|
+
* Support TYPE "A T" (ASCII/Telnet)
|
16
|
+
* Support TYPE "LOCAL 8"
|
17
|
+
* Added switches to example to set authentication values
|
18
|
+
* --user
|
19
|
+
* --password
|
20
|
+
* --account
|
21
|
+
|
22
|
+
API changes
|
23
|
+
|
24
|
+
* {Example::Driver#authenticate authenticate} now takes three
|
25
|
+
parameters (user, password, account). For compatability, it can be
|
26
|
+
defined to take only two, provided you are not doing account
|
27
|
+
authentication.
|
28
|
+
* Added {Ftpd::FtpServer#auth_level} option
|
29
|
+
* Added {Ftpd::DiskFileSystem::Append}
|
6
30
|
|
7
31
|
### 0.3.1
|
8
32
|
|
data/README.md
CHANGED
@@ -8,7 +8,8 @@ embedded in a program.
|
|
8
8
|
## A note about this README
|
9
9
|
|
10
10
|
This readme, and the other files, contains Yardoc markup, especially
|
11
|
-
for links to the API docs
|
11
|
+
for links to the API docs; those links don't display properly on
|
12
|
+
github. You'll find a properly rendered version
|
12
13
|
{http://rubydoc.info/gems/ftpd on rubydoc.info}
|
13
14
|
|
14
15
|
## HELLO WORLD
|
@@ -91,6 +92,77 @@ Here are the methods a file system may expose:
|
|
91
92
|
* {Ftpd::DiskFileSystem::List#dir dir}
|
92
93
|
* {Ftpd::DiskFileSystem::Rename#rename rename}
|
93
94
|
|
95
|
+
### DiskFileSystem
|
96
|
+
|
97
|
+
Ftpd includes a disk based file system:
|
98
|
+
|
99
|
+
class Driver
|
100
|
+
|
101
|
+
...
|
102
|
+
|
103
|
+
def file_system(user)
|
104
|
+
Ftpd::DiskFileSystem.new('/var/lib/ftp')
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
*Warning*: The DiskFileSystem allows file and directory modification
|
110
|
+
including writing, renaming, deleting, etc. If you want a read-only
|
111
|
+
file system, then use {Ftpd::ReadOnlyDiskFileSystem} instead.
|
112
|
+
|
113
|
+
The DiskFileSystem is composed out of modules:
|
114
|
+
|
115
|
+
* {Ftpd::DiskFileSystem::Base Base} - You will need this
|
116
|
+
* {Ftpd::DiskFileSystem::Delete Delete} - File deletion
|
117
|
+
* {Ftpd::DiskFileSystem::List List} - Directory listing
|
118
|
+
* {Ftpd::DiskFileSystem::Mkdir Mkdir} - Directory creation
|
119
|
+
* {Ftpd::DiskFileSystem::Read Read} - File reading
|
120
|
+
* {Ftpd::DiskFileSystem::Rename Rename} - File renaming
|
121
|
+
* {Ftpd::DiskFileSystem::Rmdir Rmdir} - Directory removal
|
122
|
+
* {Ftpd::DiskFileSystem::Write Write} - File writing
|
123
|
+
|
124
|
+
For example, to create a custom file system that allows reading and
|
125
|
+
writing only, then:
|
126
|
+
|
127
|
+
class CustomDiskFileSystem
|
128
|
+
include DiskFileSystem::Base
|
129
|
+
include DiskFileSystem::List
|
130
|
+
include DiskFileSystem::Read
|
131
|
+
include DiskFileSystem::Write
|
132
|
+
end
|
133
|
+
|
134
|
+
class Driver
|
135
|
+
|
136
|
+
...
|
137
|
+
|
138
|
+
def file_system(user)
|
139
|
+
CustomDiskFileSystem('/var/lib/ftp')
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
## LIST output format
|
145
|
+
|
146
|
+
By default, the LIST command uses Unix "ls -l" formatting:
|
147
|
+
|
148
|
+
-rw-r--r-- 1 user group 1234 Mar 3 08:38 foo
|
149
|
+
|
150
|
+
To switch to
|
151
|
+
{http://cr.yp.to/ftp/list/eplf.html Easily Parsed LIST format (EPLF)}
|
152
|
+
format:
|
153
|
+
|
154
|
+
ftp_server.list_formatter = Ftpd::ListFormat::Eplf
|
155
|
+
|
156
|
+
To create your own custom formatter, create a class with these
|
157
|
+
methods:
|
158
|
+
|
159
|
+
* {Ftpd::ListFormat::Ls#initialize initialize}
|
160
|
+
* {Ftpd::ListFormat::Ls#to_s to_s}
|
161
|
+
|
162
|
+
And register your class with the ftp_server before starting it:
|
163
|
+
|
164
|
+
ftp_server.list_formatter = MyListFormatter
|
165
|
+
|
94
166
|
## DEBUGGING
|
95
167
|
|
96
168
|
Ftpd can write debugging information (essentially a transcript of its
|
@@ -111,16 +183,25 @@ output without having to change any code.
|
|
111
183
|
|
112
184
|
## LIMITATIONS
|
113
185
|
|
114
|
-
Ftpd is not
|
115
|
-
to get by. {file:doc/rfc.md Here} is a list of RFCs, indicating
|
116
|
-
much of each Ftpd complies with.
|
186
|
+
Ftpd is not fully RFC compliant. It does most of RFC969, and enough
|
187
|
+
TLS to get by. {file:doc/rfc.md Here} is a list of RFCs, indicating
|
188
|
+
how much of each Ftpd complies with.
|
189
|
+
|
190
|
+
RFC does not meet the following
|
191
|
+
[RFC-1123](http://tools.ietf.org/rfc/rfc1123.txt) "MUST" requrements.
|
192
|
+
If FTPD met these requirements, but did not meet the "SHOULD"
|
193
|
+
requirements, it would be "conditionally compliant":
|
194
|
+
|
195
|
+
* Server-FTP handle Telnet options
|
196
|
+
* Support command STAT
|
197
|
+
|
198
|
+
RFC does not meet the following
|
199
|
+
[RFC-1123](http://tools.ietf.org/rfc/rfc1123.txt)
|
200
|
+
"SHOULD" requrements. If FTPD met both the "MUST" and the "SHOULD"
|
201
|
+
requirements, it would be "unconditionally compliant":
|
117
202
|
|
118
|
-
|
119
|
-
|
120
|
-
That's because the interface IP is used both for binding server ports,
|
121
|
-
_and_ for advertising to the client which IP to connect to. Binding
|
122
|
-
to 0.0.0.0 will work fine, but when the client tries to connect to
|
123
|
-
0.0.0.0, it won't get to the server.
|
203
|
+
* Idle timeout in server-FTP
|
204
|
+
* Configurable idle timeout
|
124
205
|
|
125
206
|
## RUBY COMPATABILITY
|
126
207
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/doc/rfc-compliance.md
CHANGED
@@ -22,10 +22,10 @@ pyftpdlib is what every FTP library wants to be when it grows up.
|
|
22
22
|
Commands supported:
|
23
23
|
|
24
24
|
ABOR No --- Abort transfer
|
25
|
-
ACCT
|
25
|
+
ACCT Yes 0.4.0 Specify user's account
|
26
26
|
ALLO Yes 0.2.0 Allocate storage space
|
27
27
|
Treated as a NOOP
|
28
|
-
APPE
|
28
|
+
APPE Yes 0.4.0 Append to file
|
29
29
|
CDUP Yes 0.1.0 Change to parent directory
|
30
30
|
CWD Yes 0.1.0 Change working directory
|
31
31
|
DELE Yes 0.1.0 Delete file
|
@@ -87,18 +87,18 @@ with "C" where FTPD complies, or "E" where compliance is not required.
|
|
87
87
|
| |T|D|Y|O|O|t
|
88
88
|
FEATURE |SECTION | | | |T|T|e
|
89
89
|
-------------------------------------------|---------------|-|-|-|-|-|--
|
90
|
-
Implement TYPE T if same as TYPE N |4.1.2.2 | |x| | | |
|
91
|
-
File/Record transform invertible if poss. |4.1.2.4 | |x| | | |
|
90
|
+
Implement TYPE T if same as TYPE N |4.1.2.2 | |x| | | | C
|
91
|
+
File/Record transform invertible if poss. |4.1.2.4 | |x| | | | C
|
92
92
|
Server-FTP implement PASV |4.1.2.6 |x| | | | | C
|
93
93
|
PASV is per-transfer |4.1.2.6 |x| | | | | C
|
94
94
|
NLST reply usable in RETR cmds |4.1.2.7 |x| | | | | C
|
95
95
|
Implied type for LIST and NLST |4.1.2.7 | |x| | | | C
|
96
|
-
SITE cmd for non-standard features |4.1.2.8 | |x| | | |
|
96
|
+
SITE cmd for non-standard features |4.1.2.8 | |x| | | | C
|
97
97
|
STOU cmd return pathname as specified |4.1.2.9 |x| | | | | C
|
98
98
|
Use TCP READ boundaries on control conn. |4.1.2.10 | | | | |x| C
|
99
99
|
Server-FTP send only correct reply format |4.1.2.11 |x| | | | | C
|
100
100
|
Server-FTP use defined reply code if poss. |4.1.2.11 | |x| | | | C
|
101
|
-
New reply code following Section 4.2 |4.1.2.11 | | |x| | |
|
101
|
+
New reply code following Section 4.2 |4.1.2.11 | | |x| | | E
|
102
102
|
Default data port same IP addr as ctl conn |4.1.2.12 |x| | | | | C
|
103
103
|
Server-FTP handle Telnet options |4.1.2.12 |x| | | | |
|
104
104
|
Handle "Experimental" directory cmds |4.1.3.1 | |x| | | | C
|
@@ -109,30 +109,30 @@ Sender assume 110 replies are synchronous |4.1.3.4 | | | | |x|
|
|
109
109
|
| | | | | | |
|
110
110
|
Support TYPE: | | | | | | |
|
111
111
|
ASCII - Non-Print (AN) |4.1.2.13 |x| | | | | C
|
112
|
-
ASCII - Telnet (AT) -- if same as AN |4.1.2.2 | |x| | | |
|
113
|
-
ASCII - Carriage Control (AC) |959 3.1.1.5.2 | | |x| | |
|
114
|
-
EBCDIC - (any form) |959 3.1.1.2 | | |x| | |
|
112
|
+
ASCII - Telnet (AT) -- if same as AN |4.1.2.2 | |x| | | | C
|
113
|
+
ASCII - Carriage Control (AC) |959 3.1.1.5.2 | | |x| | | E
|
114
|
+
EBCDIC - (any form) |959 3.1.1.2 | | |x| | | E
|
115
115
|
IMAGE |4.1.2.1 |x| | | | | C
|
116
|
-
LOCAL 8 |4.1.2.1 |x| | | | |
|
117
|
-
LOCAL m |4.1.2.1 | | |x| | |2
|
116
|
+
LOCAL 8 |4.1.2.1 |x| | | | | C
|
117
|
+
LOCAL m |4.1.2.1 | | |x| | |2 E
|
118
118
|
| | | | | | |
|
119
119
|
Support MODE: | | | | | | |
|
120
120
|
Stream |4.1.2.13 |x| | | | | C
|
121
|
-
Block |959 3.4.2 | | |x| | |
|
121
|
+
Block |959 3.4.2 | | |x| | | E
|
122
122
|
| | | | | | |
|
123
123
|
Support STRUCTURE: | | | | | | |
|
124
124
|
File |4.1.2.13 |x| | | | | C
|
125
125
|
Record |4.1.2.13 |x| | | | |3 E
|
126
|
-
Page |4.1.2.3 | | | |x| |
|
126
|
+
Page |4.1.2.3 | | | |x| | E
|
127
127
|
| | | | | | |
|
128
128
|
Support commands: | | | | | | |
|
129
129
|
USER |4.1.2.13 |x| | | | | C
|
130
130
|
PASS |4.1.2.13 |x| | | | | C
|
131
|
-
ACCT |4.1.2.13 |x| | | | |
|
131
|
+
ACCT |4.1.2.13 |x| | | | | C
|
132
132
|
CWD |4.1.2.13 |x| | | | | C
|
133
133
|
CDUP |4.1.2.13 |x| | | | | C
|
134
|
-
SMNT |959 5.3.1 | | |x| | |
|
135
|
-
REIN |959 5.3.1 | | |x| | |
|
134
|
+
SMNT |959 5.3.1 | | |x| | | E
|
135
|
+
REIN |959 5.3.1 | | |x| | | E
|
136
136
|
QUIT |4.1.2.13 |x| | | | | C
|
137
137
|
| | | | | | |
|
138
138
|
PORT |4.1.2.13 |x| | | | | C
|
@@ -144,19 +144,19 @@ Support commands: | | | | | | |
|
|
144
144
|
RETR |4.1.2.13 |x| | | | | C
|
145
145
|
STOR |4.1.2.13 |x| | | | | C
|
146
146
|
STOU |959 5.3.1 | | |x| | | C
|
147
|
-
APPE |4.1.2.13 |x| | | | |
|
147
|
+
APPE |4.1.2.13 |x| | | | | C
|
148
148
|
ALLO |959 5.3.1 | | |x| | | C
|
149
|
-
REST |959 5.3.1 | | |x| | |
|
149
|
+
REST |959 5.3.1 | | |x| | | E
|
150
150
|
RNFR |4.1.2.13 |x| | | | | C
|
151
151
|
RNTO |4.1.2.13 |x| | | | | C
|
152
|
-
ABOR |959 5.3.1 | | |x| | |
|
152
|
+
ABOR |959 5.3.1 | | |x| | | E
|
153
153
|
DELE |4.1.2.13 |x| | | | | C
|
154
154
|
RMD |4.1.2.13 |x| | | | | C
|
155
155
|
MKD |4.1.2.13 |x| | | | | C
|
156
156
|
PWD |4.1.2.13 |x| | | | | C
|
157
157
|
LIST |4.1.2.13 |x| | | | | C
|
158
158
|
NLST |4.1.2.13 |x| | | | | C
|
159
|
-
SITE |4.1.2.8 | | |x| | |
|
159
|
+
SITE |4.1.2.8 | | |x| | | E
|
160
160
|
STAT |4.1.2.13 |x| | | | |
|
161
161
|
SYST |4.1.2.13 |x| | | | | C
|
162
162
|
HELP |4.1.2.13 |x| | | | | C
|
data/examples/example.rb
CHANGED
@@ -13,15 +13,24 @@ module Example
|
|
13
13
|
|
14
14
|
class Arguments
|
15
15
|
|
16
|
+
attr_reader :account
|
17
|
+
attr_reader :auth_level
|
16
18
|
attr_reader :eplf
|
17
19
|
attr_reader :interface
|
20
|
+
attr_reader :password
|
18
21
|
attr_reader :port
|
22
|
+
attr_reader :read_only
|
19
23
|
attr_reader :tls
|
24
|
+
attr_reader :user
|
20
25
|
|
21
26
|
def initialize(argv)
|
22
27
|
@interface = 'localhost'
|
23
28
|
@tls = :explicit
|
24
29
|
@port = 0
|
30
|
+
@auth_level = 'password'
|
31
|
+
@user = ENV['LOGNAME']
|
32
|
+
@password = ''
|
33
|
+
@account = ''
|
25
34
|
op = option_parser
|
26
35
|
op.parse!(argv)
|
27
36
|
rescue OptionParser::ParseError => e
|
@@ -40,11 +49,32 @@ module Example
|
|
40
49
|
@interface = t
|
41
50
|
end
|
42
51
|
op.on('--tls [TYPE]', [:off, :explicit, :implicit],
|
43
|
-
'Select TLS support (off, explicit, implicit)'
|
52
|
+
'Select TLS support (off, explicit, implicit)',
|
53
|
+
'default = off') do |t|
|
44
54
|
@tls = t
|
45
55
|
end
|
46
56
|
op.on('--eplf', 'LIST uses EPLF format') do |t|
|
47
57
|
@eplf = t
|
58
|
+
end
|
59
|
+
op.on('--read-only', 'Prohibit put, delete, rmdir, etc.') do |t|
|
60
|
+
@read_only = t
|
61
|
+
end
|
62
|
+
op.on('--auth [LEVEL]', [:user, :password, :account],
|
63
|
+
'Set authorization level (user, password, account)',
|
64
|
+
'default = password') do |t|
|
65
|
+
@auth_level = t
|
66
|
+
end
|
67
|
+
op.on('-U', '--user NAME', 'User for authentication',
|
68
|
+
'defaults to current user') do |t|
|
69
|
+
@user = t
|
70
|
+
end
|
71
|
+
op.on('-P', '--password PW', 'Password for authentication',
|
72
|
+
'defaults to empty string') do |t|
|
73
|
+
@password = t
|
74
|
+
end
|
75
|
+
op.on('-A', '--account PW', 'Account for authentication',
|
76
|
+
'defaults to empty string') do |t|
|
77
|
+
@account = t
|
48
78
|
end
|
49
79
|
end
|
50
80
|
end
|
@@ -64,19 +94,32 @@ module Example
|
|
64
94
|
# Your driver's initialize method can be anything you need. Ftpd
|
65
95
|
# does not create an instance of your driver.
|
66
96
|
|
67
|
-
def initialize(user, password, data_dir)
|
97
|
+
def initialize(user, password, account, data_dir, read_only)
|
68
98
|
@user = user
|
69
99
|
@password = password
|
100
|
+
@account = account
|
70
101
|
@data_dir = data_dir
|
102
|
+
@read_only = read_only
|
71
103
|
end
|
72
104
|
|
73
105
|
# Return true if the user should be allowed to log in.
|
74
106
|
# @param user [String]
|
75
107
|
# @param password [String]
|
108
|
+
# @param account [String]
|
76
109
|
# @return [Boolean]
|
77
|
-
|
78
|
-
|
79
|
-
|
110
|
+
#
|
111
|
+
# Depending upon the server's auth_level, some of these parameters
|
112
|
+
# may be nil. A parameter with a nil value is not required for
|
113
|
+
# authentication. Here are the parameters that are non-nil for
|
114
|
+
# each auth_level:
|
115
|
+
# * :user (user)
|
116
|
+
# * :password (user, password)
|
117
|
+
# * :account (user, password, account)
|
118
|
+
|
119
|
+
def authenticate(user, password, account)
|
120
|
+
user == @user &&
|
121
|
+
(password.nil? || password == @password) &&
|
122
|
+
(account.nil? || account == @account)
|
80
123
|
end
|
81
124
|
|
82
125
|
# Return the file system to use for a user.
|
@@ -84,7 +127,11 @@ module Example
|
|
84
127
|
# @return A file system driver that quacks like {Ftpd::DiskFileSystem}
|
85
128
|
|
86
129
|
def file_system(user)
|
87
|
-
|
130
|
+
if @read_only
|
131
|
+
Ftpd::ReadOnlyDiskFileSystem
|
132
|
+
else
|
133
|
+
Ftpd::DiskFileSystem
|
134
|
+
end.new(@data_dir)
|
88
135
|
end
|
89
136
|
|
90
137
|
end
|
@@ -99,7 +146,8 @@ module Example
|
|
99
146
|
@args = Arguments.new(argv)
|
100
147
|
@data_dir = Ftpd::TempDir.make
|
101
148
|
create_files
|
102
|
-
@driver = Driver.new(user, password,
|
149
|
+
@driver = Driver.new(user, password, account,
|
150
|
+
@data_dir, @args.read_only)
|
103
151
|
@server = Ftpd::FtpServer.new(@driver)
|
104
152
|
@server.interface = @args.interface
|
105
153
|
@server.port = @args.port
|
@@ -108,6 +156,7 @@ module Example
|
|
108
156
|
if @args.eplf
|
109
157
|
@server.list_formatter = Ftpd::ListFormat::Eplf
|
110
158
|
end
|
159
|
+
@server.auth_level = auth_level
|
111
160
|
@server.start
|
112
161
|
display_connection_info
|
113
162
|
create_connection_script
|
@@ -121,6 +170,10 @@ module Example
|
|
121
170
|
|
122
171
|
HOST = 'localhost'
|
123
172
|
|
173
|
+
def auth_level
|
174
|
+
Ftpd.const_get("AUTH_#{@args.auth_level.upcase}")
|
175
|
+
end
|
176
|
+
|
124
177
|
def create_files
|
125
178
|
create_file 'README',
|
126
179
|
"This file, and the directory it is in, will go away\n"
|
@@ -138,8 +191,9 @@ module Example
|
|
138
191
|
def display_connection_info
|
139
192
|
puts "Interface: #{@server.interface}"
|
140
193
|
puts "Port: #{@server.bound_port}"
|
141
|
-
puts "User: #{user}"
|
142
|
-
puts "Pass: #{password}"
|
194
|
+
puts "User: #{user.inspect}"
|
195
|
+
puts "Pass: #{password.inspect}" if auth_level >= Ftpd::AUTH_PASSWORD
|
196
|
+
puts "Acctount: #{account.inspect}" if auth_level >= Ftpd::AUTH_ACCOUNT
|
143
197
|
puts "TLS: #{@args.tls}"
|
144
198
|
puts "Directory: #{@data_dir}"
|
145
199
|
puts "URI: ftp://#{HOST}:#{@server.bound_port}"
|
@@ -166,11 +220,15 @@ module Example
|
|
166
220
|
end
|
167
221
|
|
168
222
|
def user
|
169
|
-
|
223
|
+
@args.user
|
170
224
|
end
|
171
225
|
|
172
226
|
def password
|
173
|
-
|
227
|
+
@args.password
|
228
|
+
end
|
229
|
+
|
230
|
+
def account
|
231
|
+
@args.account
|
174
232
|
end
|
175
233
|
|
176
234
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
Feature: Example
|
2
|
+
|
3
|
+
As a programmer
|
4
|
+
I want to start a read-only server
|
5
|
+
So that nobody can modify the file system I expose
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given the example has argument "--read-only"
|
9
|
+
And the example server is started
|
10
|
+
|
11
|
+
Scenario: Fetch README
|
12
|
+
Given a successful login
|
13
|
+
When the client successfully gets text "README"
|
14
|
+
Then the local file "README" should match the remote file
|
15
|
+
|
16
|
+
Scenario: Fetch README
|
17
|
+
Given a successful login
|
18
|
+
When the client successfully gets text "README"
|
19
|
+
Then the local file "README" should match the remote file
|
20
|
+
|
21
|
+
Scenario: List
|
22
|
+
Given a successful login
|
23
|
+
When the client successfully lists the directory
|
24
|
+
Then the file list should be in long form
|
25
|
+
And the file list should contain "README"
|
26
|
+
|
27
|
+
Scenario: Name List
|
28
|
+
Given a successful login
|
29
|
+
When the client successfully name-lists the directory
|
30
|
+
Then the file list should be in short form
|
31
|
+
And the file list should contain "README"
|
32
|
+
|
33
|
+
Scenario: Put
|
34
|
+
Given a successful login
|
35
|
+
And the client has file "foo"
|
36
|
+
When the client puts text "foo"
|
37
|
+
Then the server returns an unimplemented command error
|
38
|
+
|
39
|
+
Scenario: Put unique
|
40
|
+
Given a successful login
|
41
|
+
And the client has file "foo"
|
42
|
+
When the client stores unique "foo"
|
43
|
+
Then the server returns an unimplemented command error
|
44
|
+
|
45
|
+
Scenario: Delete
|
46
|
+
Given a successful login
|
47
|
+
When the client deletes "README"
|
48
|
+
Then the server returns an unimplemented command error
|
49
|
+
|
50
|
+
Scenario: Mkdir
|
51
|
+
Given a successful login
|
52
|
+
When the client makes directory "foo"
|
53
|
+
Then the server returns an unimplemented command error
|
54
|
+
|
55
|
+
Scenario: Rename
|
56
|
+
Given a successful login
|
57
|
+
When the client renames "README" to "foo"
|
58
|
+
Then the server returns an unimplemented command error
|
59
|
+
|
60
|
+
Scenario: Rmdir
|
61
|
+
Given a successful login
|
62
|
+
When the client removes directory "foo"
|
63
|
+
Then the server returns an unimplemented command error
|