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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -0
  3. data/lib/hrr_rb_sftp.rb +79 -1
  4. data/lib/hrr_rb_sftp/loggable.rb +34 -0
  5. data/lib/hrr_rb_sftp/protocol.rb +39 -33
  6. data/lib/hrr_rb_sftp/protocol/common.rb +6 -3
  7. data/lib/hrr_rb_sftp/protocol/common/data_types.rb +19 -0
  8. data/lib/hrr_rb_sftp/protocol/common/data_types/byte.rb +40 -0
  9. data/lib/hrr_rb_sftp/protocol/common/data_types/extension_pair.rb +41 -0
  10. data/lib/hrr_rb_sftp/protocol/common/data_types/extension_pairs.rb +42 -0
  11. data/lib/hrr_rb_sftp/protocol/common/data_types/string.rb +42 -0
  12. data/lib/hrr_rb_sftp/protocol/common/data_types/uint32.rb +40 -0
  13. data/lib/hrr_rb_sftp/protocol/common/data_types/uint64.rb +40 -0
  14. data/lib/hrr_rb_sftp/protocol/common/packets.rb +16 -0
  15. data/lib/hrr_rb_sftp/protocol/common/packets/001_ssh_fxp_init.rb +27 -0
  16. data/lib/hrr_rb_sftp/protocol/common/packets/002_ssh_fxp_version.rb +28 -0
  17. data/lib/hrr_rb_sftp/protocol/common/packets/packet.rb +96 -0
  18. data/lib/hrr_rb_sftp/protocol/version1.rb +11 -3
  19. data/lib/hrr_rb_sftp/protocol/version1/data_types.rb +15 -0
  20. data/lib/hrr_rb_sftp/protocol/version1/data_types/attrs.rb +91 -0
  21. data/lib/hrr_rb_sftp/protocol/version1/packets.rb +72 -0
  22. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/003_ssh_fxp_open.rb +94 -43
  23. data/lib/hrr_rb_sftp/protocol/version1/packets/004_ssh_fxp_close.rb +61 -0
  24. data/lib/hrr_rb_sftp/protocol/version1/packets/005_ssh_fxp_read.rb +72 -0
  25. data/lib/hrr_rb_sftp/protocol/version1/packets/006_ssh_fxp_write.rb +64 -0
  26. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/007_ssh_fxp_lstat.rb +27 -9
  27. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/008_ssh_fxp_fstat.rb +26 -9
  28. data/lib/hrr_rb_sftp/protocol/version1/packets/009_ssh_fxp_setstat.rb +92 -0
  29. data/lib/hrr_rb_sftp/protocol/version1/packets/010_ssh_fxp_fsetstat.rb +85 -0
  30. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/011_ssh_fxp_opendir.rb +32 -11
  31. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/012_ssh_fxp_readdir.rb +73 -49
  32. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/013_ssh_fxp_remove.rb +27 -9
  33. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/014_ssh_fxp_mkdir.rb +28 -10
  34. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/015_ssh_fxp_rmdir.rb +32 -12
  35. data/lib/hrr_rb_sftp/protocol/version1/packets/016_ssh_fxp_realpath.rb +47 -0
  36. data/lib/hrr_rb_sftp/protocol/version1/{packet → packets}/017_ssh_fxp_stat.rb +27 -9
  37. data/lib/hrr_rb_sftp/protocol/version1/packets/101_ssh_fxp_status.rb +73 -0
  38. data/lib/hrr_rb_sftp/protocol/version1/packets/102_ssh_fxp_handle.rb +28 -0
  39. data/lib/hrr_rb_sftp/protocol/version1/packets/103_ssh_fxp_data.rb +28 -0
  40. data/lib/hrr_rb_sftp/protocol/version1/packets/104_ssh_fxp_name.rb +48 -0
  41. data/lib/hrr_rb_sftp/protocol/version1/packets/105_ssh_fxp_attrs.rb +28 -0
  42. data/lib/hrr_rb_sftp/protocol/version1/packets/packet.rb +57 -0
  43. data/lib/hrr_rb_sftp/protocol/version2.rb +11 -3
  44. data/lib/hrr_rb_sftp/protocol/version2/data_types.rb +13 -0
  45. data/lib/hrr_rb_sftp/protocol/version2/packets.rb +14 -0
  46. data/lib/hrr_rb_sftp/protocol/version2/packets/018_ssh_fxp_rename.rb +91 -0
  47. data/lib/hrr_rb_sftp/protocol/version3.rb +12 -3
  48. data/lib/hrr_rb_sftp/protocol/version3/data_types.rb +13 -0
  49. data/lib/hrr_rb_sftp/protocol/version3/extensions.rb +94 -0
  50. data/lib/hrr_rb_sftp/protocol/version3/extensions/extension.rb +58 -0
  51. data/lib/hrr_rb_sftp/protocol/version3/extensions/fsync_at_openssh_com.rb +59 -0
  52. data/lib/hrr_rb_sftp/protocol/version3/extensions/hardlink_at_openssh_com.rb +84 -0
  53. data/lib/hrr_rb_sftp/protocol/version3/extensions/lsetstat_at_openssh_com.rb +132 -0
  54. data/lib/hrr_rb_sftp/protocol/version3/extensions/posix_rename_at_openssh_com.rb +76 -0
  55. data/lib/hrr_rb_sftp/protocol/version3/packets.rb +19 -0
  56. data/lib/hrr_rb_sftp/protocol/version3/packets/014_ssh_fxp_mkdir.rb +75 -0
  57. data/lib/hrr_rb_sftp/protocol/version3/packets/019_ssh_fxp_readlink.rb +76 -0
  58. data/lib/hrr_rb_sftp/protocol/version3/packets/020_ssh_fxp_symlink.rb +76 -0
  59. data/lib/hrr_rb_sftp/protocol/version3/packets/101_ssh_fxp_status.rb +25 -0
  60. data/lib/hrr_rb_sftp/protocol/version3/packets/200_ssh_fxp_extended.rb +95 -0
  61. data/lib/hrr_rb_sftp/protocol/version3/packets/201_ssh_fxp_extended_reply.rb +60 -0
  62. data/lib/hrr_rb_sftp/receiver.rb +17 -2
  63. data/lib/hrr_rb_sftp/sender.rb +15 -1
  64. data/lib/hrr_rb_sftp/server.rb +43 -12
  65. data/lib/hrr_rb_sftp/version.rb +5 -1
  66. metadata +54 -47
  67. data/lib/hrr_rb_sftp/protocol/common/data_type.rb +0 -15
  68. data/lib/hrr_rb_sftp/protocol/common/data_type/byte.rb +0 -22
  69. data/lib/hrr_rb_sftp/protocol/common/data_type/extension_pair.rb +0 -23
  70. data/lib/hrr_rb_sftp/protocol/common/data_type/extension_pairs.rb +0 -24
  71. data/lib/hrr_rb_sftp/protocol/common/data_type/string.rb +0 -24
  72. data/lib/hrr_rb_sftp/protocol/common/data_type/uint32.rb +0 -22
  73. data/lib/hrr_rb_sftp/protocol/common/data_type/uint64.rb +0 -22
  74. data/lib/hrr_rb_sftp/protocol/common/packet.rb +0 -11
  75. data/lib/hrr_rb_sftp/protocol/common/packet/001_ssh_fxp_init.rb +0 -18
  76. data/lib/hrr_rb_sftp/protocol/common/packet/002_ssh_fxp_version.rb +0 -19
  77. data/lib/hrr_rb_sftp/protocol/common/packetable.rb +0 -72
  78. data/lib/hrr_rb_sftp/protocol/version1/data_type.rb +0 -11
  79. data/lib/hrr_rb_sftp/protocol/version1/data_type/attrs.rb +0 -54
  80. data/lib/hrr_rb_sftp/protocol/version1/packet.rb +0 -29
  81. data/lib/hrr_rb_sftp/protocol/version1/packet/004_ssh_fxp_close.rb +0 -44
  82. data/lib/hrr_rb_sftp/protocol/version1/packet/005_ssh_fxp_read.rb +0 -53
  83. data/lib/hrr_rb_sftp/protocol/version1/packet/006_ssh_fxp_write.rb +0 -46
  84. data/lib/hrr_rb_sftp/protocol/version1/packet/009_ssh_fxp_setstat.rb +0 -63
  85. data/lib/hrr_rb_sftp/protocol/version1/packet/010_ssh_fxp_fsetstat.rb +0 -48
  86. data/lib/hrr_rb_sftp/protocol/version1/packet/016_ssh_fxp_realpath.rb +0 -30
  87. data/lib/hrr_rb_sftp/protocol/version1/packet/101_ssh_fxp_status.rb +0 -29
  88. data/lib/hrr_rb_sftp/protocol/version1/packet/102_ssh_fxp_handle.rb +0 -19
  89. data/lib/hrr_rb_sftp/protocol/version1/packet/103_ssh_fxp_data.rb +0 -19
  90. data/lib/hrr_rb_sftp/protocol/version1/packet/104_ssh_fxp_name.rb +0 -33
  91. data/lib/hrr_rb_sftp/protocol/version1/packet/105_ssh_fxp_attrs.rb +0 -19
  92. data/lib/hrr_rb_sftp/protocol/version2/data_type.rb +0 -9
  93. data/lib/hrr_rb_sftp/protocol/version2/packet.rb +0 -11
  94. data/lib/hrr_rb_sftp/protocol/version2/packet/018_ssh_fxp_rename.rb +0 -70
  95. data/lib/hrr_rb_sftp/protocol/version3/data_type.rb +0 -9
  96. data/lib/hrr_rb_sftp/protocol/version3/packet.rb +0 -16
  97. data/lib/hrr_rb_sftp/protocol/version3/packet/014_ssh_fxp_mkdir.rb +0 -58
  98. data/lib/hrr_rb_sftp/protocol/version3/packet/019_ssh_fxp_readlink.rb +0 -57
  99. data/lib/hrr_rb_sftp/protocol/version3/packet/020_ssh_fxp_symlink.rb +0 -58
  100. data/lib/hrr_rb_sftp/protocol/version3/packet/101_ssh_fxp_status.rb +0 -31
  101. data/lib/hrr_rb_sftp/protocol/version3/packet/200_ssh_fxp_extended.rb +0 -34
  102. 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
- class Version1
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/data_type"
10
- require "hrr_rb_sftp/protocol/version1/packet"
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"