ftpd 0.11.0 → 0.12.0
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 +4 -4
- data/Changelog.md +7 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +28 -4
- data/VERSION +1 -1
- data/examples/foo.rb +114 -0
- data/ftpd.gemspec +56 -8
- data/lib/ftpd.rb +77 -31
- data/lib/ftpd/ascii_helper.rb +16 -0
- data/lib/ftpd/cmd_abor.rb +13 -0
- data/lib/ftpd/cmd_allo.rb +20 -0
- data/lib/ftpd/cmd_appe.rb +23 -0
- data/lib/ftpd/cmd_auth.rb +21 -0
- data/lib/ftpd/cmd_cdup.rb +16 -0
- data/lib/ftpd/cmd_cwd.rb +20 -0
- data/lib/ftpd/cmd_dele.rb +21 -0
- data/lib/ftpd/cmd_eprt.rb +23 -0
- data/lib/ftpd/cmd_epsv.rb +30 -0
- data/lib/ftpd/cmd_feat.rb +44 -0
- data/lib/ftpd/cmd_help.rb +29 -0
- data/lib/ftpd/cmd_list.rb +33 -0
- data/lib/ftpd/cmd_login.rb +60 -0
- data/lib/ftpd/cmd_mdtm.rb +27 -0
- data/lib/ftpd/cmd_mkd.rb +23 -0
- data/lib/ftpd/cmd_mode.rb +27 -0
- data/lib/ftpd/cmd_nlst.rb +27 -0
- data/lib/ftpd/cmd_noop.rb +14 -0
- data/lib/ftpd/cmd_opts.rb +14 -0
- data/lib/ftpd/cmd_pasv.rb +28 -0
- data/lib/ftpd/cmd_pbsz.rb +23 -0
- data/lib/ftpd/cmd_port.rb +28 -0
- data/lib/ftpd/cmd_prot.rb +34 -0
- data/lib/ftpd/cmd_pwd.rb +15 -0
- data/lib/ftpd/cmd_quit.rb +18 -0
- data/lib/ftpd/cmd_rein.rb +13 -0
- data/lib/ftpd/cmd_rename.rb +32 -0
- data/lib/ftpd/cmd_rest.rb +13 -0
- data/lib/ftpd/cmd_retr.rb +23 -0
- data/lib/ftpd/cmd_rmd.rb +22 -0
- data/lib/ftpd/cmd_site.rb +13 -0
- data/lib/ftpd/cmd_size.rb +21 -0
- data/lib/ftpd/cmd_smnt.rb +13 -0
- data/lib/ftpd/cmd_stat.rb +15 -0
- data/lib/ftpd/cmd_stor.rb +24 -0
- data/lib/ftpd/cmd_stou.rb +24 -0
- data/lib/ftpd/cmd_stru.rb +27 -0
- data/lib/ftpd/cmd_syst.rb +16 -0
- data/lib/ftpd/cmd_type.rb +28 -0
- data/lib/ftpd/command_handler.rb +91 -0
- data/lib/ftpd/command_handler_factory.rb +51 -0
- data/lib/ftpd/command_handlers.rb +60 -0
- data/lib/ftpd/command_loop.rb +77 -0
- data/lib/ftpd/data_connection_helper.rb +124 -0
- data/lib/ftpd/disk_file_system.rb +22 -6
- data/lib/ftpd/error.rb +4 -0
- data/lib/ftpd/file_system_helper.rb +67 -0
- data/lib/ftpd/ftp_server.rb +1 -1
- data/lib/ftpd/server.rb +1 -0
- data/lib/ftpd/session.rb +31 -749
- data/lib/ftpd/tls_server.rb +2 -0
- data/spec/server_spec.rb +66 -0
- metadata +81 -30
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdNlst < CommandHandler
|
6
|
+
|
7
|
+
def cmd_nlst(argument)
|
8
|
+
close_data_server_socket_when_done do
|
9
|
+
ensure_logged_in
|
10
|
+
ensure_file_system_supports :dir
|
11
|
+
path = list_path(argument)
|
12
|
+
path = File.expand_path(path, name_prefix)
|
13
|
+
transmit_file(name_list(path), 'A')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def name_list(path)
|
20
|
+
path_list(path).map do |path|
|
21
|
+
File.basename(path) + "\n"
|
22
|
+
end.join
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdPasv < CommandHandler
|
6
|
+
|
7
|
+
def cmd_pasv(argument)
|
8
|
+
ensure_logged_in
|
9
|
+
ensure_not_epsv_all
|
10
|
+
if data_server
|
11
|
+
reply "200 Already in passive mode"
|
12
|
+
else
|
13
|
+
interface = socket.addr[3]
|
14
|
+
self.data_server = TCPServer.new(interface, 0)
|
15
|
+
ip = data_server.addr[3]
|
16
|
+
port = data_server.addr[1]
|
17
|
+
quads = [
|
18
|
+
ip.scan(/\d+/),
|
19
|
+
port >> 8,
|
20
|
+
port & 0xff,
|
21
|
+
].flatten.join(',')
|
22
|
+
reply "227 Entering passive mode (#{quads})"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdPbsz < CommandHandler
|
6
|
+
|
7
|
+
def cmd_pbsz(buffer_size)
|
8
|
+
ensure_tls_supported
|
9
|
+
syntax_error unless buffer_size =~ /^\d+$/
|
10
|
+
buffer_size = buffer_size.to_i
|
11
|
+
unless socket.encrypted?
|
12
|
+
error "503 PBSZ must be preceded by AUTH"
|
13
|
+
end
|
14
|
+
unless buffer_size == 0
|
15
|
+
error "501 PBSZ=0"
|
16
|
+
end
|
17
|
+
reply "200 PBSZ=0"
|
18
|
+
self.protection_buffer_size_set = true
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
# The Data Port (PORT) command.
|
6
|
+
|
7
|
+
class CmdPort < CommandHandler
|
8
|
+
|
9
|
+
def cmd_port(argument)
|
10
|
+
ensure_logged_in
|
11
|
+
ensure_not_epsv_all
|
12
|
+
pieces = argument.split(/,/)
|
13
|
+
syntax_error unless pieces.size == 6
|
14
|
+
pieces.collect! do |s|
|
15
|
+
syntax_error unless s =~ /^\d{1,3}$/
|
16
|
+
i = s.to_i
|
17
|
+
syntax_error unless (0..255) === i
|
18
|
+
i
|
19
|
+
end
|
20
|
+
hostname = pieces[0..3].join('.')
|
21
|
+
port = pieces[4] << 8 | pieces[5]
|
22
|
+
set_active_mode_address hostname, port
|
23
|
+
reply "200 PORT command successful"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdProt < CommandHandler
|
6
|
+
|
7
|
+
def cmd_prot(level_arg)
|
8
|
+
level_code = level_arg.upcase
|
9
|
+
unless protection_buffer_size_set
|
10
|
+
error "503 PROT must be preceded by PBSZ"
|
11
|
+
end
|
12
|
+
level = DATA_CHANNEL_PROTECTION_LEVELS[level_code]
|
13
|
+
unless level
|
14
|
+
error "504 Unknown protection level"
|
15
|
+
end
|
16
|
+
unless level == :private
|
17
|
+
error "536 Unsupported protection level #{level}"
|
18
|
+
end
|
19
|
+
self.data_channel_protection_level = level
|
20
|
+
reply "200 Data protection level #{level_code}"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
DATA_CHANNEL_PROTECTION_LEVELS = {
|
26
|
+
'C'=>:clear,
|
27
|
+
'S'=>:safe,
|
28
|
+
'E'=>:confidential,
|
29
|
+
'P'=>:private
|
30
|
+
}
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/lib/ftpd/cmd_pwd.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
# The Quit (QUIT) command
|
6
|
+
|
7
|
+
class CmdQuit < CommandHandler
|
8
|
+
|
9
|
+
def cmd_quit(argument)
|
10
|
+
syntax_error if argument
|
11
|
+
ensure_logged_in
|
12
|
+
reply "221 Byebye"
|
13
|
+
self.logged_in = false
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdRename < CommandHandler
|
6
|
+
|
7
|
+
def cmd_rnfr(argument)
|
8
|
+
ensure_logged_in
|
9
|
+
ensure_file_system_supports :rename
|
10
|
+
syntax_error unless argument
|
11
|
+
from_path = File.expand_path(argument, name_prefix)
|
12
|
+
ensure_accessible from_path
|
13
|
+
ensure_exists from_path
|
14
|
+
@rename_from_path = from_path
|
15
|
+
reply '350 RNFR accepted; ready for destination'
|
16
|
+
expect 'rnto'
|
17
|
+
end
|
18
|
+
|
19
|
+
def cmd_rnto(argument)
|
20
|
+
ensure_logged_in
|
21
|
+
ensure_file_system_supports :rename
|
22
|
+
syntax_error unless argument
|
23
|
+
to_path = File.expand_path(argument, name_prefix)
|
24
|
+
ensure_accessible to_path
|
25
|
+
ensure_does_not_exist to_path
|
26
|
+
file_system.rename(@rename_from_path, to_path)
|
27
|
+
reply '250 Rename successful'
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdRetr < CommandHandler
|
6
|
+
|
7
|
+
def cmd_retr(argument)
|
8
|
+
close_data_server_socket_when_done do
|
9
|
+
ensure_logged_in
|
10
|
+
ensure_file_system_supports :read
|
11
|
+
path = argument
|
12
|
+
syntax_error unless path
|
13
|
+
path = File.expand_path(path, name_prefix)
|
14
|
+
ensure_accessible path
|
15
|
+
ensure_exists path
|
16
|
+
contents = file_system.read(path)
|
17
|
+
transmit_file contents
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/ftpd/cmd_rmd.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdRmd < CommandHandler
|
6
|
+
|
7
|
+
def cmd_rmd(argument)
|
8
|
+
syntax_error unless argument
|
9
|
+
ensure_logged_in
|
10
|
+
ensure_file_system_supports :rmdir
|
11
|
+
path = File.expand_path(argument, name_prefix)
|
12
|
+
ensure_accessible path
|
13
|
+
ensure_exists path
|
14
|
+
ensure_directory path
|
15
|
+
file_system.rmdir path
|
16
|
+
reply '250 RMD command successful'
|
17
|
+
end
|
18
|
+
alias cmd_xrmd :cmd_rmd
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdSize < CommandHandler
|
6
|
+
|
7
|
+
def cmd_size(path)
|
8
|
+
ensure_logged_in
|
9
|
+
ensure_file_system_supports :read
|
10
|
+
syntax_error unless path
|
11
|
+
path = File.expand_path(path, name_prefix)
|
12
|
+
ensure_accessible(path)
|
13
|
+
ensure_exists(path)
|
14
|
+
contents = file_system.read(path)
|
15
|
+
contents = (data_type == 'A') ? unix_to_nvt_ascii(contents) : contents
|
16
|
+
reply "213 #{contents.bytesize}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdStor < CommandHandler
|
6
|
+
|
7
|
+
def cmd_stor(argument)
|
8
|
+
close_data_server_socket_when_done do
|
9
|
+
ensure_logged_in
|
10
|
+
ensure_file_system_supports :write
|
11
|
+
path = argument
|
12
|
+
syntax_error unless path
|
13
|
+
path = File.expand_path(path, name_prefix)
|
14
|
+
ensure_accessible path
|
15
|
+
ensure_exists File.dirname(path)
|
16
|
+
contents = receive_file
|
17
|
+
file_system.write path, contents
|
18
|
+
reply "226 Transfer complete"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'command_handler'
|
2
|
+
|
3
|
+
module Ftpd
|
4
|
+
|
5
|
+
class CmdStou < CommandHandler
|
6
|
+
|
7
|
+
def cmd_stou(argument)
|
8
|
+
close_data_server_socket_when_done do
|
9
|
+
ensure_logged_in
|
10
|
+
ensure_file_system_supports :write
|
11
|
+
path = argument || 'ftpd'
|
12
|
+
path = File.expand_path(path, name_prefix)
|
13
|
+
path = unique_path(path)
|
14
|
+
ensure_accessible path
|
15
|
+
ensure_exists File.dirname(path)
|
16
|
+
contents = receive_file(File.basename(path))
|
17
|
+
file_system.write path, contents
|
18
|
+
reply "226 Transfer complete"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|