diff --git a/lib/net/sftp/protocol/05/attributes.rb b/lib/net/sftp/protocol/05/attributes.rb new file mode 100644 index 0000000..d28579c --- /dev/null +++ b/lib/net/sftp/protocol/05/attributes.rb @@ -0,0 +1,61 @@ +require 'net/sftp/protocol/04/attributes' + +module Net; module SFTP; module Protocol; module V05 + + # A class representing the attributes of a file or directory on the server. + # It may be used to specify new attributes, or to query existing attributes. + # This particular class is specific to versions 4 and 5 of the SFTP + # protocol. + # + # To specify new attributes, just pass a hash as the argument to the + # constructor. The following keys are supported: + # + # * :type:: the type of the item (integer, one of the T_ constants) + # * :size:: the size of the item (integer) + # * :uid:: the user-id that owns the file (integer) + # * :gid:: the group-id that owns the file (integer) + # * :owner:: the name of the user that owns the file (string) + # * :group:: the name of the group that owns the file (string) + # * :permissions:: the permissions on the file (integer, e.g. 0755) + # * :atime:: the access time of the file (integer, seconds since epoch) + # * :atime_nseconds:: the nanosecond component of atime (integer) + # * :createtime:: the time at which the file was created (integer, seconds since epoch) + # * :createtime_nseconds:: the nanosecond component of createtime (integer) + # * :mtime:: the modification time of the file (integer, seconds since epoch) + # * :mtime_nseconds:: the nanosecond component of mtime (integer) + # * :acl:: an array of ACL entries for the item + # * :attrib_bits:: other attributes of the file or directory (as a bit field) (integer) + # * :extended:: a hash of name/value pairs identifying extended info + # + # Likewise, when the server sends an Attributes object, all of the + # above attributes are exposed as methods (though not all will be set with + # non-nil values from the server). + class Attributes < V04::Attributes + F_BITS = 0x00000200 + + # The list of supported elements in the attributes structure as defined + # by v5 of the sftp protocol. + def self.elements #:nodoc: + @elements ||= [ + [:type, :byte, 0], + [:size, :int64, F_SIZE], + [:owner, :string, F_OWNERGROUP], + [:group, :string, F_OWNERGROUP], + [:permissions, :long, F_PERMISSIONS], + [:atime, :int64, F_ACCESSTIME], + [:atime_nseconds, :long, F_ACCESSTIME | F_SUBSECOND_TIMES], + [:createtime, :int64, F_CREATETIME], + [:createtime_nseconds, :long, F_CREATETIME | F_SUBSECOND_TIMES], + [:mtime, :int64, F_MODIFYTIME], + [:mtime_nseconds, :long, F_MODIFYTIME | F_SUBSECOND_TIMES], + [:acl, :special, F_ACL], + [:attrib_bits, :long, F_BITS], + [:extended, :special, F_EXTENDED] + ] + end + + # The type of the item on the remote server. Must be one of the T_* constants. + attr_accessor :attrib_bits + end + +end ; end ; end ; end diff --git a/lib/net/sftp/protocol/05/base.rb b/lib/net/sftp/protocol/05/base.rb index 3c40b8f..8bb9b0e 100644 --- a/lib/net/sftp/protocol/05/base.rb +++ b/lib/net/sftp/protocol/05/base.rb @@ -1,4 +1,5 @@ require 'net/sftp/protocol/04/base' +require 'net/sftp/protocol/05/attributes' module Net; module SFTP; module Protocol; module V05 @@ -61,6 +62,14 @@ def open(path, flags, options) send_request(FXP_OPEN, :string, path, :long, desired_access, :long, sftp_flags, :raw, attributes.to_s) end + protected + + # Returns the Attributes class used by this version of the protocol + # (Net::SFTP::Protocol::V05::Attributes, in this case) + def attribute_factory + V05::Attributes + end + end -end; end; end; end \ No newline at end of file +end; end; end; end diff --git a/lib/net/sftp/protocol/06/attributes.rb b/lib/net/sftp/protocol/06/attributes.rb index e233222..44928f5 100644 --- a/lib/net/sftp/protocol/06/attributes.rb +++ b/lib/net/sftp/protocol/06/attributes.rb @@ -1,4 +1,4 @@ -require 'net/sftp/protocol/04/attributes' +require 'net/sftp/protocol/05/attributes' module Net; module SFTP; module Protocol; module V06 @@ -38,8 +38,7 @@ module Net; module SFTP; module Protocol; module V06 # Likewise, when the server sends an Attributes object, all of the # above attributes are exposed as methods (though not all will be set with # non-nil values from the server). - class Attributes < V04::Attributes - F_BITS = 0x00000200 + class Attributes < V05::Attributes F_ALLOCATION_SIZE = 0x00000400 F_TEXT_HINT = 0x00000800 F_MIME_TYPE = 0x00001000 diff --git a/test/protocol/05/test_attributes.rb b/test/protocol/05/test_attributes.rb new file mode 100644 index 0000000..fb47b9c --- /dev/null +++ b/test/protocol/05/test_attributes.rb @@ -0,0 +1,80 @@ +require 'common' + +module Etc; end + +class Protocol::V05::TestAttributes < Net::SFTP::TestCase + def test_from_buffer_should_correctly_parse_buffer_and_return_attribute_object + attributes = attributes_factory.from_buffer(full_buffer) + + assert_equal 9, attributes.type + assert_equal 1234567890, attributes.size + assert_equal "jamis", attributes.owner + assert_equal "users", attributes.group + assert_equal 0755, attributes.permissions + assert_equal 1234567890, attributes.atime + assert_equal 12345, attributes.atime_nseconds + assert_equal 2345678901, attributes.createtime + assert_equal 23456, attributes.createtime_nseconds + assert_equal 3456789012, attributes.mtime + assert_equal 34567, attributes.mtime_nseconds + + assert_equal 2, attributes.acl.length + + assert_equal 1, attributes.acl.first.type + assert_equal 2, attributes.acl.first.flag + assert_equal 3, attributes.acl.first.mask + assert_equal "foo", attributes.acl.first.who + + assert_equal 4, attributes.acl.last.type + assert_equal 5, attributes.acl.last.flag + assert_equal 6, attributes.acl.last.mask + assert_equal "bar", attributes.acl.last.who + + assert_equal 0x12341234, attributes.attrib_bits + + assert_equal "second", attributes.extended["first"] + end + + def test_attributes_to_s_should_build_binary_representation + attributes = attributes_factory.new( + :type => 9, + :size => 1234567890, + :owner => "jamis", :group => "users", + :permissions => 0755, + :atime => 1234567890, :atime_nseconds => 12345, + :createtime => 2345678901, :createtime_nseconds => 23456, + :mtime => 3456789012, :mtime_nseconds => 34567, + :acl => [attributes_factory::ACL.new(1,2,3,"foo"), + attributes_factory::ACL.new(4,5,6,"bar")], + :attrib_bits => 0x12341234, + :extended => { "first" => "second" }) + + assert_equal full_buffer.to_s, attributes.to_s + end + + def test_attributes_to_s_should_build_binary_representation_when_subset_is_present + attributes = attributes_factory.new(:permissions => 0755) + assert_equal Net::SSH::Buffer.from(:long, 0x4, :byte, 1, :long, 0755).to_s, attributes.to_s + end + + private + + def full_buffer + Net::SSH::Buffer.from(:long, 0x800003fd, + :byte, 9, :int64, 1234567890, + :string, "jamis", :string, "users", + :long, 0755, + :int64, 1234567890, :long, 12345, + :int64, 2345678901, :long, 23456, + :int64, 3456789012, :long, 34567, + :string, raw(:long, 2, + :long, 1, :long, 2, :long, 3, :string, "foo", + :long, 4, :long, 5, :long, 6, :string, "bar"), + :long, 0x12341234, + :long, 1, :string, "first", :string, "second") + end + + def attributes_factory + Net::SFTP::Protocol::V05::Attributes + end +end