hrr_rb_sftp 0.1.0 → 0.2.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/README.md +23 -0
- data/lib/hrr_rb_sftp.rb +79 -1
- data/lib/hrr_rb_sftp/loggable.rb +34 -0
- data/lib/hrr_rb_sftp/protocol.rb +39 -33
- data/lib/hrr_rb_sftp/protocol/common.rb +6 -3
- data/lib/hrr_rb_sftp/protocol/common/data_types.rb +19 -0
- data/lib/hrr_rb_sftp/protocol/common/data_types/byte.rb +40 -0
- data/lib/hrr_rb_sftp/protocol/common/data_types/extension_pair.rb +41 -0
- data/lib/hrr_rb_sftp/protocol/common/data_types/extension_pairs.rb +42 -0
- data/lib/hrr_rb_sftp/protocol/common/data_types/string.rb +42 -0
- data/lib/hrr_rb_sftp/protocol/common/data_types/uint32.rb +40 -0
- data/lib/hrr_rb_sftp/protocol/common/data_types/uint64.rb +40 -0
- data/lib/hrr_rb_sftp/protocol/common/packets.rb +16 -0
- data/lib/hrr_rb_sftp/protocol/common/packets/001_ssh_fxp_init.rb +27 -0
- data/lib/hrr_rb_sftp/protocol/common/packets/002_ssh_fxp_version.rb +28 -0
- data/lib/hrr_rb_sftp/protocol/common/packets/packet.rb +96 -0
- data/lib/hrr_rb_sftp/protocol/version1.rb +11 -3
- data/lib/hrr_rb_sftp/protocol/version1/data_types.rb +15 -0
- data/lib/hrr_rb_sftp/protocol/version1/data_types/attrs.rb +91 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets.rb +72 -0
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/003_ssh_fxp_open.rb +94 -43
- data/lib/hrr_rb_sftp/protocol/version1/packets/004_ssh_fxp_close.rb +61 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/005_ssh_fxp_read.rb +72 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/006_ssh_fxp_write.rb +64 -0
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/007_ssh_fxp_lstat.rb +27 -9
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/008_ssh_fxp_fstat.rb +26 -9
- data/lib/hrr_rb_sftp/protocol/version1/packets/009_ssh_fxp_setstat.rb +92 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/010_ssh_fxp_fsetstat.rb +85 -0
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/011_ssh_fxp_opendir.rb +32 -11
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/012_ssh_fxp_readdir.rb +73 -49
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/013_ssh_fxp_remove.rb +27 -9
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/014_ssh_fxp_mkdir.rb +28 -10
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/015_ssh_fxp_rmdir.rb +32 -12
- data/lib/hrr_rb_sftp/protocol/version1/packets/016_ssh_fxp_realpath.rb +47 -0
- data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/017_ssh_fxp_stat.rb +27 -9
- data/lib/hrr_rb_sftp/protocol/version1/packets/101_ssh_fxp_status.rb +73 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/102_ssh_fxp_handle.rb +28 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/103_ssh_fxp_data.rb +28 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/104_ssh_fxp_name.rb +48 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/105_ssh_fxp_attrs.rb +28 -0
- data/lib/hrr_rb_sftp/protocol/version1/packets/packet.rb +57 -0
- data/lib/hrr_rb_sftp/protocol/version2.rb +11 -3
- data/lib/hrr_rb_sftp/protocol/version2/data_types.rb +13 -0
- data/lib/hrr_rb_sftp/protocol/version2/packets.rb +14 -0
- data/lib/hrr_rb_sftp/protocol/version2/packets/018_ssh_fxp_rename.rb +91 -0
- data/lib/hrr_rb_sftp/protocol/version3.rb +12 -3
- data/lib/hrr_rb_sftp/protocol/version3/data_types.rb +13 -0
- data/lib/hrr_rb_sftp/protocol/version3/extensions.rb +94 -0
- data/lib/hrr_rb_sftp/protocol/version3/extensions/extension.rb +58 -0
- data/lib/hrr_rb_sftp/protocol/version3/extensions/fsync_at_openssh_com.rb +59 -0
- data/lib/hrr_rb_sftp/protocol/version3/extensions/hardlink_at_openssh_com.rb +84 -0
- data/lib/hrr_rb_sftp/protocol/version3/extensions/lsetstat_at_openssh_com.rb +132 -0
- data/lib/hrr_rb_sftp/protocol/version3/extensions/posix_rename_at_openssh_com.rb +76 -0
- data/lib/hrr_rb_sftp/protocol/version3/packets.rb +19 -0
- data/lib/hrr_rb_sftp/protocol/version3/packets/014_ssh_fxp_mkdir.rb +75 -0
- data/lib/hrr_rb_sftp/protocol/version3/packets/019_ssh_fxp_readlink.rb +76 -0
- data/lib/hrr_rb_sftp/protocol/version3/packets/020_ssh_fxp_symlink.rb +76 -0
- data/lib/hrr_rb_sftp/protocol/version3/packets/101_ssh_fxp_status.rb +25 -0
- data/lib/hrr_rb_sftp/protocol/version3/packets/200_ssh_fxp_extended.rb +95 -0
- data/lib/hrr_rb_sftp/protocol/version3/packets/201_ssh_fxp_extended_reply.rb +60 -0
- data/lib/hrr_rb_sftp/receiver.rb +17 -2
- data/lib/hrr_rb_sftp/sender.rb +15 -1
- data/lib/hrr_rb_sftp/server.rb +43 -12
- data/lib/hrr_rb_sftp/version.rb +5 -1
- metadata +54 -47
- data/lib/hrr_rb_sftp/protocol/common/data_type.rb +0 -15
- data/lib/hrr_rb_sftp/protocol/common/data_type/byte.rb +0 -22
- data/lib/hrr_rb_sftp/protocol/common/data_type/extension_pair.rb +0 -23
- data/lib/hrr_rb_sftp/protocol/common/data_type/extension_pairs.rb +0 -24
- data/lib/hrr_rb_sftp/protocol/common/data_type/string.rb +0 -24
- data/lib/hrr_rb_sftp/protocol/common/data_type/uint32.rb +0 -22
- data/lib/hrr_rb_sftp/protocol/common/data_type/uint64.rb +0 -22
- data/lib/hrr_rb_sftp/protocol/common/packet.rb +0 -11
- data/lib/hrr_rb_sftp/protocol/common/packet/001_ssh_fxp_init.rb +0 -18
- data/lib/hrr_rb_sftp/protocol/common/packet/002_ssh_fxp_version.rb +0 -19
- data/lib/hrr_rb_sftp/protocol/common/packetable.rb +0 -72
- data/lib/hrr_rb_sftp/protocol/version1/data_type.rb +0 -11
- data/lib/hrr_rb_sftp/protocol/version1/data_type/attrs.rb +0 -54
- data/lib/hrr_rb_sftp/protocol/version1/packet.rb +0 -29
- data/lib/hrr_rb_sftp/protocol/version1/packet/004_ssh_fxp_close.rb +0 -44
- data/lib/hrr_rb_sftp/protocol/version1/packet/005_ssh_fxp_read.rb +0 -53
- data/lib/hrr_rb_sftp/protocol/version1/packet/006_ssh_fxp_write.rb +0 -46
- data/lib/hrr_rb_sftp/protocol/version1/packet/009_ssh_fxp_setstat.rb +0 -63
- data/lib/hrr_rb_sftp/protocol/version1/packet/010_ssh_fxp_fsetstat.rb +0 -48
- data/lib/hrr_rb_sftp/protocol/version1/packet/016_ssh_fxp_realpath.rb +0 -30
- data/lib/hrr_rb_sftp/protocol/version1/packet/101_ssh_fxp_status.rb +0 -29
- data/lib/hrr_rb_sftp/protocol/version1/packet/102_ssh_fxp_handle.rb +0 -19
- data/lib/hrr_rb_sftp/protocol/version1/packet/103_ssh_fxp_data.rb +0 -19
- data/lib/hrr_rb_sftp/protocol/version1/packet/104_ssh_fxp_name.rb +0 -33
- data/lib/hrr_rb_sftp/protocol/version1/packet/105_ssh_fxp_attrs.rb +0 -19
- data/lib/hrr_rb_sftp/protocol/version2/data_type.rb +0 -9
- data/lib/hrr_rb_sftp/protocol/version2/packet.rb +0 -11
- data/lib/hrr_rb_sftp/protocol/version2/packet/018_ssh_fxp_rename.rb +0 -70
- data/lib/hrr_rb_sftp/protocol/version3/data_type.rb +0 -9
- data/lib/hrr_rb_sftp/protocol/version3/packet.rb +0 -16
- data/lib/hrr_rb_sftp/protocol/version3/packet/014_ssh_fxp_mkdir.rb +0 -58
- data/lib/hrr_rb_sftp/protocol/version3/packet/019_ssh_fxp_readlink.rb +0 -57
- data/lib/hrr_rb_sftp/protocol/version3/packet/020_ssh_fxp_symlink.rb +0 -58
- data/lib/hrr_rb_sftp/protocol/version3/packet/101_ssh_fxp_status.rb +0 -31
- data/lib/hrr_rb_sftp/protocol/version3/packet/200_ssh_fxp_extended.rb +0 -34
- data/lib/hrr_rb_sftp/protocol/version3/packet/201_ssh_fxp_extended_reply.rb +0 -23
@@ -0,0 +1,42 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Common
|
4
|
+
module DataTypes
|
5
|
+
|
6
|
+
#
|
7
|
+
# This module provides methods to convert ::String value and binary string with its length each other.
|
8
|
+
#
|
9
|
+
module String
|
10
|
+
|
11
|
+
#
|
12
|
+
# Convert ::String value into binary string with its length.
|
13
|
+
#
|
14
|
+
# @param arg [::String] ::String value to be converted.
|
15
|
+
# @raise [::ArgumentError] When arg is not ::String value or length of arg is longer than 0xffff_ffff.
|
16
|
+
# @return [::String] Converted binary string with its length.
|
17
|
+
#
|
18
|
+
def self.encode arg
|
19
|
+
unless arg.kind_of? ::String
|
20
|
+
raise ArgumentError, "must be a kind of String, but got #{arg.inspect}"
|
21
|
+
end
|
22
|
+
if arg.bytesize > 0xffff_ffff
|
23
|
+
raise ArgumentError, "must be shorter than or equal to #{0xffff_ffff}, but got length #{arg.bytesize}"
|
24
|
+
end
|
25
|
+
[arg.bytesize, arg].pack("Na*")
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Convert binary string with its length into ::String value.
|
30
|
+
#
|
31
|
+
# @param io [::IO] ::IO instance that has buffer to be read.
|
32
|
+
# @return [::String] Converted UTF-8 ::String value.
|
33
|
+
#
|
34
|
+
def self.decode io
|
35
|
+
length = io.read(4).unpack("N")[0]
|
36
|
+
io.read(length).unpack("a*")[0].force_encoding(Encoding::UTF_8)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Common
|
4
|
+
module DataTypes
|
5
|
+
|
6
|
+
#
|
7
|
+
# This module provides methods to convert ::Integer value and 32-bit unsigned binary string each other.
|
8
|
+
#
|
9
|
+
module Uint32
|
10
|
+
|
11
|
+
#
|
12
|
+
# Convert ::Integer value into 32-bit unsigned binary string.
|
13
|
+
#
|
14
|
+
# @param arg [::Integer] ::Integer value to be converted.
|
15
|
+
# @raise [::ArgumentError] When arg is not between 0x0000_0000 and 0xffff_ffff.
|
16
|
+
# @return [::String] Converted 32-bit unsigned binary string.
|
17
|
+
#
|
18
|
+
def self.encode arg
|
19
|
+
case arg
|
20
|
+
when 0x0000_0000..0xffff_ffff
|
21
|
+
[arg].pack("N")
|
22
|
+
else
|
23
|
+
raise ArgumentError, "must be in #{0x0000_0000}..#{0xffff_ffff}, but got #{arg.inspect}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Convert 32-bit unsigned binary into ::Integer value.
|
29
|
+
#
|
30
|
+
# @param io [::IO] ::IO instance that has buffer to be read.
|
31
|
+
# @return [::Integer] Converted ::Integer value.
|
32
|
+
#
|
33
|
+
def self.decode io
|
34
|
+
io.read(4).unpack("N")[0]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Common
|
4
|
+
module DataTypes
|
5
|
+
|
6
|
+
#
|
7
|
+
# This module provides methods to convert ::Integer value and 64-bit unsigned binary string each other.
|
8
|
+
#
|
9
|
+
module Uint64
|
10
|
+
|
11
|
+
#
|
12
|
+
# Convert ::Integer value into 64-bit unsigned binary string.
|
13
|
+
#
|
14
|
+
# @param arg [::Integer] ::Integer value to be converted.
|
15
|
+
# @raise [::ArgumentError] When arg is not between 0x0000_0000_0000_0000 and 0xffff_ffff_ffff_ffff.
|
16
|
+
# @return [::String] Converted 64-bit unsigned binary string.
|
17
|
+
#
|
18
|
+
def self.encode arg
|
19
|
+
case arg
|
20
|
+
when 0x0000_0000_0000_0000..0xffff_ffff_ffff_ffff
|
21
|
+
[arg >> 32].pack("N") + [arg & 0x0000_0000_ffff_ffff].pack("N")
|
22
|
+
else
|
23
|
+
raise ArgumentError, "must be in #{0x0000_0000_0000_0000}..#{0xffff_ffff_ffff_ffff}, but got #{arg.inspect}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Convert 64-bit unsigned binary into ::Integer value.
|
29
|
+
#
|
30
|
+
# @param io [::IO] ::IO instance that has buffer to be read.
|
31
|
+
# @return [::Integer] Converted ::Integer value.
|
32
|
+
#
|
33
|
+
def self.decode io
|
34
|
+
(io.read(4).unpack("N")[0] << 32) + (io.read(4).unpack("N")[0])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Common
|
4
|
+
|
5
|
+
#
|
6
|
+
# This module implements SFTP protocol version independent packet types, formats, and responders.
|
7
|
+
#
|
8
|
+
module Packets
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require "hrr_rb_sftp/protocol/common/packets/packet"
|
15
|
+
require "hrr_rb_sftp/protocol/common/packets/001_ssh_fxp_init"
|
16
|
+
require "hrr_rb_sftp/protocol/common/packets/002_ssh_fxp_version"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Common
|
4
|
+
module Packets
|
5
|
+
|
6
|
+
#
|
7
|
+
# This class implements SFTP protocol version independent SSH_FXP_INIT packet type, format, and responder.
|
8
|
+
#
|
9
|
+
class SSH_FXP_INIT < Packet
|
10
|
+
|
11
|
+
#
|
12
|
+
# Represents SSH_FXP_INIT packet type.
|
13
|
+
#
|
14
|
+
TYPE = 1
|
15
|
+
|
16
|
+
#
|
17
|
+
# Represents SSH_FXP_INIT packet format.
|
18
|
+
#
|
19
|
+
FORMAT = [
|
20
|
+
[DataTypes::Byte, :"type" ],
|
21
|
+
[DataTypes::Uint32, :"version"],
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Common
|
4
|
+
module Packets
|
5
|
+
|
6
|
+
#
|
7
|
+
# This class implements SFTP protocol version independent SSH_FXP_VERSION packet type, format, and responder.
|
8
|
+
#
|
9
|
+
class SSH_FXP_VERSION < Packet
|
10
|
+
|
11
|
+
#
|
12
|
+
# Represents SSH_FXP_VERSION packet type.
|
13
|
+
#
|
14
|
+
TYPE = 2
|
15
|
+
|
16
|
+
#
|
17
|
+
# Represents SSH_FXP_VERSION packet format.
|
18
|
+
#
|
19
|
+
FORMAT = [
|
20
|
+
[DataTypes::Byte, :"type" ],
|
21
|
+
[DataTypes::Uint32, :"version" ],
|
22
|
+
[DataTypes::ExtensionPairs, :"extensions"],
|
23
|
+
]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Common
|
4
|
+
module Packets
|
5
|
+
|
6
|
+
#
|
7
|
+
# This class implements base packet operations and is to be inherited by each packet class.
|
8
|
+
#
|
9
|
+
class Packet
|
10
|
+
include Loggable
|
11
|
+
|
12
|
+
#
|
13
|
+
# Returns a new instance of a class that includes this module.
|
14
|
+
#
|
15
|
+
# @param logger [Logger] Logger.
|
16
|
+
#
|
17
|
+
def initialize logger: nil
|
18
|
+
self.logger = logger
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Encodes packet represented in Hash into payload represented in binary string.
|
23
|
+
#
|
24
|
+
# @param packet [Hash{Symbol=>Object}] Packets represented in Hash that key and value are field name and field value.
|
25
|
+
# @return [String] Encoded payload converted from packet.
|
26
|
+
#
|
27
|
+
def encode packet
|
28
|
+
log_debug { 'encoding packet: ' + packet.inspect }
|
29
|
+
format = common_format + conditional_format(packet)
|
30
|
+
format.map{ |data_type, field_name|
|
31
|
+
begin
|
32
|
+
field_value = packet[field_name]
|
33
|
+
data_type.encode field_value
|
34
|
+
rescue => e
|
35
|
+
log_debug { "'field_name', 'field_value': #{field_name.inspect}, #{field_value.inspect}" }
|
36
|
+
raise e
|
37
|
+
end
|
38
|
+
}.join
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Decodes payload represented in binary string into packet represented in Hash.
|
43
|
+
#
|
44
|
+
# @param payload [String] Payload of binary string.
|
45
|
+
# @param complementary_packet [Hash{Symbol=>Object}] Implied fields that activate conditional format. Now this is used for debug purpose.
|
46
|
+
# @return [String] Decoded packet represented in Hash that key and value are field name and field value.
|
47
|
+
#
|
48
|
+
def decode payload, complementary_packet={}
|
49
|
+
payload_io = StringIO.new payload
|
50
|
+
format = common_format
|
51
|
+
decoded_packet = decode_recursively(payload_io).inject(Hash.new){ |h, (k, v)| h.update({k => v}) }
|
52
|
+
if complementary_packet.any?
|
53
|
+
decoded_packet.merge! decode_recursively(payload_io, complementary_packet.to_a).inject(Hash.new){ |h, (k, v)| h.update({k => v}) }
|
54
|
+
end
|
55
|
+
log_debug { 'decoded packet: ' + decoded_packet.inspect }
|
56
|
+
decoded_packet
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def common_format
|
62
|
+
self.class::FORMAT
|
63
|
+
end
|
64
|
+
|
65
|
+
def conditional_format packet
|
66
|
+
return [] unless self.class.const_defined? :CONDITIONAL_FORMAT
|
67
|
+
packet.inject([]){ |a, (field_name, field_value)|
|
68
|
+
a + ((self.class::CONDITIONAL_FORMAT[field_name] || {})[field_value] || [])
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def decode_recursively payload_io, packet=nil
|
73
|
+
if packet.class == Array and packet.size == 0
|
74
|
+
[]
|
75
|
+
else
|
76
|
+
format = case packet
|
77
|
+
when nil
|
78
|
+
common_format
|
79
|
+
when Array
|
80
|
+
conditional_format(packet)
|
81
|
+
end
|
82
|
+
decoded_packet = format.map{ |data_type, field_name|
|
83
|
+
begin
|
84
|
+
[field_name, data_type.decode(payload_io)]
|
85
|
+
rescue => e
|
86
|
+
raise RuntimeError, "Failed decoding #{field_name.inspect} (#{e.message})"
|
87
|
+
end
|
88
|
+
}
|
89
|
+
decoded_packet + decode_recursively(payload_io, decoded_packet)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -1,10 +1,18 @@
|
|
1
1
|
module HrrRbSftp
|
2
2
|
class Protocol
|
3
|
-
|
3
|
+
|
4
|
+
#
|
5
|
+
# This module implements SFTP protocol version 1 packet types, formats, and responders.
|
6
|
+
#
|
7
|
+
module Version1
|
8
|
+
|
9
|
+
#
|
10
|
+
# Represents SFTP protocol version 1.
|
11
|
+
#
|
4
12
|
PROTOCOL_VERSION = 1
|
5
13
|
end
|
6
14
|
end
|
7
15
|
end
|
8
16
|
|
9
|
-
require "hrr_rb_sftp/protocol/version1/
|
10
|
-
require "hrr_rb_sftp/protocol/version1/
|
17
|
+
require "hrr_rb_sftp/protocol/version1/data_types"
|
18
|
+
require "hrr_rb_sftp/protocol/version1/packets"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Version1
|
4
|
+
|
5
|
+
#
|
6
|
+
# This module implements SFTP protocol version 1 data types to be used to encode or decode packet.
|
7
|
+
#
|
8
|
+
module DataTypes
|
9
|
+
include Common::DataTypes
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require "hrr_rb_sftp/protocol/version1/data_types/attrs"
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Version1
|
4
|
+
module DataTypes
|
5
|
+
|
6
|
+
#
|
7
|
+
# This module provides methods to convert file attributes represented in ::Hash and binary string each other.
|
8
|
+
#
|
9
|
+
class Attrs
|
10
|
+
|
11
|
+
#
|
12
|
+
# Represents SSH_FILEXFER_ATTR_SIZE.
|
13
|
+
#
|
14
|
+
SSH_FILEXFER_ATTR_SIZE = 0x00000001
|
15
|
+
|
16
|
+
#
|
17
|
+
# Represents SSH_FILEXFER_ATTR_UIDGID.
|
18
|
+
#
|
19
|
+
SSH_FILEXFER_ATTR_UIDGID = 0x00000002
|
20
|
+
|
21
|
+
#
|
22
|
+
# Represents SSH_FILEXFER_ATTR_PERMISSIONS.
|
23
|
+
#
|
24
|
+
SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004
|
25
|
+
|
26
|
+
#
|
27
|
+
# Represents SSH_FILEXFER_ATTR_ACMODTIME.
|
28
|
+
#
|
29
|
+
SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008
|
30
|
+
|
31
|
+
#
|
32
|
+
# Represents SSH_FILEXFER_ATTR_EXTENDED.
|
33
|
+
#
|
34
|
+
SSH_FILEXFER_ATTR_EXTENDED = 0x80000000
|
35
|
+
|
36
|
+
#
|
37
|
+
# Converts file attributes represented in ::Hash into binary string.
|
38
|
+
#
|
39
|
+
# @param arg [::Hash{::Symbol=>::Object}] File attributes represented in ::Hash to be converted.
|
40
|
+
# @raise [::ArgumentError] When arg is not ::Hash value.
|
41
|
+
# @return [::String] Converted binary string.
|
42
|
+
#
|
43
|
+
def self.encode arg
|
44
|
+
unless arg.kind_of? ::Hash
|
45
|
+
raise ArgumentError, "must be a kind of Hash, but got #{arg.inspect}"
|
46
|
+
end
|
47
|
+
|
48
|
+
flags = 0
|
49
|
+
flags |= SSH_FILEXFER_ATTR_SIZE if arg.has_key?(:"size")
|
50
|
+
flags |= SSH_FILEXFER_ATTR_UIDGID if arg.has_key?(:"uid") && arg.has_key?(:"gid")
|
51
|
+
flags |= SSH_FILEXFER_ATTR_PERMISSIONS if arg.has_key?(:"permissions")
|
52
|
+
flags |= SSH_FILEXFER_ATTR_ACMODTIME if arg.has_key?(:"atime") && arg.has_key?(:"mtime")
|
53
|
+
flags |= SSH_FILEXFER_ATTR_EXTENDED if arg.has_key?(:"extensions")
|
54
|
+
|
55
|
+
payload = DataTypes::Uint32.encode flags
|
56
|
+
payload += DataTypes::Uint64.encode arg[:"size"] unless (flags & SSH_FILEXFER_ATTR_SIZE).zero?
|
57
|
+
payload += DataTypes::Uint32.encode arg[:"uid"] unless (flags & SSH_FILEXFER_ATTR_UIDGID).zero?
|
58
|
+
payload += DataTypes::Uint32.encode arg[:"gid"] unless (flags & SSH_FILEXFER_ATTR_UIDGID).zero?
|
59
|
+
payload += DataTypes::Uint32.encode arg[:"permissions"] unless (flags & SSH_FILEXFER_ATTR_PERMISSIONS).zero?
|
60
|
+
payload += DataTypes::Uint32.encode arg[:"atime"] unless (flags & SSH_FILEXFER_ATTR_ACMODTIME).zero?
|
61
|
+
payload += DataTypes::Uint32.encode arg[:"mtime"] unless (flags & SSH_FILEXFER_ATTR_ACMODTIME).zero?
|
62
|
+
payload += DataTypes::Uint32.encode arg[:"extensions"].size unless (flags & SSH_FILEXFER_ATTR_EXTENDED).zero?
|
63
|
+
payload += DataTypes::ExtensionPairs.encode arg[:"extensions"] unless (flags & SSH_FILEXFER_ATTR_EXTENDED).zero?
|
64
|
+
payload
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Converts binary string into file attributes represented in ::Hash.
|
69
|
+
#
|
70
|
+
# @param io [::IO] ::IO instance that has buffer to be read.
|
71
|
+
# @return [::Hash{::Symbol=>::Object}] Converted file attributes represented in ::Hash.
|
72
|
+
#
|
73
|
+
def self.decode io
|
74
|
+
attrs = Hash.new
|
75
|
+
flags = DataTypes::Uint32.decode(io)
|
76
|
+
attrs[:"size"] = DataTypes::Uint64.decode(io) unless (flags & SSH_FILEXFER_ATTR_SIZE).zero?
|
77
|
+
attrs[:"uid"] = DataTypes::Uint32.decode(io) unless (flags & SSH_FILEXFER_ATTR_UIDGID).zero?
|
78
|
+
attrs[:"gid"] = DataTypes::Uint32.decode(io) unless (flags & SSH_FILEXFER_ATTR_UIDGID).zero?
|
79
|
+
attrs[:"permissions"] = DataTypes::Uint32.decode(io) unless (flags & SSH_FILEXFER_ATTR_PERMISSIONS).zero?
|
80
|
+
attrs[:"atime"] = DataTypes::Uint32.decode(io) unless (flags & SSH_FILEXFER_ATTR_ACMODTIME).zero?
|
81
|
+
attrs[:"mtime"] = DataTypes::Uint32.decode(io) unless (flags & SSH_FILEXFER_ATTR_ACMODTIME).zero?
|
82
|
+
extended_count = DataTypes::Uint32.decode(io) unless (flags & SSH_FILEXFER_ATTR_EXTENDED).zero?
|
83
|
+
attrs[:"extensions"] = Array.new(extended_count){DataTypes::ExtensionPair.decode(io)} unless (flags & SSH_FILEXFER_ATTR_EXTENDED).zero?
|
84
|
+
attrs
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module HrrRbSftp
|
2
|
+
class Protocol
|
3
|
+
module Version1
|
4
|
+
|
5
|
+
#
|
6
|
+
# This class implements SFTP protocol version 1 packet types, formats, and responders.
|
7
|
+
#
|
8
|
+
class Packets
|
9
|
+
include Loggable
|
10
|
+
|
11
|
+
#
|
12
|
+
# @param context [Hash] Contextual variables.
|
13
|
+
# - :version (Integer) - Negotiated protocol version.
|
14
|
+
# - :handles (Hash\\{String=>File, Dir\}) - Opened handles.
|
15
|
+
# @param logger [Logger] Logger.
|
16
|
+
#
|
17
|
+
def initialize context, logger: nil
|
18
|
+
self.logger = logger
|
19
|
+
|
20
|
+
@packets = packet_classes.map{|c| {c::TYPE => c.new(context, logger: logger)}}.inject({}, :merge)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Responds to a request.
|
25
|
+
#
|
26
|
+
# @param request_payload [String] Request payload.
|
27
|
+
# @return [String] Response payload that is encoded packet generated by each SFTP protocol version and each request responder.
|
28
|
+
# @raise [RuntimeError] When the SFTP protocol version does not support or the library does not implement the request type.
|
29
|
+
#
|
30
|
+
def respond_to request_payload
|
31
|
+
request_type = request_payload[0].unpack("C")[0]
|
32
|
+
response_packet = if @packets.has_key?(request_type)
|
33
|
+
request_packet = @packets[request_type].decode request_payload
|
34
|
+
@packets[request_type].respond_to request_packet
|
35
|
+
else
|
36
|
+
raise RuntimeError, "Unsupported type: #{request_type}"
|
37
|
+
end
|
38
|
+
response_type = response_packet[:"type"]
|
39
|
+
@packets[response_type].encode response_packet
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def packet_classes
|
45
|
+
self.class.constants.select{|c| c.to_s.start_with?("SSH_FXP_")}.map{|c| self.class.const_get(c)}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
require "hrr_rb_sftp/protocol/version1/packets/packet"
|
53
|
+
require "hrr_rb_sftp/protocol/version1/packets/003_ssh_fxp_open"
|
54
|
+
require "hrr_rb_sftp/protocol/version1/packets/004_ssh_fxp_close"
|
55
|
+
require "hrr_rb_sftp/protocol/version1/packets/005_ssh_fxp_read"
|
56
|
+
require "hrr_rb_sftp/protocol/version1/packets/006_ssh_fxp_write"
|
57
|
+
require "hrr_rb_sftp/protocol/version1/packets/007_ssh_fxp_lstat"
|
58
|
+
require "hrr_rb_sftp/protocol/version1/packets/008_ssh_fxp_fstat"
|
59
|
+
require "hrr_rb_sftp/protocol/version1/packets/009_ssh_fxp_setstat"
|
60
|
+
require "hrr_rb_sftp/protocol/version1/packets/010_ssh_fxp_fsetstat"
|
61
|
+
require "hrr_rb_sftp/protocol/version1/packets/011_ssh_fxp_opendir"
|
62
|
+
require "hrr_rb_sftp/protocol/version1/packets/012_ssh_fxp_readdir"
|
63
|
+
require "hrr_rb_sftp/protocol/version1/packets/013_ssh_fxp_remove"
|
64
|
+
require "hrr_rb_sftp/protocol/version1/packets/014_ssh_fxp_mkdir"
|
65
|
+
require "hrr_rb_sftp/protocol/version1/packets/015_ssh_fxp_rmdir"
|
66
|
+
require "hrr_rb_sftp/protocol/version1/packets/016_ssh_fxp_realpath"
|
67
|
+
require "hrr_rb_sftp/protocol/version1/packets/017_ssh_fxp_stat"
|
68
|
+
require "hrr_rb_sftp/protocol/version1/packets/101_ssh_fxp_status"
|
69
|
+
require "hrr_rb_sftp/protocol/version1/packets/102_ssh_fxp_handle"
|
70
|
+
require "hrr_rb_sftp/protocol/version1/packets/103_ssh_fxp_data"
|
71
|
+
require "hrr_rb_sftp/protocol/version1/packets/104_ssh_fxp_name"
|
72
|
+
require "hrr_rb_sftp/protocol/version1/packets/105_ssh_fxp_attrs"
|