Skip to content

Commit 5414bd7

Browse files
nabilbendafiNabil BENDAFI
authored andcommitted
Protocole::V06::Extended
Handle SFTP V6 extensions commands - md5-hash-handle - check-file-handle - space-available - home-directory
1 parent c1393ae commit 5414bd7

File tree

4 files changed

+167
-8
lines changed

4 files changed

+167
-8
lines changed

lib/net/sftp/protocol.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'net/sftp/protocol/04/base'
55
require 'net/sftp/protocol/05/base'
66
require 'net/sftp/protocol/06/base'
7+
require 'net/sftp/protocol/06/extended'
78

89
module Net; module SFTP
910

@@ -22,11 +23,11 @@ def self.load(session, version)
2223
when 3 then V03::Base.new(session)
2324
when 4 then V04::Base.new(session)
2425
when 5 then V05::Base.new(session)
25-
when 6 then V06::Base.new(session)
26+
when 6 then V06::Extended.new(session)
2627
else raise NotImplementedError, "unsupported SFTP version #{version.inspect}"
2728
end
2829
end
2930

3031
end
3132

32-
end; end
33+
end; end

lib/net/sftp/protocol/06/extended.rb

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
require 'net/sftp/protocol/06/base'
2+
3+
module Net; module SFTP; module Protocol; module V06
4+
5+
# Wraps the low-level SFTP calls for version 6 of the SFTP protocol.
6+
#
7+
# None of these protocol methods block--all of them return immediately,
8+
# requiring the SSH event loop to be run while the server response is
9+
# pending.
10+
#
11+
# You will almost certainly never need to use this driver directly. Please
12+
# see Net::SFTP::Session for the recommended interface.
13+
class Extended < V06::Base
14+
15+
# Parses the given "md5-hash" FXP_EXTENDED_REPL packet and returns a
16+
# hash with one key, :md5, which references the computed hash.
17+
def parse_md5_packet(data)
18+
md5 = ""
19+
20+
if !data.empty?
21+
md5 = data.read_string
22+
end
23+
24+
{ :md5 => md5 }
25+
end
26+
27+
# Parses the given "check-file" FXP_EXTENDED_REPL packet and returns a hash
28+
# with two keys, :algo, which references the hash algorithm used and
29+
# :hashes which references the computed hashes.
30+
def parse_hash_packet(data)
31+
hashes = []
32+
33+
algo = data.read_string
34+
size = case algo
35+
when "md5" then 128
36+
when "sha256" then 256
37+
when "sha384" then 284
38+
when "sha512" then 512
39+
else raise NotImplementedError, "unsupported algorithm: #{algo}"
40+
end
41+
42+
while !data.eof? do
43+
hashes << data.read(size)
44+
end
45+
46+
{ :algo => algo, :hashes => hashes }
47+
end
48+
49+
# Parses the given "home-directory" FXP_EXTENDED_REPL packet and returns a
50+
# hash with one key, :home, which references the home directory returned by
51+
# the server.
52+
def parse_home_packet(data)
53+
{ :home => data.read_string }
54+
end
55+
56+
# Parses the given FXP_EXTENDED_REPL packet and returns a hash, with
57+
# :extension key, which references SFTP extension and the associated keys
58+
def parse_extented_reply_packet(packet)
59+
packet.read_string do |extension|
60+
data = packet.remainder_as_buffer
61+
parsed_packet = case extension
62+
when "md5-hash" then parse_md5_packet(data)
63+
when "check-file" then parse_hash_packet(data)
64+
when "home-directory" then parse_home_packet(data)
65+
else raise NotImplementedError, "unknown packet type: #{extension}"
66+
end
67+
end
68+
69+
{ :extension => extension }.merge(parsed_packet)
70+
end
71+
72+
# Sends a FXP_EXTENDED packet to the server to request MD5 checksum
73+
# computation for file (or portion of file) obtained on the given +handle+,
74+
# for the given byte +offset+ and +length+. The +quick_hash+ parameter is
75+
# the hash over the first 2048 bytes of the data. It allows the server to
76+
# quickly check if it is worth the resources to hash a big file.
77+
def md5(handle, offset, length, quick_hash)
78+
send_request(FXP_EXTENDED, :string, "md5-hash-handle", :int64, offset, :int64, length, :string, quick_hash)
79+
end
80+
81+
# Sends a FXP_EXTENDED packet to the server to request checksum computation
82+
# for file (or portion of file) obtained on the given +handle+, for the
83+
# given byte +offset+ and +length+. The +block_size+ parameter is used to
84+
# compute over every +block_size+ block in the file. If the +block_size+ is
85+
# 0, then only one hash, over the entire range, is made.
86+
def hash(handle, offset, length, block_size=0)
87+
if block_size != 0 && block_size < 255
88+
block_size = 256
89+
end
90+
send_request(FXP_EXTENDED, :string, "check-file-handle", :string, handle, :string, "md5,sha256,sha384,sha512", :int64, offset, :int64, length, :long, block_size)
91+
end
92+
93+
# Sends a FXP_EXTENDED packet to the server to request disk space availability
94+
# for the given +path+ location.
95+
def space_available(path)
96+
send_request(FXP_EXTENDED, :string, "space-available", :string, path)
97+
end
98+
99+
# Sends a FXP_EXTENDED packet to the server to request home directory
100+
# for the given +username+.
101+
def home(username)
102+
send_request(FXP_EXTENDED, :string, "home-directory", :string, username)
103+
end
104+
105+
end
106+
107+
end; end; end; end

