Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protocole::V06::Extended #62

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/net/sftp/protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'net/sftp/protocol/04/base'
require 'net/sftp/protocol/05/base'
require 'net/sftp/protocol/06/base'
require 'net/sftp/protocol/06/extended'

module Net; module SFTP

Expand All @@ -22,11 +23,11 @@ def self.load(session, version)
when 3 then V03::Base.new(session)
when 4 then V04::Base.new(session)
when 5 then V05::Base.new(session)
when 6 then V06::Base.new(session)
when 6 then V06::Extended.new(session)
else raise NotImplementedError, "unsupported SFTP version #{version.inspect}"
end
end

end

end; end
end; end
107 changes: 107 additions & 0 deletions lib/net/sftp/protocol/06/extended.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
require 'net/sftp/protocol/06/base'

module Net; module SFTP; module Protocol; module V06

# Wraps the low-level SFTP calls for version 6 of the SFTP protocol.
#
# None of these protocol methods block--all of them return immediately,
# requiring the SSH event loop to be run while the server response is
# pending.
#
# You will almost certainly never need to use this driver directly. Please
# see Net::SFTP::Session for the recommended interface.
class Extended < V06::Base

# Parses the given "md5-hash" FXP_EXTENDED_REPL packet and returns a
# hash with one key, :md5, which references the computed hash.
def parse_md5_packet(data)
md5 = ""

if !data.empty?
md5 = data.read_string
end

{ :md5 => md5 }
end

# Parses the given "check-file" FXP_EXTENDED_REPL packet and returns a hash
# with two keys, :algo, which references the hash algorithm used and
# :hashes which references the computed hashes.
def parse_hash_packet(data)
hashes = []

algo = data.read_string
size = case algo
when "md5" then 128
when "sha256" then 256
when "sha384" then 284
when "sha512" then 512
else raise NotImplementedError, "unsupported algorithm: #{algo}"
end

while !data.eof? do
hashes << data.read(size)
end

{ :algo => algo, :hashes => hashes }
end

# Parses the given "home-directory" FXP_EXTENDED_REPL packet and returns a
# hash with one key, :home, which references the home directory returned by
# the server.
def parse_home_packet(data)
{ :home => data.read_string }
end

# Parses the given FXP_EXTENDED_REPL packet and returns a hash, with
# :extension key, which references SFTP extension and the associated keys
def parse_extented_reply_packet(packet)
packet.read_string do |extension|
data = packet.remainder_as_buffer
parsed_packet = case extension
when "md5-hash" then parse_md5_packet(data)
when "check-file" then parse_hash_packet(data)
when "home-directory" then parse_home_packet(data)
else raise NotImplementedError, "unknown packet type: #{extension}"
end
end

{ :extension => extension }.merge(parsed_packet)
end

# Sends a FXP_EXTENDED packet to the server to request MD5 checksum
# computation for file (or portion of file) obtained on the given +handle+,
# for the given byte +offset+ and +length+. The +quick_hash+ parameter is
# the hash over the first 2048 bytes of the data. It allows the server to
# quickly check if it is worth the resources to hash a big file.
def md5(handle, offset, length, quick_hash)
send_request(FXP_EXTENDED, :string, "md5-hash-handle", :int64, offset, :int64, length, :string, quick_hash)
end

# Sends a FXP_EXTENDED packet to the server to request checksum computation
# for file (or portion of file) obtained on the given +handle+, for the
# given byte +offset+ and +length+. The +block_size+ parameter is used to
# compute over every +block_size+ block in the file. If the +block_size+ is
# 0, then only one hash, over the entire range, is made.
def hash(handle, offset, length, block_size=0)
if block_size != 0 && block_size < 255
block_size = 256
end
send_request(FXP_EXTENDED, :string, "check-file-handle", :string, handle, :string, "md5,sha256,sha384,sha512", :int64, offset, :int64, length, :long, block_size)
end

# Sends a FXP_EXTENDED packet to the server to request disk space availability
# for the given +path+ location.
def space_available(path)
send_request(FXP_EXTENDED, :string, "space-available", :string, path)
end

# Sends a FXP_EXTENDED packet to the server to request home directory
# for the given +username+.
def home(username)
send_request(FXP_EXTENDED, :string, "home-directory", :string, username)
end

end