lib/net/sftp/protocol/base.rb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ def initialize(session)
2626
# (the keys in the hash are packet-type specific).
2727
def parse(packet)
2828
case packet.type
29-
when FXP_STATUS then parse_status_packet(packet)
30-
when FXP_HANDLE then parse_handle_packet(packet)
31-
when FXP_DATA then parse_data_packet(packet)
32-
when FXP_NAME then parse_name_packet(packet)
33-
when FXP_ATTRS then parse_attrs_packet(packet)
29+
when FXP_STATUS then parse_status_packet(packet)
30+
when FXP_HANDLE then parse_handle_packet(packet)
31+
when FXP_DATA then parse_data_packet(packet)
32+
when FXP_NAME then parse_name_packet(packet)
33+
when FXP_ATTRS then parse_attrs_packet(packet)
34+
when FXP_EXTENDED_REPLY then parse_extented_reply_packet(packet)
3435
else raise NotImplementedError, "unknown packet type: #{packet.type}"
3536
end
3637
end
@@ -47,4 +48,4 @@ def send_request(type, *args)
4748
end
4849
end
4950

50-
end; end; end
51+
end; end; end

test/protocol/06/test_extended.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require 'common'
2+
require 'protocol/06/test_base'
3+
4+
class Protocol::V06::TestExtended < Protocol::V06::TestBase
5+
6+
def setup
7+
@session = stub('session', :logger => nil)
8+
@base = driver.new(@session)
9+
end
10+
11+
def test_version
12+
assert_equal 6, @base.version
13+
end
14+
15+
def test_md5_should_send_md5_hash_packet
16+
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "md5-hash-handle", :int64, 112233, :int64, 445566, :string, "ABCDEF")
17+
assert_equal 0, @base.md5("test", 112233, 445566, "ABCDEF")
18+
end
19+
20+
def test_hash_should_send_hash_packet
21+
@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)
22+
assert_equal 0, @base.hash("test", 112233, 445566)
23+
end
24+
25+
def test_hash_should_send_hash_packet_with_block_size
26+
@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)
27+
assert_equal 0, @base.hash("test", 112233, 445566, 123)
28+
end
29+
30+
def test_hash_should_send_hash_packet_with_valid_block_size
31+
@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)
32+
assert_equal 0, @base.hash("test", 112233, 445566, -1)
33+
end
34+
35+
def test_space_available_should_send_space_available_packet
36+
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "space-available", :string, "/var/log/Xorg.0.log")
37+
assert_equal 0, @base.space_available("/var/log/Xorg.0.log")
38+
end
39+
40+
def test_home_should_send_home_directory_packet
41+
@session.expects(:send_packet).with(FXP_EXTENDED, :long, 0, :string, "home-directory", :string, "test")
42+
assert_equal 0, @base.home("test")
43+
end
44+
45+
private
46+
47+
def driver
48+
Net::SFTP::Protocol::V06::Extended
49+
end
50+
end

0 commit comments

Comments
 (0)