end; end; end; end
13 changes: 7 additions & 6 deletions lib/net/sftp/protocol/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ def initialize(session)
# (the keys in the hash are packet-type specific).
def parse(packet)
case packet.type
when FXP_STATUS then parse_status_packet(packet)
when FXP_HANDLE then parse_handle_packet(packet)
when FXP_DATA then parse_data_packet(packet)
when FXP_NAME then parse_name_packet(packet)
when FXP_ATTRS then parse_attrs_packet(packet)
when FXP_STATUS then parse_status_packet(packet)
when FXP_HANDLE then parse_handle_packet(packet)
when FXP_DATA then parse_data_packet(packet)
when FXP_NAME then parse_name_packet(packet)
when FXP_ATTRS then parse_attrs_packet(packet)
when FXP_EXTENDED_REPLY then parse_extented_reply_packet(packet)
else raise NotImplementedError, "unknown packet type: #{packet.type}"
end
end
Expand All @@ -47,4 +48,4 @@ def send_request(type, *args)
end
end

end; end; end
end; end; end
2 changes: 2 additions & 0 deletions net-sftp.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Gem::Specification.new do |s|
"lib/net/sftp/protocol/05/base.rb",
"lib/net/sftp/protocol/06/attributes.rb",
"lib/net/sftp/protocol/06/base.rb",
"lib/net/sftp/protocol/06/extended.rb",
"lib/net/sftp/protocol/base.rb",
"lib/net/sftp/request.rb",
"lib/net/sftp/response.rb",
Expand All @@ -64,6 +65,7 @@ Gem::Specification.new do |s|
"test/protocol/05/test_base.rb",
"test/protocol/06/test_attributes.rb",
"test/protocol/06/test_base.rb",
"test/protocol/06/test_extended.rb",
"test/protocol/test_base.rb",
"test/test_all.rb",
"test/test_dir.rb",
Expand Down
50 changes: 50 additions & 0 deletions test/protocol/06/test_extended.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'common'
require 'protocol/06/test_base'

class Protocol::V06::TestExtended < Protocol::V06::TestBase

def setup
@session = stub('session', :logger => nil)
@base = driver.new(@session)
end

def test_version
assert_equal 6, @base.version
end

def test_md5_should_send_md5_hash_packet
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "md5-hash-handle", :int64, 112233, :int64, 445566, :string, "ABCDEF")
assert_equal 0, @base.md5("test", 112233, 445566, "ABCDEF")
end

def test_hash_should_send_hash_packet
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "check-file-handle", :string, "test", :string, "md5,sha256,sha384,sha512", :int64, 112233, :int64, 445566, :long, 0)
assert_equal 0, @base.hash("test", 112233, 445566)
end

def test_hash_should_send_hash_packet_with_block_size
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "check-file-handle", :string, "test", :string, "md5,sha256,sha384,sha512", :int64, 112233, :int64, 445566, :long, 256)
assert_equal 0, @base.hash("test", 112233, 445566, 123)
end

def test_hash_should_send_hash_packet_with_valid_block_size
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "check-file-handle", :string, "test", :string, "md5,sha256,sha384,sha512", :int64, 112233, :int64, 445566, :long, 256)
assert_equal 0, @base.hash("test", 112233, 445566, -1)
end

def test_space_available_should_send_space_available_packet
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "space-available", :string, "/var/log/Xorg.0.log")
assert_equal 0, @base.space_available("/var/log/Xorg.0.log")
end

def test_home_should_send_home_directory_packet
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "home-directory", :string, "test")
assert_equal 0, @base.home("test")
end

private

def driver
Net::SFTP::Protocol::V06::Extended
end
end
10 changes: 8 additions & 2 deletions test/test_protocol.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
require 'common'

class ProtocolTest < Net::SFTP::TestCase
1.upto(6) do |version|
1.upto(5) do |version|
define_method("test_load_version_#{version}_should_return_v#{version}_driver") do
session = stub('session', :logger => nil)
driver = Net::SFTP::Protocol.load(session, version)
assert_instance_of Net::SFTP::Protocol.const_get("V%02d" % version)::Base, driver
end
end

def test_load_version_6_should_return_v6_driver
session = stub('session', :logger => nil)
driver = Net::SFTP::Protocol.load(session, 6)
assert_instance_of Net::SFTP::Protocol.const_get("V06")::Extended, driver
end

def test_load_version_7_should_be_unsupported
assert_raises(NotImplementedError) do
Net::SFTP::Protocol.load(stub('session'), 7)
end
end
end
end