net-sftp-2.1.2/0000755000004100000410000000000012144456021013303 5ustar www-datawww-datanet-sftp-2.1.2/net-sftp.gemspec0000644000004100000410000000663712144456021016424 0ustar www-datawww-data# Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = "net-sftp" s.version = "2.1.2" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Jamis Buck", "Delano Mandelbaum"] s.cert_chain = ["gem-public_cert.pem"] s.date = "2013-05-07" s.description = "A pure Ruby implementation of the SFTP client protocol" s.email = "net-ssh@solutious.com" s.extra_rdoc_files = [ "LICENSE.txt", "README.rdoc" ] s.files = [ "CHANGES.txt", "LICENSE.txt", "Manifest", "README.rdoc", "Rakefile", "gem-public_cert.pem", "lib/net/sftp.rb", "lib/net/sftp/constants.rb", "lib/net/sftp/errors.rb", "lib/net/sftp/operations/dir.rb", "lib/net/sftp/operations/download.rb", "lib/net/sftp/operations/file.rb", "lib/net/sftp/operations/file_factory.rb", "lib/net/sftp/operations/upload.rb", "lib/net/sftp/packet.rb", "lib/net/sftp/protocol.rb", "lib/net/sftp/protocol/01/attributes.rb", "lib/net/sftp/protocol/01/base.rb", "lib/net/sftp/protocol/01/name.rb", "lib/net/sftp/protocol/02/base.rb", "lib/net/sftp/protocol/03/base.rb", "lib/net/sftp/protocol/04/attributes.rb", "lib/net/sftp/protocol/04/base.rb", "lib/net/sftp/protocol/04/name.rb", "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/base.rb", "lib/net/sftp/request.rb", "lib/net/sftp/response.rb", "lib/net/sftp/session.rb", "lib/net/sftp/version.rb", "net-sftp.gemspec", "setup.rb", "test/common.rb", "test/protocol/01/test_attributes.rb", "test/protocol/01/test_base.rb", "test/protocol/01/test_name.rb", "test/protocol/02/test_base.rb", "test/protocol/03/test_base.rb", "test/protocol/04/test_attributes.rb", "test/protocol/04/test_base.rb", "test/protocol/04/test_name.rb", "test/protocol/05/test_base.rb", "test/protocol/06/test_attributes.rb", "test/protocol/06/test_base.rb", "test/protocol/test_base.rb", "test/test_all.rb", "test/test_dir.rb", "test/test_download.rb", "test/test_file.rb", "test/test_file_factory.rb", "test/test_packet.rb", "test/test_protocol.rb", "test/test_request.rb", "test/test_response.rb", "test/test_session.rb", "test/test_upload.rb" ] s.homepage = "https://github.com/net-ssh/net-sftp" s.licenses = ["MIT"] s.require_paths = ["lib"] s.rubyforge_project = "net-sftp" s.rubygems_version = "1.8.25" s.signing_key = "/mnt/gem/gem-private_key.pem" s.summary = "A pure Ruby implementation of the SFTP client protocol" if s.respond_to? :specification_version then s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, [">= 2.6.5"]) s.add_development_dependency(%q, [">= 0"]) s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, [">= 2.6.5"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, [">= 2.6.5"]) s.add_dependency(%q, [">= 0"]) s.add_dependency(%q, [">= 0"]) end end net-sftp-2.1.2/CHANGES.txt0000644000004100000410000000355012144456021015117 0ustar www-datawww-data === 2.1.2 / 07 May 2013 * Fix fragmentation download failure [accardi] === 2.1.0 / 06 Feb 2013 * Added public cert. All gem releases are now signed. See INSTALL in readme. * Remove self-require, it causes a warning in Ruby 1.9.2. [jbarnette] * Allow for upload to use the filename of the local file by default [czarneckid] * Properly handle receiving less data than requested. [thedarkone] * Added option to create directory on directory upload [Pablo Merino] * Remove a warnings in tests [kachick] === 2.0.5 / 19 Aug 2010 * Fixed missing StringIO exception in download! [Toby Bryans, Delano Mandelbaum] === 2.0.4 / 23 Nov 2009 * Fixed frozen string issue in StatusException.message [appoxy] === 2.0.3 / 17 Nov 2009 * Reference the correct Exception class when rescuing errors [Scott Tadman] === 2.0.2 / 1 Feb 2009 * Avoid using 'ensure' in Net::SFTP.start since it causes unfriendly behavior when exceptions are raised [Jamis Buck] === 2.0.1 / 29 May 2008 * Open files in binary mode to appease Windows [Jamis Buck] === 2.0.0 / 1 May 2008 * Make Net::SSH::Connection::Session#sftp accept an argument determining whether or not to block while the SFTP subsystem initializes (defaults to true) [Jamis Buck] * Allow Session#connect to be useful even in the open/opening states by invoking or queuing the callback block [Jamis Buck] * Allow custom properties to be set on upload/download initiation via the :properties option [Jamis Buck] * Custom properties on Download instances [Jamis Buck] * Add #abort! method to Upload and Download operations [Jamis Buck] * More complete support for file-type detection in protocol versions 1-3 [Jamis Buck] === 2.0 Preview Release 2 (1.99.1) / 10 Apr 2008 * Custom properties on Upload instances [Jamis Buck] === 2.0 Preview Release 1 (1.99.0) / 22 Mar 2008 * Rewritten! (Never, ever, do this at home.) New and improved API. net-sftp-2.1.2/test/0000755000004100000410000000000012144456021014262 5ustar www-datawww-datanet-sftp-2.1.2/test/test_session.rb0000644000004100000410000006764212144456021017350 0ustar www-datawww-datarequire "common" class SessionTest < Net::SFTP::TestCase include Net::SFTP::Constants include Net::SFTP::Constants::OpenFlags include Net::SFTP::Constants::PacketTypes (1..6).each do |version| define_method("test_server_reporting_version_#{version}_should_cause_version_#{version}_to_be_used") do expect_sftp_session :server_version => version assert_scripted { sftp.connect! } assert_equal version, sftp.protocol.version end end def test_v1_open_read_only_that_succeeds_should_invoke_callback expect_open("/path/to/file", "r", nil, :server_version => 1) assert_successful_open("/path/to/file") end def test_v1_open_read_only_that_fails_should_invoke_callback expect_open("/path/to/file", "r", nil, :server_version => 1, :fail => 2) assert_command_with_callback(:open, "/path/to/file") do |response| assert !response.ok? assert_equal 2, response.code end end def test_v1_open_write_only_that_succeeds_should_invoke_callback expect_open("/path/to/file", "w", nil, :server_version => 1) assert_successful_open("/path/to/file", "w") end def test_v1_open_read_write_that_succeeds_should_invoke_callback expect_open("/path/to/file", "rw", nil, :server_version => 1) assert_successful_open("/path/to/file", "r+") end def test_v1_open_append_that_succeeds_should_invoke_callback expect_open("/path/to/file", "a", nil, :server_version => 1) assert_successful_open("/path/to/file", "a") end def test_v1_open_with_permissions_should_specify_permissions expect_open("/path/to/file", "r", 0765, :server_version => 1) assert_successful_open("/path/to/file", "r", :permissions => 0765) end def test_v4_open_with_permissions_should_specify_permissions expect_open("/path/to/file", "r", 0765, :server_version => 4) assert_successful_open("/path/to/file", "r", :permissions => 0765) end def test_v5_open_read_only_shuld_invoke_callback expect_open("/path/to/file", "r", 0765, :server_version => 5) assert_successful_open("/path/to/file", "r", :permissions => 0765) end def test_v6_open_with_permissions_should_specify_permissions expect_open("/path/to/file", "r", 0765, :server_version => 6) assert_successful_open("/path/to/file", "r", :permissions => 0765) end def test_open_bang_should_block_and_return_handle expect_open("/path/to/file", "r", nil) handle = assert_synchronous_command(:open!, "/path/to/file", "r") assert_equal "handle", handle end def test_open_bang_should_block_and_raise_exception_on_error expect_open("/path/to/file", "r", nil, :fail => 5) assert_raises(Net::SFTP::StatusException) do assert_synchronous_command(:open!, "/path/to/file", "r") end end def test_close_should_send_close_request_and_invoke_callback expect_sftp_session do |channel| channel.sends_packet(FXP_CLOSE, :long, 0, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:close, "handle") { |r| assert r.ok? } end def test_close_bang_should_block_and_return_response expect_sftp_session do |channel| channel.sends_packet(FXP_CLOSE, :long, 0, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:close!, "handle") assert response.ok? end def test_read_should_send_read_request_and_invoke_callback expect_sftp_session do |channel| channel.sends_packet(FXP_READ, :long, 0, :string, "handle", :int64, 512123, :long, 1024) channel.gets_packet(FXP_DATA, :long, 0, :string, "this is some data!") end assert_command_with_callback(:read, "handle", 512123, 1024) do |response| assert response.ok? assert_equal "this is some data!", response[:data] end end def test_read_bang_should_block_and_return_data expect_sftp_session do |channel| channel.sends_packet(FXP_READ, :long, 0, :string, "handle", :int64, 512123, :long, 1024) channel.gets_packet(FXP_DATA, :long, 0, :string, "this is some data!") end data = assert_synchronous_command(:read!, "handle", 512123, 1024) assert_equal "this is some data!", data end def test_read_bang_should_block_and_return_nil_on_eof expect_sftp_session do |channel| channel.sends_packet(FXP_READ, :long, 0, :string, "handle", :int64, 512123, :long, 1024) channel.gets_packet(FXP_STATUS, :long, 0, :long, 1) end data = assert_synchronous_command(:read!, "handle", 512123, 1024) assert_nil data end def test_write_should_send_write_request_and_invoke_callback expect_sftp_session do |channel| channel.sends_packet(FXP_WRITE, :long, 0, :string, "handle", :int64, 512123, :string, "this is some data!") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:write, "handle", 512123, "this is some data!") do |response| assert response.ok? end end def test_write_bang_should_block_and_return_response expect_sftp_session do |channel| channel.sends_packet(FXP_WRITE, :long, 0, :string, "handle", :int64, 512123, :string, "this is some data!") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:write!, "handle", 512123, "this is some data!") assert response.ok? end def test_v1_lstat_should_send_lstat_request_and_invoke_callback expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_LSTAT, :long, 0, :string, "/path/to/file") channel.gets_packet(FXP_ATTRS, :long, 0, :long, 0xF, :int64, 123456, :long, 1, :long, 2, :long, 0765, :long, 123456789, :long, 234567890) end assert_command_with_callback(:lstat, "/path/to/file") do |response| assert response.ok? assert_equal 123456, response[:attrs].size assert_equal 1, response[:attrs].uid assert_equal 2, response[:attrs].gid assert_equal 0765, response[:attrs].permissions assert_equal 123456789, response[:attrs].atime assert_equal 234567890, response[:attrs].mtime end end def test_v4_lstat_should_send_default_flags_parameter expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_LSTAT, :long, 0, :string, "/path/to/file", :long, 0x800001fd) channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:lstat, "/path/to/file") end def test_v4_lstat_should_honor_flags_parameter expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_LSTAT, :long, 0, :string, "/path/to/file", :long, 0x1) channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:lstat, "/path/to/file", 0x1) end def test_lstat_bang_should_block_and_return_attrs expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_LSTAT, :long, 0, :string, "/path/to/file") channel.gets_packet(FXP_ATTRS, :long, 0, :long, 0xF, :int64, 123456, :long, 1, :long, 2, :long, 0765, :long, 123456789, :long, 234567890) end attrs = assert_synchronous_command(:lstat!, "/path/to/file") assert_equal 123456, attrs.size assert_equal 1, attrs.uid assert_equal 2, attrs.gid assert_equal 0765, attrs.permissions assert_equal 123456789, attrs.atime assert_equal 234567890, attrs.mtime end def test_v1_fstat_should_send_fstat_request_and_invoke_callback expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_FSTAT, :long, 0, :string, "handle") channel.gets_packet(FXP_ATTRS, :long, 0, :long, 0xF, :int64, 123456, :long, 1, :long, 2, :long, 0765, :long, 123456789, :long, 234567890) end assert_command_with_callback(:fstat, "handle") do |response| assert response.ok? assert_equal 123456, response[:attrs].size assert_equal 1, response[:attrs].uid assert_equal 2, response[:attrs].gid assert_equal 0765, response[:attrs].permissions assert_equal 123456789, response[:attrs].atime assert_equal 234567890, response[:attrs].mtime end end def test_v4_fstat_should_send_default_flags_parameter expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_FSTAT, :long, 0, :string, "handle", :long, 0x800001fd) channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:fstat, "handle") end def test_v4_fstat_should_honor_flags_parameter expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_FSTAT, :long, 0, :string, "handle", :long, 0x1) channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:fstat, "handle", 0x1) end def test_fstat_bang_should_block_and_return_attrs expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_FSTAT, :long, 0, :string, "handle") channel.gets_packet(FXP_ATTRS, :long, 0, :long, 0xF, :int64, 123456, :long, 1, :long, 2, :long, 0765, :long, 123456789, :long, 234567890) end attrs = assert_synchronous_command(:fstat!, "handle") assert_equal 123456, attrs.size assert_equal 1, attrs.uid assert_equal 2, attrs.gid assert_equal 0765, attrs.permissions assert_equal 123456789, attrs.atime assert_equal 234567890, attrs.mtime end def test_v1_setstat_should_send_v1_attributes expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_SETSTAT, :long, 0, :string, "/path/to/file", :long, 0xc, :long, 0765, :long, 1234567890, :long, 2345678901) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:setstat, "/path/to/file", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901) end def test_v4_setstat_should_send_v4_attributes expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_SETSTAT, :long, 0, :string, "/path/to/file", :long, 0x2c, :byte, 1, :long, 0765, :int64, 1234567890, :int64, 2345678901) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:setstat, "/path/to/file", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901) end def test_v6_setstat_should_send_v6_attributes expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_SETSTAT, :long, 0, :string, "/path/to/file", :long, 0x102c, :byte, 1, :long, 0765, :int64, 1234567890, :int64, 2345678901, :string, "text/plain") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:setstat, "/path/to/file", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901, :mime_type => "text/plain") end def test_setstat_bang_should_block_and_return_response expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_SETSTAT, :long, 0, :string, "/path/to/file", :long, 0xc, :long, 0765, :long, 1234567890, :long, 2345678901) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:setstat!, "/path/to/file", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901) assert response.ok? end def test_v1_fsetstat_should_send_v1_attributes expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_FSETSTAT, :long, 0, :string, "handle", :long, 0xc, :long, 0765, :long, 1234567890, :long, 2345678901) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:fsetstat, "handle", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901) end def test_v4_fsetstat_should_send_v4_attributes expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_FSETSTAT, :long, 0, :string, "handle", :long, 0x2c, :byte, 1, :long, 0765, :int64, 1234567890, :int64, 2345678901) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:fsetstat, "handle", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901) end def test_v6_fsetstat_should_send_v6_attributes expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_FSETSTAT, :long, 0, :string, "handle", :long, 0x102c, :byte, 1, :long, 0765, :int64, 1234567890, :int64, 2345678901, :string, "text/plain") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:fsetstat, "handle", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901, :mime_type => "text/plain") end def test_fsetstat_bang_should_block_and_return_response expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_FSETSTAT, :long, 0, :string, "handle", :long, 0xc, :long, 0765, :long, 1234567890, :long, 2345678901) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:fsetstat!, "handle", :permissions => 0765, :atime => 1234567890, :mtime => 2345678901) assert response.ok? end def test_opendir_should_send_opendir_request_and_invoke_callback expect_sftp_session do |channel| channel.sends_packet(FXP_OPENDIR, :long, 0, :string, "/path/to/dir") channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") end assert_command_with_callback(:opendir, "/path/to/dir") end def test_opendir_bang_should_block_and_return_handle expect_sftp_session do |channel| channel.sends_packet(FXP_OPENDIR, :long, 0, :string, "/path/to/dir") channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") end handle = assert_synchronous_command(:opendir!, "/path/to/dir") assert_equal "handle", handle end def test_readdir_should_send_readdir_request_and_invoke_callback expect_sftp_session do |channel| channel.sends_packet(FXP_READDIR, :long, 0, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 0, :long, 1) end assert_command_with_callback(:readdir, "handle") { |r| assert r.eof? } end def test_readdir_bang_should_block_and_return_names_array expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_READDIR, :long, 0, :string, "handle") channel.gets_packet(FXP_NAME, :long, 0, :long, 2, :string, "first", :string, "longfirst", :long, 0x0, :string, "next", :string, "longnext", :long, 0x0) end names = assert_synchronous_command(:readdir!, "handle") assert_equal 2, names.length assert_equal %w(first next), names.map { |n| n.name } end def test_remove_should_send_remove_packet expect_sftp_session do |channel| channel.sends_packet(FXP_REMOVE, :long, 0, :string, "/path/to/file") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:remove, "/path/to/file") end def test_remove_bang_should_block_and_return_response expect_sftp_session do |channel| channel.sends_packet(FXP_REMOVE, :long, 0, :string, "/path/to/file") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:remove!, "/path/to/file") assert response.ok? end def test_mkdir_should_send_mkdir_packet expect_sftp_session do |channel| channel.sends_packet(FXP_MKDIR, :long, 0, :string, "/path/to/dir", :long, 0x4, :byte, 1, :long, 0765) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:mkdir, "/path/to/dir", :permissions => 0765) end def test_mkdir_bang_should_block_and_return_response expect_sftp_session do |channel| channel.sends_packet(FXP_MKDIR, :long, 0, :string, "/path/to/dir", :long, 0x4, :byte, 1, :long, 0765) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:mkdir!, "/path/to/dir", :permissions => 0765) assert response.ok? end def test_rmdir_should_send_rmdir_packet expect_sftp_session do |channel| channel.sends_packet(FXP_RMDIR, :long, 0, :string, "/path/to/dir") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:rmdir, "/path/to/dir") end def test_rmdir_bang_should_block_and_return_response expect_sftp_session do |channel| channel.sends_packet(FXP_RMDIR, :long, 0, :string, "/path/to/dir") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:rmdir!, "/path/to/dir") assert response.ok? end def test_realpath_should_send_realpath_packet expect_sftp_session do |channel| channel.sends_packet(FXP_REALPATH, :long, 0, :string, "/path/to/dir") channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:realpath, "/path/to/dir") end def test_realpath_bang_should_block_and_return_names_item expect_sftp_session do |channel| channel.sends_packet(FXP_REALPATH, :long, 0, :string, "/path/to/dir") channel.gets_packet(FXP_NAME, :long, 0, :long, 1, :string, "dir", :long, 0x0, :long, 2) end name = assert_synchronous_command(:realpath!, "/path/to/dir") assert_equal "dir", name.name end def test_v1_stat_should_send_stat_request_and_invoke_callback expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_STAT, :long, 0, :string, "/path/to/file") channel.gets_packet(FXP_ATTRS, :long, 0, :long, 0xF, :int64, 123456, :long, 1, :long, 2, :long, 0765, :long, 123456789, :long, 234567890) end assert_command_with_callback(:stat, "/path/to/file") do |response| assert response.ok? assert_equal 123456, response[:attrs].size assert_equal 1, response[:attrs].uid assert_equal 2, response[:attrs].gid assert_equal 0765, response[:attrs].permissions assert_equal 123456789, response[:attrs].atime assert_equal 234567890, response[:attrs].mtime end end def test_v4_stat_should_send_default_flags_parameter expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_STAT, :long, 0, :string, "/path/to/file", :long, 0x800001fd) channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:stat, "/path/to/file") end def test_v4_stat_should_honor_flags_parameter expect_sftp_session :server_version => 4 do |channel| channel.sends_packet(FXP_STAT, :long, 0, :string, "/path/to/file", :long, 0x1) channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:stat, "/path/to/file", 0x1) end def test_stat_bang_should_block_and_return_attrs expect_sftp_session :server_version => 1 do |channel| channel.sends_packet(FXP_STAT, :long, 0, :string, "/path/to/file") channel.gets_packet(FXP_ATTRS, :long, 0, :long, 0xF, :int64, 123456, :long, 1, :long, 2, :long, 0765, :long, 123456789, :long, 234567890) end attrs = assert_synchronous_command(:stat!, "/path/to/file") assert_equal 123456, attrs.size assert_equal 1, attrs.uid assert_equal 2, attrs.gid assert_equal 0765, attrs.permissions assert_equal 123456789, attrs.atime assert_equal 234567890, attrs.mtime end def test_v1_rename_should_be_unimplemented assert_not_implemented 1, :rename, "from", "to" end def test_v2_rename_should_send_rename_packet expect_sftp_session :server_version => 2 do |channel| channel.sends_packet(FXP_RENAME, :long, 0, :string, "from", :string, "to") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:rename, "from", "to") end def test_v5_rename_should_send_rename_packet_and_default_flags expect_sftp_session :server_version => 5 do |channel| channel.sends_packet(FXP_RENAME, :long, 0, :string, "from", :string, "to", :long, 0) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:rename, "from", "to") end def test_v5_rename_should_send_rename_packet_and_honor_flags expect_sftp_session :server_version => 5 do |channel| channel.sends_packet(FXP_RENAME, :long, 0, :string, "from", :string, "to", :long, 1) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:rename, "from", "to", 1) end def test_rename_bang_should_block_and_return_response expect_sftp_session :server_version => 2 do |channel| channel.sends_packet(FXP_RENAME, :long, 0, :string, "from", :string, "to") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:rename!, "from", "to") assert response.ok? end def test_v2_readlink_should_be_unimplemented assert_not_implemented 2, :readlink, "/path/to/link" end def test_v3_readlink_should_send_readlink_packet expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_READLINK, :long, 0, :string, "/path/to/link") channel.gets_packet(FXP_STATUS, :long, 0, :long, 2) end assert_command_with_callback(:readlink, "/path/to/link") end def test_readlink_bang_should_block_and_return_name expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_READLINK, :long, 0, :string, "/path/to/link") channel.gets_packet(FXP_NAME, :long, 0, :long, 1, :string, "target", :string, "longtarget", :long, 0x0) end name = assert_synchronous_command(:readlink!, "/path/to/link") assert_equal "target", name.name end def test_v2_symlink_should_be_unimplemented assert_not_implemented 2, :symlink, "/path/to/source", "/path/to/link" end def test_v3_symlink_should_send_symlink_packet expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_SYMLINK, :long, 0, :string, "/path/to/source", :string, "/path/to/link") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:symlink, "/path/to/source", "/path/to/link") end def test_v6_symlink_should_send_link_packet expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_LINK, :long, 0, :string, "/path/to/link", :string, "/path/to/source", :bool, true) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:symlink, "/path/to/link", "/path/to/source") end def test_symlink_bang_should_block_and_return_response expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_SYMLINK, :long, 0, :string, "/path/to/source", :string, "/path/to/link") channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:symlink!, "/path/to/source", "/path/to/link") assert response.ok? end def test_v5_link_should_be_unimplemented assert_not_implemented 5, :link, "/path/to/source", "/path/to/link", true end def test_v6_link_should_send_link_packet expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_LINK, :long, 0, :string, "/path/to/link", :string, "/path/to/source", :bool, true) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:link, "/path/to/link", "/path/to/source", true) end def test_link_bang_should_block_and_return_response expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_LINK, :long, 0, :string, "/path/to/link", :string, "/path/to/source", :bool, true) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:link!, "/path/to/link", "/path/to/source", true) assert response.ok? end def test_v5_block_should_be_unimplemented assert_not_implemented 5, :block, "handle", 12345, 67890, 0xabcd end def test_v6_block_should_send_block_packet expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_BLOCK, :long, 0, :string, "handle", :int64, 12345, :int64, 67890, :long, 0xabcd) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:block, "handle", 12345, 67890, 0xabcd) end def test_block_bang_should_block_and_return_response expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_BLOCK, :long, 0, :string, "handle", :int64, 12345, :int64, 67890, :long, 0xabcd) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:block!, "handle", 12345, 67890, 0xabcd) assert response.ok? end def test_v5_unblock_should_be_unimplemented assert_not_implemented 5, :unblock, "handle", 12345, 67890 end def test_v6_unblock_should_send_block_packet expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_UNBLOCK, :long, 0, :string, "handle", :int64, 12345, :int64, 67890) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end assert_command_with_callback(:unblock, "handle", 12345, 67890) end def test_unblock_bang_should_block_and_return_response expect_sftp_session :server_version => 6 do |channel| channel.sends_packet(FXP_UNBLOCK, :long, 0, :string, "handle", :int64, 12345, :int64, 67890) channel.gets_packet(FXP_STATUS, :long, 0, :long, 0) end response = assert_synchronous_command(:unblock!, "handle", 12345, 67890) assert response.ok? end private def assert_not_implemented(server_version, command, *args) expect_sftp_session :server_version => 1 sftp.connect! assert_raises(NotImplementedError) { sftp.send(command, *args) } end def assert_command_with_callback(command, *args) called = false assert_scripted_command do sftp.send(command, *args) do |response| called = true yield response if block_given? end end assert called, "expected callback to be invoked, but it wasn't" end def assert_synchronous_command(command, *args) assert_scripted_command do sequence = [:start] result = sftp.send(command, *args) do |response| sequence << :done yield response if block_given? end sequence << :after assert_equal [:start, :done, :after], sequence, "expected #{command} to be synchronous" return result end end def assert_successful_open(*args) assert_command_with_callback(:open, *args) do |response| assert response.ok? assert_equal "handle", response[:handle] end end def expect_open(path, mode, perms, options={}) version = options[:server_version] || 6 fail = options.delete(:fail) attrs = [:long, perms ? 0x4 : 0] attrs += [:byte, 1] if version >= 4 attrs += [:long, perms] if perms expect_sftp_session(options) do |channel| if version >= 5 flags, access = case mode when "r" then [FV5::OPEN_EXISTING, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES] when "w" then [FV5::CREATE_TRUNCATE, ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES] when "rw" then [FV5::OPEN_OR_CREATE, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES | ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES] when "a" then [FV5::OPEN_OR_CREATE | FV5::APPEND_DATA, ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES | ACE::Mask::APPEND_DATA] else raise ArgumentError, "unsupported mode #{mode.inspect}" end channel.sends_packet(FXP_OPEN, :long, 0, :string, path, :long, access, :long, flags, *attrs) else flags = case mode when "r" then FV1::READ when "w" then FV1::WRITE | FV1::TRUNC | FV1::CREAT when "rw" then FV1::WRITE | FV1::READ when "a" then FV1::APPEND | FV1::WRITE | FV1::CREAT else raise ArgumentError, "unsupported mode #{mode.inspect}" end channel.sends_packet(FXP_OPEN, :long, 0, :string, path, :long, flags, *attrs) end if fail channel.gets_packet(FXP_STATUS, :long, 0, :long, fail) else channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") end end end end net-sftp-2.1.2/test/test_download.rb0000644000004100000410000002640212144456021017461 0ustar www-datawww-datarequire "common" class DownloadTest < Net::SFTP::TestCase FXP_DATA_CHUNK_SIZE = 1024 def setup prepare_progress! end def test_download_file_should_transfer_remote_to_local local = "/path/to/local" remote = "/path/to/remote" text = "this is some text\n" expect_file_transfer(remote, text) file = StringIO.new File.stubs(:open).with(local, "wb").returns(file) assert_scripted_command { sftp.download(remote, local) } assert_equal text, file.string end def test_download_file_should_transfer_remote_to_local_in_spite_of_fragmentation local = "/path/to/local" remote = "/path/to/remote" text = "this is some text\n" expect_file_transfer(remote, text, :fragment_len => 1) file = StringIO.new File.stubs(:open).with(local, "wb").returns(file) assert_scripted_command { sftp.download(remote, local) } assert_equal text, file.string end def test_download_large_file_should_transfer_remote_to_local local = "/path/to/local" remote = "/path/to/remote" text = "0123456789" * 1024 file = prepare_large_file_download(local, remote, text) assert_scripted_command { sftp.download(remote, local, :read_size => 1024) } assert_equal text, file.string end def test_download_large_file_should_handle_too_large_read_size local = "/path/to/local" remote = "/path/to/remote" text = "0123456789" * 1024 # some servers put upper bound on the max read_size value and send less data than requested too_large_read_size = FXP_DATA_CHUNK_SIZE + 1 file = prepare_large_file_download(local, remote, text, too_large_read_size) assert_scripted_command { sftp.download(remote, local, :read_size => too_large_read_size) } assert_equal text, file.string end def test_download_large_file_with_progress_should_report_progress local = "/path/to/local" remote = "/path/to/remote" text = "0123456789" * 1024 file = prepare_large_file_download(local, remote, text) assert_scripted_command do sftp.download(remote, local, :read_size => 1024) do |*args| record_progress(args) end end assert_equal text, file.string assert_progress_reported_open :remote => "/path/to/remote" assert_progress_reported_get 0, 1024 assert_progress_reported_get 1024, 1024 assert_progress_reported_get 2048, 1024 assert_progress_reported_get 3072, 1024 assert_progress_reported_get 4096, 1024 assert_progress_reported_get 5120, 1024 assert_progress_reported_get 6144, 1024 assert_progress_reported_get 7168, 1024 assert_progress_reported_get 8192, 1024 assert_progress_reported_get 9216, 1024 assert_progress_reported_close assert_progress_reported_finish assert_no_more_reported_events end def test_download_directory_should_mirror_directory_locally file1, file2 = prepare_directory_tree_download("/path/to/local", "/path/to/remote") assert_scripted_command do sftp.download("/path/to/remote", "/path/to/local", :recursive => true) end assert_equal "contents of file1", file1.string assert_equal "contents of file2", file2.string end def test_download_directory_with_progress_should_report_progress file1, file2 = prepare_directory_tree_download("/path/to/local", "/path/to/remote") assert_scripted_command do sftp.download("/path/to/remote", "/path/to/local", :recursive => true) do |*args| record_progress(args) end end assert_equal "contents of file1", file1.string assert_equal "contents of file2", file2.string assert_progress_reported_mkdir "/path/to/local" assert_progress_reported_mkdir "/path/to/local/subdir1" assert_progress_reported_open :remote => "/path/to/remote/file1" assert_progress_reported_open :remote => "/path/to/remote/subdir1/file2" assert_progress_reported_get 0, "contents of file1" assert_progress_reported_close :remote => "/path/to/remote/file1" assert_progress_reported_get 0, "contents of file2" assert_progress_reported_close :remote => "/path/to/remote/subdir1/file2" assert_progress_reported_finish assert_no_more_reported_events end def test_download_file_should_transfer_remote_to_local_buffer remote = "/path/to/remote" text = "this is some text\n" expect_file_transfer(remote, text) local = StringIO.new assert_scripted_command { sftp.download(remote, local) } assert_equal text, local.string end def test_download_directory_to_buffer_should_fail expect_sftp_session :server_version => 3 assert_raises(ArgumentError) { sftp.download("/path/to/remote", StringIO.new, :recursive => true) } end private def expect_file_transfer(remote, text, opts={}) expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_OPEN, :long, 0, :string, remote, :long, 0x01, :long, 0) channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") channel.sends_packet(FXP_READ, :long, 1, :string, "handle", :int64, 0, :long, 32_000) channel.gets_packet_in_two(opts[:fragment_len], FXP_DATA, :long, 1, :string, text) channel.sends_packet(FXP_READ, :long, 2, :string, "handle", :int64, text.bytesize, :long, 32_000) channel.gets_packet(FXP_STATUS, :long, 2, :long, 1) channel.sends_packet(FXP_CLOSE, :long, 3, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 3, :long, 0) end end def prepare_large_file_download(local, remote, text, requested_chunk_size = FXP_DATA_CHUNK_SIZE) expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_OPEN, :long, 0, :string, remote, :long, 0x01, :long, 0) channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") offset = 0 data_packet_count = (text.bytesize / FXP_DATA_CHUNK_SIZE.to_f).ceil data_packet_count.times do |n| payload = text[n*FXP_DATA_CHUNK_SIZE,FXP_DATA_CHUNK_SIZE] channel.sends_packet(FXP_READ, :long, n+1, :string, "handle", :int64, offset, :long, requested_chunk_size) offset += payload.bytesize channel.gets_packet(FXP_DATA, :long, n+1, :string, payload) end channel.sends_packet(FXP_READ, :long, data_packet_count + 1, :string, "handle", :int64, offset, :long, requested_chunk_size) channel.gets_packet(FXP_STATUS, :long, data_packet_count + 1, :long, 1) channel.sends_packet(FXP_CLOSE, :long, data_packet_count + 2, :string, "handle") channel.gets_packet(FXP_STATUS, :long, data_packet_count + 2, :long, 0) end file = StringIO.new File.stubs(:open).with(local, "wb").returns(file) return file end # 0:OPENDIR(remote) -> # <- 0:HANDLE("dir1") # 1:READDIR("dir1") -> # <- 1:NAME("..", ".", "subdir1", "file1") # 2:OPENDIR(remote/subdir1) -> # 3:OPEN(remote/file1) -> # 4:READDIR("dir1") -> # <- 2:HANDLE("dir2") # 5:READDIR("dir2") -> # <- 3:HANDLE("file1") # 6:READ("file1", 0, 32k) -> # <- 4:STATUS(1) # 7:CLOSE("dir1") -> # <- 5:NAME("..", ".", "file2") # 8:OPEN(remote/subdir1/file2) -> # 9:READDIR("dir2") -> # <- 6:DATA("blah blah blah") # 10:READ("file1", n, 32k) # <- 7:STATUS(0) # <- 8:HANDLE("file2") # 11:READ("file2", 0, 32k) -> # <- 9:STATUS(1) # 12:CLOSE("dir2") -> # <- 10:STATUS(1) # 13:CLOSE("file1") -> # <- 11:DATA("blah blah blah") # 14:READ("file2", n, 32k) -> # <- 12:STATUS(0) # <- 13:STATUS(0) # <- 14:STATUS(1) # 15:CLOSE("file2") -> # <- 15:STATUS(0) def prepare_directory_tree_download(local, remote) file1_contents = "contents of file1" file2_contents = "contents of file2" expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_OPENDIR, :long, 0, :string, remote) channel.gets_packet(FXP_HANDLE, :long, 0, :string, "dir1") channel.sends_packet(FXP_READDIR, :long, 1, :string, "dir1") channel.gets_packet(FXP_NAME, :long, 1, :long, 4, :string, "..", :string, "drwxr-xr-x 4 bob bob 136 Aug 1 ..", :long, 0x04, :long, 040755, :string, ".", :string, "drwxr-xr-x 4 bob bob 136 Aug 1 .", :long, 0x04, :long, 040755, :string, "subdir1", :string, "drwxr-xr-x 4 bob bob 136 Aug 1 subdir1", :long, 0x04, :long, 040755, :string, "file1", :string, "-rw-rw-r-- 1 bob bob 100 Aug 1 file1", :long, 0x04, :long, 0100644) channel.sends_packet(FXP_OPENDIR, :long, 2, :string, File.join(remote, "subdir1")) channel.sends_packet(FXP_OPEN, :long, 3, :string, File.join(remote, "file1"), :long, 0x01, :long, 0) channel.sends_packet(FXP_READDIR, :long, 4, :string, "dir1") channel.gets_packet(FXP_HANDLE, :long, 2, :string, "dir2") channel.sends_packet(FXP_READDIR, :long, 5, :string, "dir2") channel.gets_packet(FXP_HANDLE, :long, 3, :string, "file1") channel.sends_packet(FXP_READ, :long, 6, :string, "file1", :int64, 0, :long, 32_000) channel.gets_packet(FXP_STATUS, :long, 4, :long, 1) channel.sends_packet(FXP_CLOSE, :long, 7, :string, "dir1") channel.gets_packet(FXP_NAME, :long, 5, :long, 3, :string, "..", :string, "drwxr-xr-x 4 bob bob 136 Aug 1 ..", :long, 0x04, :long, 040755, :string, ".", :string, "drwxr-xr-x 4 bob bob 136 Aug 1 .", :long, 0x04, :long, 040755, :string, "file2", :string, "-rw-rw-r-- 1 bob bob 100 Aug 1 file2", :long, 0x04, :long, 0100644) channel.sends_packet(FXP_OPEN, :long, 8, :string, File.join(remote, "subdir1", "file2"), :long, 0x01, :long, 0) channel.sends_packet(FXP_READDIR, :long, 9, :string, "dir2") channel.gets_packet(FXP_DATA, :long, 6, :string, file1_contents) channel.sends_packet(FXP_READ, :long, 10, :string, "file1", :int64, file1_contents.bytesize, :long, 32_000) channel.gets_packet(FXP_STATUS, :long, 7, :long, 0) channel.gets_packet(FXP_HANDLE, :long, 8, :string, "file2") channel.sends_packet(FXP_READ, :long, 11, :string, "file2", :int64, 0, :long, 32_000) channel.gets_packet(FXP_STATUS, :long, 9, :long, 1) channel.sends_packet(FXP_CLOSE, :long, 12, :string, "dir2") channel.gets_packet(FXP_STATUS, :long, 10, :long, 1) channel.sends_packet(FXP_CLOSE, :long, 13, :string, "file1") channel.gets_packet(FXP_DATA, :long, 11, :string, file2_contents) channel.sends_packet(FXP_READ, :long, 14, :string, "file2", :int64, file2_contents.bytesize, :long, 32_000) channel.gets_packet(FXP_STATUS, :long, 12, :long, 0) channel.gets_packet(FXP_STATUS, :long, 13, :long, 0) channel.gets_packet(FXP_STATUS, :long, 14, :long, 1) channel.sends_packet(FXP_CLOSE, :long, 15, :string, "file2") channel.gets_packet(FXP_STATUS, :long, 15, :long, 0) end File.expects(:directory?).with(local).returns(false) File.expects(:directory?).with(File.join(local, "subdir1")).returns(false) Dir.expects(:mkdir).with(local) Dir.expects(:mkdir).with(File.join(local, "subdir1")) file1 = StringIO.new file2 = StringIO.new File.expects(:open).with(File.join(local, "file1"), "wb").returns(file1) File.expects(:open).with(File.join(local, "subdir1", "file2"), "wb").returns(file2) [file1, file2] end endnet-sftp-2.1.2/test/test_request.rb0000644000004100000410000000404212144456021017336 0ustar www-datawww-datarequire 'common' class RequestTest < Net::SFTP::TestCase def test_property_setter_should_symbolize_key request = Net::SFTP::Request.new(stub("session"), :open, 1) request["key"] = :value assert_equal :value, request['key'] assert_equal :value, request[:key] assert_equal :value, request.properties[:key] assert_nil request.properties['key'] end def test_pending_should_query_pending_requests_of_session session = stub("session", :pending_requests => {1 => true}) request = Net::SFTP::Request.new(session, :open, 1) assert request.pending? request = Net::SFTP::Request.new(session, :open, 2) assert !request.pending? end def test_wait_should_run_loop_while_pending_and_return_self session = MockSession.new request = Net::SFTP::Request.new(session, :open, 1) request.expects(:pending?).times(4).returns(true, true, true, false) assert_equal 0, session.loops assert_equal request, request.wait assert_equal 4, session.loops end def test_respond_to_should_set_response_property packet = stub("packet", :type => 1) session = stub("session", :protocol => mock("protocol")) session.protocol.expects(:parse).with(packet).returns({}) request = Net::SFTP::Request.new(session, :open, 1) assert_nil request.response request.respond_to(packet) assert_instance_of Net::SFTP::Response, request.response end def test_respond_to_with_callback_should_invoke_callback packet = stub("packet", :type => 1) session = stub("session", :protocol => mock("protocol")) session.protocol.expects(:parse).with(packet).returns({}) called = false request = Net::SFTP::Request.new(session, :open, 1) do |response| called = true assert_equal request.response, response end request.respond_to(packet) assert called end private class MockSession attr_reader :loops def initialize @loops = 0 end def loop while true @loops += 1 break unless yield end end end endnet-sftp-2.1.2/test/common.rb0000644000004100000410000001047412144456021016105 0ustar www-datawww-datarequire 'test/unit' require 'mocha/setup' require 'stringio' begin require 'net/ssh' require 'net/ssh/version' raise LoadError, "wrong version" unless Net::SSH::Version::STRING >= '1.99.0' rescue LoadError begin gem 'net-ssh', ">= 2.0.0" require 'net/ssh' rescue LoadError => e abort "could not load net/ssh v2 (#{e.inspect})" end end $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" require 'net/sftp' require 'net/sftp/constants' require 'net/ssh/test' class Net::SFTP::TestCase < Test::Unit::TestCase include Net::SFTP::Constants::PacketTypes include Net::SSH::Test def default_test # do nothing, this is just hacky-hack to work around Test::Unit's # insistence that all TestCase subclasses have at least one test # method defined. end protected def raw(*args) Net::SSH::Buffer.from(*args).to_s end def sftp(options={}) @sftp ||= Net::SFTP::Session.new(connection(options)) end def expect_sftp_session(opts={}) story do |session| channel = session.opens_channel channel.sends_subsystem("sftp") channel.sends_packet(FXP_INIT, :long, opts[:client_version] || Net::SFTP::Session::HIGHEST_PROTOCOL_VERSION_SUPPORTED) channel.gets_packet(FXP_VERSION, :long, opts[:server_version] || Net::SFTP::Session::HIGHEST_PROTOCOL_VERSION_SUPPORTED) yield channel if block_given? end end def assert_scripted_command assert_scripted do sftp.connect! yield sftp.loop end end def assert_progress_reported_open(expect={}) assert_progress_reported(:open, expect) end def assert_progress_reported_put(offset, data, expect={}) assert_equal offset, current_event[3] if offset assert_equal data, current_event[4] if data assert_progress_reported(:put, expect) end def assert_progress_reported_get(offset, data, expect={}) assert_equal offset, current_event[3] if offset if data.is_a?(Fixnum) assert_equal data, current_event[4].length elsif data assert_equal data, current_event[4] end assert_progress_reported(:get, expect) end def assert_progress_reported_close(expect={}) assert_progress_reported(:close, expect) end def assert_progress_reported_mkdir(dir) assert_equal dir, current_event[2] assert_progress_reported(:mkdir) end def assert_progress_reported_finish assert_progress_reported(:finish) end def assert_progress_reported(event, expect={}) assert_equal event, current_event[0] expect.each do |key, value| assert_equal value, current_event[2].send(key) end next_event! end def assert_no_more_reported_events assert @progress.empty?, "expected #{@progress.empty?} to be empty" end def prepare_progress! @progress = [] end def record_progress(event) @progress << event end def current_event @progress.first end def next_event! @progress.shift end end class Net::SSH::Test::Channel def gets_packet(type, *args) gets_data(sftp_packet(type, *args)) end def gets_packet_in_two(fragment_len, type, *args) fragment_len ||= 0 whole_packet = sftp_packet(type, *args) if 0 < fragment_len && fragment_len < whole_packet.length gets_data(whole_packet[0, whole_packet.length - fragment_len]) gets_data(whole_packet[-fragment_len..-1]) else gets_data(whole_packet) end end def sends_packet(type, *args) sends_data(sftp_packet(type, *args)) end private def sftp_packet(type, *args) data = Net::SSH::Buffer.from(*args) Net::SSH::Buffer.from(:long, data.length+1, :byte, type, :raw, data).to_s end end class ProgressHandler def initialize(progress_ref) @progress = progress_ref end def on_open(*args) @progress << [:open, *args] end def on_put(*args) @progress << [:put, *args] end def on_close(*args) @progress << [:close, *args] end def on_finish(*args) @progress << [:finish, *args] end end # "prime the pump", so to speak: predefine the modules we need so we can # define the test classes in a more elegant short-hand. module Protocol module V01; end module V02; end module V03; end module V04; end module V05; end module V06; end end net-sftp-2.1.2/test/test_upload.rb0000644000004100000410000002647112144456021017144 0ustar www-datawww-datarequire "common" class UploadTest < Net::SFTP::TestCase def setup prepare_progress! end def test_upload_file_should_send_file_contents expect_file_transfer("/path/to/local", "/path/to/remote", "here are the contents") assert_scripted_command { sftp.upload("/path/to/local", "/path/to/remote") } end def test_upload_file_without_remote_uses_filename_of_local_file expect_file_transfer("/path/to/local", "local", "here are the contents") assert_scripted_command do sftp.upload("/path/to/local") { |*args| record_progress(args) } end assert_progress_reported_open(:remote => "local") assert_progress_reported_put(0, "here are the contents", :remote => "local") assert_progress_reported_close(:remote => "local") assert_progress_reported_finish assert_no_more_reported_events end def test_upload_file_with_progress_should_report_progress expect_file_transfer("/path/to/local", "/path/to/remote", "here are the contents") assert_scripted_command do sftp.upload("/path/to/local", "/path/to/remote") { |*args| record_progress(args) } end assert_progress_reported_open(:remote => "/path/to/remote") assert_progress_reported_put(0, "here are the contents", :remote => "/path/to/remote") assert_progress_reported_close(:remote => "/path/to/remote") assert_progress_reported_finish assert_no_more_reported_events end def test_upload_file_with_progress_handler_should_report_progress expect_file_transfer("/path/to/local", "/path/to/remote", "here are the contents") assert_scripted_command do sftp.upload("/path/to/local", "/path/to/remote", :progress => ProgressHandler.new(@progress)) end assert_progress_reported_open(:remote => "/path/to/remote") assert_progress_reported_put(0, "here are the contents", :remote => "/path/to/remote") assert_progress_reported_close(:remote => "/path/to/remote") assert_progress_reported_finish assert_no_more_reported_events end def test_upload_file_should_read_chunks_of_size(requested_size=nil) size = requested_size || Net::SFTP::Operations::Upload::DEFAULT_READ_SIZE expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_OPEN, :long, 0, :string, "/path/to/remote", :long, 0x1A, :long, 0) channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") channel.sends_packet(FXP_WRITE, :long, 1, :string, "handle", :int64, 0, :string, "a" * size) channel.sends_packet(FXP_WRITE, :long, 2, :string, "handle", :int64, size, :string, "b" * size) channel.sends_packet(FXP_WRITE, :long, 3, :string, "handle", :int64, size*2, :string, "c" * size) channel.gets_packet(FXP_STATUS, :long, 1, :long, 0) channel.sends_packet(FXP_WRITE, :long, 4, :string, "handle", :int64, size*3, :string, "d" * size) channel.gets_packet(FXP_STATUS, :long, 2, :long, 0) channel.sends_packet(FXP_CLOSE, :long, 5, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 3, :long, 0) channel.gets_packet(FXP_STATUS, :long, 4, :long, 0) channel.gets_packet(FXP_STATUS, :long, 5, :long, 0) end expect_file("/path/to/local", "a" * size + "b" * size + "c" * size + "d" * size) assert_scripted_command do opts = {} opts[:read_size] = size if requested_size sftp.upload("/path/to/local", "/path/to/remote", opts) end end def test_upload_file_with_custom_read_size_should_read_chunks_of_that_size test_upload_file_should_read_chunks_of_size(100) end def test_upload_file_with_custom_requests_should_start_that_many_writes size = Net::SFTP::Operations::Upload::DEFAULT_READ_SIZE expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_OPEN, :long, 0, :string, "/path/to/remote", :long, 0x1A, :long, 0) channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") channel.sends_packet(FXP_WRITE, :long, 1, :string, "handle", :int64, 0, :string, "a" * size) channel.sends_packet(FXP_WRITE, :long, 2, :string, "handle", :int64, size, :string, "b" * size) channel.sends_packet(FXP_WRITE, :long, 3, :string, "handle", :int64, size*2, :string, "c" * size) channel.sends_packet(FXP_WRITE, :long, 4, :string, "handle", :int64, size*3, :string, "d" * size) channel.gets_packet(FXP_STATUS, :long, 1, :long, 0) channel.sends_packet(FXP_CLOSE, :long, 5, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 2, :long, 0) channel.gets_packet(FXP_STATUS, :long, 3, :long, 0) channel.gets_packet(FXP_STATUS, :long, 4, :long, 0) channel.gets_packet(FXP_STATUS, :long, 5, :long, 0) end expect_file("/path/to/local", "a" * size + "b" * size + "c" * size + "d" * size) assert_scripted_command do sftp.upload("/path/to/local", "/path/to/remote", :requests => 3) end end def test_upload_directory_should_mirror_directory_structure_remotely prepare_directory assert_scripted_command do sftp.upload("/path/to/local", "/path/to/remote", :mkdir => true) end end def test_upload_directory_with_handler_should_report_progress prepare_directory assert_scripted_command do sftp.upload("/path/to/local", "/path/to/remote", :mkdir => true) { |*args| record_progress(args) } end assert_progress_reported_open(:remote => "/path/to/remote/file1") assert_progress_reported_open(:remote => "/path/to/remote/file2") assert_progress_reported_open(:remote => "/path/to/remote/file3") assert_progress_reported_mkdir("/path/to/remote/subdir") assert_progress_reported_open(:remote => "/path/to/remote/subdir/other1") assert_progress_reported_open(:remote => "/path/to/remote/subdir/other2") assert_progress_reported_put(0, "contents of file1", :remote => "/path/to/remote/file1") assert_progress_reported_put(0, "contents of file2", :remote => "/path/to/remote/file2") assert_progress_reported_put(0, "contents of file3", :remote => "/path/to/remote/file3") assert_progress_reported_close(:remote => "/path/to/remote/file1") assert_progress_reported_put(0, "contents of other1", :remote => "/path/to/remote/subdir/other1") assert_progress_reported_put(0, "contents of other2", :remote => "/path/to/remote/subdir/other2") assert_progress_reported_close(:remote => "/path/to/remote/file2") assert_progress_reported_close(:remote => "/path/to/remote/file3") assert_progress_reported_close(:remote => "/path/to/remote/subdir/other1") assert_progress_reported_close(:remote => "/path/to/remote/subdir/other2") assert_progress_reported_finish assert_no_more_reported_events end def test_upload_io_should_send_io_as_file expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_OPEN, :long, 0, :string, "/path/to/remote", :long, 0x1A, :long, 0) channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") channel.sends_packet(FXP_WRITE, :long, 1, :string, "handle", :int64, 0, :string, "this is some text") channel.sends_packet(FXP_CLOSE, :long, 2, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 1, :long, 0) channel.gets_packet(FXP_STATUS, :long, 2, :long, 0) end assert_scripted_command do sftp.upload(StringIO.new("this is some text"), "/path/to/remote") end end private def prepare_directory expect_directory("/path/to/local", %w(. .. file1 file2 file3 subdir)) expect_directory("/path/to/local/subdir", %w(. .. other1 other2)) expect_file("/path/to/local/file1", "contents of file1") expect_file("/path/to/local/file2", "contents of file2") expect_file("/path/to/local/file3", "contents of file3") expect_file("/path/to/local/subdir/other1", "contents of other1") expect_file("/path/to/local/subdir/other2", "contents of other2") expect_sftp_session :server_version => 3 do |ch| ch.sends_packet(FXP_MKDIR, :long, 0, :string, "/path/to/remote", :long, 0) ch.gets_packet(FXP_STATUS, :long, 0, :long, 0) ch.sends_packet(FXP_OPEN, :long, 1, :string, "/path/to/remote/file1", :long, 0x1A, :long, 0) ch.sends_packet(FXP_OPEN, :long, 2, :string, "/path/to/remote/file2", :long, 0x1A, :long, 0) ch.sends_packet(FXP_OPEN, :long, 3, :string, "/path/to/remote/file3", :long, 0x1A, :long, 0) ch.sends_packet(FXP_MKDIR, :long, 4, :string, "/path/to/remote/subdir", :long, 0) ch.sends_packet(FXP_OPEN, :long, 5, :string, "/path/to/remote/subdir/other1", :long, 0x1A, :long, 0) ch.sends_packet(FXP_OPEN, :long, 6, :string, "/path/to/remote/subdir/other2", :long, 0x1A, :long, 0) ch.gets_packet(FXP_HANDLE, :long, 1, :string, "hfile1") ch.sends_packet(FXP_WRITE, :long, 7, :string, "hfile1", :int64, 0, :string, "contents of file1") ch.gets_packet(FXP_HANDLE, :long, 2, :string, "hfile2") ch.sends_packet(FXP_WRITE, :long, 8, :string, "hfile2", :int64, 0, :string, "contents of file2") ch.gets_packet(FXP_HANDLE, :long, 3, :string, "hfile3") ch.sends_packet(FXP_WRITE, :long, 9, :string, "hfile3", :int64, 0, :string, "contents of file3") ch.gets_packet(FXP_STATUS, :long, 4, :long, 0) ch.gets_packet(FXP_HANDLE, :long, 5, :string, "hother1") ch.sends_packet(FXP_CLOSE, :long, 10, :string, "hfile1") ch.sends_packet(FXP_WRITE, :long, 11, :string, "hother1", :int64, 0, :string, "contents of other1") ch.gets_packet(FXP_HANDLE, :long, 6, :string, "hother2") ch.sends_packet(FXP_WRITE, :long, 12, :string, "hother2", :int64, 0, :string, "contents of other2") ch.gets_packet(FXP_STATUS, :long, 7, :long, 0) ch.sends_packet(FXP_CLOSE, :long, 13, :string, "hfile2") ch.gets_packet(FXP_STATUS, :long, 8, :long, 0) ch.sends_packet(FXP_CLOSE, :long, 14, :string, "hfile3") ch.gets_packet(FXP_STATUS, :long, 9, :long, 0) ch.sends_packet(FXP_CLOSE, :long, 15, :string, "hother1") ch.gets_packet(FXP_STATUS, :long, 10, :long, 0) ch.sends_packet(FXP_CLOSE, :long, 16, :string, "hother2") ch.gets_packet(FXP_STATUS, :long, 11, :long, 0) ch.gets_packet(FXP_STATUS, :long, 12, :long, 0) ch.gets_packet(FXP_STATUS, :long, 13, :long, 0) ch.gets_packet(FXP_STATUS, :long, 14, :long, 0) ch.gets_packet(FXP_STATUS, :long, 15, :long, 0) ch.gets_packet(FXP_STATUS, :long, 16, :long, 0) end end def expect_file(path, data) File.stubs(:directory?).with(path).returns(false) File.stubs(:exists?).with(path).returns(true) file = StringIO.new(data) file.stubs(:stat).returns(stub("stat", :size => data.length)) File.stubs(:open).with(path, "rb").returns(file) end def expect_directory(path, entries) Dir.stubs(:entries).with(path).returns(entries) File.stubs(:directory?).with(path).returns(true) end def expect_file_transfer(local, remote, data) expect_sftp_session :server_version => 3 do |channel| channel.sends_packet(FXP_OPEN, :long, 0, :string, remote, :long, 0x1A, :long, 0) channel.gets_packet(FXP_HANDLE, :long, 0, :string, "handle") channel.sends_packet(FXP_WRITE, :long, 1, :string, "handle", :int64, 0, :string, data) channel.sends_packet(FXP_CLOSE, :long, 2, :string, "handle") channel.gets_packet(FXP_STATUS, :long, 1, :long, 0) channel.gets_packet(FXP_STATUS, :long, 2, :long, 0) end expect_file(local, data) end end net-sftp-2.1.2/test/test_response.rb0000644000004100000410000000363312144456021017511 0ustar www-datawww-datarequire 'common' class ResponseTest < Net::SFTP::TestCase def test_code_should_default_to_FX_OK response = Net::SFTP::Response.new(mock("response")) assert_equal Net::SFTP::Response::FX_OK, response.code end def test_brackets_should_symbolize_key response = Net::SFTP::Response.new(mock("response"), :handle => "foo") assert_equal "foo", response['handle'] end def test_to_s_with_nil_message_should_show_default_message response = Net::SFTP::Response.new(mock("response"), :code => 14) assert_equal "no space on filesystem (14)", response.to_s end def test_to_s_with_empty_message_should_show_default_message response = Net::SFTP::Response.new(mock("response"), :code => 14, :message => "") assert_equal "no space on filesystem (14)", response.to_s end def test_to_s_with_default_message_should_show_default_message response = Net::SFTP::Response.new(mock("response"), :code => 14, :message => "no space on filesystem") assert_equal "no space on filesystem (14)", response.to_s end def test_to_s_with_explicit_message_should_show_explicit_message response = Net::SFTP::Response.new(mock("response"), :code => 14, :message => "out of space") assert_equal "out of space (no space on filesystem, 14)", response.to_s end def test_ok_should_be_true_when_code_is_FX_OK response = Net::SFTP::Response.new(mock("response")) assert_equal true, response.ok? end def test_ok_should_be_false_when_code_is_not_FX_OK response = Net::SFTP::Response.new(mock("response"), :code => 14) assert_equal false, response.ok? end def test_eof_should_be_true_when_code_is_FX_EOF response = Net::SFTP::Response.new(mock("response"), :code => 1) assert_equal true, response.eof? end def test_eof_should_be_false_when_code_is_not_FX_EOF response = Net::SFTP::Response.new(mock("response"), :code => 14) assert_equal false, response.eof? end end net-sftp-2.1.2/test/test_all.rb0000644000004100000410000000041412144456021016415 0ustar www-datawww-data# $ ruby -I../net-ssh/lib -Ilib -Itest -rrubygems test/test_all.rb #require 'net/ssh' #puts Net::SSH::Version::CURRENT require 'common' Dir.chdir(File.dirname(__FILE__)) do Dir['**/test_*.rb'].each { |file| require(file) unless file == File.basename(__FILE__) } endnet-sftp-2.1.2/test/test_dir.rb0000644000004100000410000000405012144456021016423 0ustar www-datawww-datarequire 'common' class DirOperationsTest < Net::SFTP::TestCase def setup @sftp = mock("sftp") @dir = Net::SFTP::Operations::Dir.new(@sftp) end def test_foreach_should_iterate_over_all_entries_in_directory @sftp.expects(:opendir!).with("/path/to/remote").returns("handle") @sftp.expects(:readdir!).with("handle").returns([:e1, :e2, :e3], [:e4, :e5], nil).times(3) @sftp.expects(:close!).with("handle") entries = [] @dir.foreach("/path/to/remote") { |entry| entries << entry } assert_equal [:e1, :e2, :e3, :e4, :e5], entries end def test_entries_should_return_all_entries_in_a_single_array @sftp.expects(:opendir!).with("/path/to/remote").returns("handle") @sftp.expects(:readdir!).with("handle").returns([:e1, :e2, :e3], [:e4, :e5], nil).times(3) @sftp.expects(:close!).with("handle") assert_equal [:e1, :e2, :e3, :e4, :e5], @dir.entries("/path/to/remote") end def test_glob_should_search_under_path_for_matching_entries @sftp.expects(:opendir!).with("/path/to/remote").returns("handle") @sftp.expects(:opendir!).with("/path/to/remote/e3").returns("handle-e3") @sftp.expects(:opendir!).with("/path/to/remote/e5").returns("handle-e5") @sftp.expects(:readdir!).with("handle").returns([n(".", true), n("..", true), n("e1"), n("e2"), n("e3", true)], [n("e4"), n("e5", true)], nil).times(3) @sftp.expects(:readdir!).with("handle-e3").returns([n(".", true), n("..", true), n("e3e1"), n("e3e2")], nil).times(2) @sftp.expects(:readdir!).with("handle-e5").returns([n(".", true), n("..", true), n("e5e1"), n("e5e2"), n("e5e3")], nil).times(2) @sftp.expects(:close!).with("handle") @sftp.expects(:close!).with("handle-e3") @sftp.expects(:close!).with("handle-e5") assert_equal %w(e3/e3e2 e5/e5e2), @dir.glob("/path/to/remote", "**/e?e2").map { |e| e.name } end private def n(name, directory=false) Net::SFTP::Protocol::V01::Name.new(name.to_s, "longname for #{name}", Net::SFTP::Protocol::V01::Attributes.new(:permissions => directory ? 040755 : 0100644)) end endnet-sftp-2.1.2/test/test_file.rb0000644000004100000410000001163612144456021016574 0ustar www-datawww-datarequire 'common' class FileOperationsTest < Net::SFTP::TestCase def setup @sftp = mock("sftp") @file = Net::SFTP::Operations::File.new(@sftp, "handle") @save_dollar_fslash, $/ = $/, "\n" @save_dollar_bslash, $\ = $\, nil end def teardown $/ = @save_dollar_fslash $\ = @save_dollar_bslash end def test_pos_assignment_should_set_position @file.pos = 15 assert_equal 15, @file.pos end def test_pos_assignment_should_reset_eof @sftp.expects(:read!).with("handle", 0, 8192).returns(nil) assert !@file.eof? @file.read assert @file.eof? @file.pos = 0 assert !@file.eof? end def test_close_should_close_handle_and_set_handle_to_nil assert_equal "handle", @file.handle @sftp.expects(:close!).with("handle") @file.close assert_nil @file.handle end def test_eof_should_be_false_if_at_eof_but_data_remains_in_buffer @sftp.expects(:read!).returns("hello world", nil) @file.read(1) assert !@file.eof? end def test_eof_should_be_true_if_at_eof_and_no_data_in_buffer @sftp.expects(:read!).times(2).returns("hello world", nil) @file.read assert @file.eof? end def test_read_without_argument_should_read_and_return_remainder_of_file_and_set_pos @sftp.expects(:read!).times(2).returns("hello world", nil) assert_equal "hello world", @file.read assert_equal 11, @file.pos end def test_read_with_argument_should_read_and_return_n_bytes_and_set_pos @sftp.expects(:read!).returns("hello world") assert_equal "hello", @file.read(5) assert_equal 5, @file.pos end def test_read_after_pos_assignment_should_read_from_specified_position @sftp.expects(:read!).with("handle", 5, 8192).returns("hello world") @file.pos = 5 assert_equal "hello", @file.read(5) assert_equal 10, @file.pos end def test_gets_without_argument_should_read_until_first_dollar_fslash @sftp.expects(:read!).returns("hello world\ngoodbye world\n\nfarewell!\n") assert_equal "\n", $/ assert_equal "hello world\n", @file.gets assert_equal 12, @file.pos end def test_gets_with_empty_argument_should_read_until_double_dollar_fslash @sftp.expects(:read!).returns("hello world\ngoodbye world\n\nfarewell!\n") assert_equal "\n", $/ assert_equal "hello world\ngoodbye world\n\n", @file.gets("") assert_equal 27, @file.pos end def test_gets_with_argument_should_read_until_first_instance_of_argument @sftp.expects(:read!).returns("hello world\ngoodbye world\n\nfarewell!\n") assert_equal "hello w", @file.gets("w") assert_equal 7, @file.pos end def test_gets_when_no_such_delimiter_exists_in_stream_should_read_to_EOF @sftp.expects(:read!).times(2).returns("hello world\ngoodbye world\n\nfarewell!\n", nil) assert_equal "hello world\ngoodbye world\n\nfarewell!\n", @file.gets("X") assert @file.eof? end def test_gets_at_EOF_should_return_nil @sftp.expects(:read!).returns(nil) assert_nil @file.gets assert @file.eof? end def test_readline_should_raise_exception_on_EOF @sftp.expects(:read!).returns(nil) assert_raises(EOFError) { @file.readline } end def test_write_should_write_data_and_increment_pos_and_return_data_length @sftp.expects(:write!).with("handle", 0, "hello world") assert_equal 11, @file.write("hello world") assert_equal 11, @file.pos end def test_write_after_pos_assignment_should_write_at_position @sftp.expects(:write!).with("handle", 15, "hello world") @file.pos = 15 assert_equal 11, @file.write("hello world") assert_equal 26, @file.pos end def test_print_with_no_arguments_should_write_nothing_if_dollar_bslash_is_nil assert_nil $\ @sftp.expects(:write!).never @file.print end def test_print_with_no_arguments_should_write_dollar_bslash_if_dollar_bslash_is_not_nil $\ = "-" @sftp.expects(:write!).with("handle", 0, "-") @file.print end def test_print_with_arguments_should_write_all_arguments @sftp.expects(:write!).with("handle", 0, "hello") @sftp.expects(:write!).with("handle", 5, " ") @sftp.expects(:write!).with("handle", 6, "world") @file.print("hello", " ", "world") end def test_puts_should_recursively_puts_array_arguments 10.times do |i| @sftp.expects(:write!).with("handle", i*2, i.to_s) @sftp.expects(:write!).with("handle", i*2+1, "\n") end @file.puts 0, [1, [2, 3], 4, [5, [6, 7, 8]]], 9 end def test_puts_should_not_append_newline_if_argument_ends_in_newline @sftp.expects(:write!).with("handle", 0, "a") @sftp.expects(:write!).with("handle", 1, "\n") @sftp.expects(:write!).with("handle", 2, "b\n") @sftp.expects(:write!).with("handle", 4, "c") @sftp.expects(:write!).with("handle", 5, "\n") @file.puts "a", "b\n", "c" end def test_stat_should_return_attributes_object_for_handle stat = stub("stat") @sftp.expects(:fstat!).with("handle").returns(stat) assert_equal stat, @file.stat end endnet-sftp-2.1.2/test/test_packet.rb0000644000004100000410000000043112144456021017113 0ustar www-datawww-datarequire 'common' class PacketTest < Net::SFTP::TestCase def test_packet_should_auto_read_type_byte packet = Net::SFTP::Packet.new("\001rest-of-packet-here") assert_equal 1, packet.type assert_equal "rest-of-packet-here", packet.content[packet.position..-1] end endnet-sftp-2.1.2/test/test_file_factory.rb0000644000004100000410000000302112144456021020310 0ustar www-datawww-datarequire "common" class FileFactoryTest < Net::SFTP::TestCase def setup @sftp = stub(:sftp) @factory = Net::SFTP::Operations::FileFactory.new(@sftp) end def test_open_with_block_should_yield_and_close_handle @sftp.expects(:open!).with("/path/to/remote", "r", :permissions => nil).returns("handle") @sftp.expects(:close!).with("handle") called = false @factory.open("/path/to/remote") do |f| called = true assert_instance_of Net::SFTP::Operations::File, f end assert called end def test_open_with_block_should_close_file_even_if_exception_is_raised @sftp.expects(:open!).with("/path/to/remote", "r", :permissions => nil).returns("handle") @sftp.expects(:close!).with("handle") assert_raises(RuntimeError) do @factory.open("/path/to/remote") { |f| raise RuntimeError, "b00m" } end end def test_open_without_block_should_return_new_file @sftp.expects(:open!).with("/path/to/remote", "r", :permissions => nil).returns("handle") @sftp.expects(:close!).never f = @factory.open("/path/to/remote") assert_instance_of Net::SFTP::Operations::File, f end def test_directory_should_be_true_for_directory @sftp.expects(:lstat!).with("/path/to/dir").returns(mock('attrs', :directory? => true)) assert @factory.directory?("/path/to/dir") end def test_directory_should_be_false_for_non_directory @sftp.expects(:lstat!).with("/path/to/file").returns(mock('attrs', :directory? => false)) assert !@factory.directory?("/path/to/file") end endnet-sftp-2.1.2/test/test_protocol.rb0000644000004100000410000000103112144456021017502 0ustar www-datawww-datarequire 'common' class ProtocolTest < Net::SFTP::TestCase 1.upto(6) 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_7_should_be_unsupported assert_raises(NotImplementedError) do Net::SFTP::Protocol.load(stub('session'), 7) end end endnet-sftp-2.1.2/test/protocol/0000755000004100000410000000000012144456021016123 5ustar www-datawww-datanet-sftp-2.1.2/test/protocol/02/0000755000004100000410000000000012144456021016344 5ustar www-datawww-datanet-sftp-2.1.2/test/protocol/02/test_base.rb0000644000004100000410000000134312144456021020643 0ustar www-datawww-datarequire 'common' require 'protocol/01/test_base' class Protocol::V02::TestBase < Protocol::V01::TestBase def test_version assert_equal 2, @base.version end undef test_rename_should_raise_not_implemented_error def test_rename_should_send_rename_packet @session.expects(:send_packet).with(FXP_RENAME, :long, 0, :string, "/old/file", :string, "/new/file") assert_equal 0, @base.rename("/old/file", "/new/file") end def test_rename_should_ignore_flags_parameter @session.expects(:send_packet).with(FXP_RENAME, :long, 0, :string, "/old/file", :string, "/new/file") assert_equal 0, @base.rename("/old/file", "/new/file", 1234) end private def driver Net::SFTP::Protocol::V02::Base end end net-sftp-2.1.2/test/protocol/06/0000755000004100000410000000000012144456021016350 5ustar www-datawww-datanet-sftp-2.1.2/test/protocol/06/test_attributes.rb0000644000004100000410000001112712144456021022124 0ustar www-datawww-datarequire 'common' module Etc; end class Protocol::V06::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 2345678901, attributes.allocation_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 4567890123, attributes.ctime assert_equal 45678, attributes.ctime_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 0x23452345, attributes.attrib_bits_valid assert_equal 0x3, attributes.text_hint assert_equal "text/html", attributes.mime_type assert_equal 144, attributes.link_count assert_equal "an untranslated name", attributes.untranslated_name assert_equal "second", attributes.extended["first"] end def test_from_buffer_should_correctly_parse_buffer_with_attribute_subset_and_return_attribute_object buffer = Net::SSH::Buffer.from(:long, 0x4, :byte, 1, :long, 0755) attributes = attributes_factory.from_buffer(buffer) assert_equal 1, attributes.type assert_equal 0755, attributes.permissions assert_nil attributes.size assert_nil attributes.allocation_size assert_nil attributes.owner assert_nil attributes.group assert_nil attributes.atime assert_nil attributes.atime_nseconds assert_nil attributes.createtime assert_nil attributes.createtime_nseconds assert_nil attributes.mtime assert_nil attributes.mtime_nseconds assert_nil attributes.ctime assert_nil attributes.ctime_nseconds assert_nil attributes.acl assert_nil attributes.attrib_bits assert_nil attributes.attrib_bits_valid assert_nil attributes.text_hint assert_nil attributes.mime_type assert_nil attributes.link_count assert_nil attributes.untranslated_name assert_nil attributes.extended end def test_attributes_to_s_should_build_binary_representation attributes = attributes_factory.new( :type => 9, :size => 1234567890, :allocation_size => 2345678901, :owner => "jamis", :group => "users", :permissions => 0755, :atime => 1234567890, :atime_nseconds => 12345, :createtime => 2345678901, :createtime_nseconds => 23456, :mtime => 3456789012, :mtime_nseconds => 34567, :ctime => 4567890123, :ctime_nseconds => 45678, :acl => [attributes_factory::ACL.new(1,2,3,"foo"), attributes_factory::ACL.new(4,5,6,"bar")], :attrib_bits => 0x12341234, :attrib_bits_valid => 0x23452345, :text_hint => 0x3, :mime_type => "text/html", :link_count => 144, :untranslated_name => "an untranslated name", :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, 0x8000fffd, :byte, 9, :int64, 1234567890, :int64, 2345678901, :string, "jamis", :string, "users", :long, 0755, :int64, 1234567890, :long, 12345, :int64, 2345678901, :long, 23456, :int64, 3456789012, :long, 34567, :int64, 4567890123, :long, 45678, :string, raw(:long, 2, :long, 1, :long, 2, :long, 3, :string, "foo", :long, 4, :long, 5, :long, 6, :string, "bar"), :long, 0x12341234, :long, 0x23452345, :byte, 0x3, :string, "text/html", :long, 144, :string, "an untranslated name", :long, 1, :string, "first", :string, "second") end def attributes_factory Net::SFTP::Protocol::V06::Attributes end endnet-sftp-2.1.2/test/protocol/06/test_base.rb0000644000004100000410000000337212144456021020653 0ustar www-datawww-datarequire 'common' require 'protocol/05/test_base' class Protocol::V06::TestBase < Protocol::V05::TestBase include Net::SFTP::Constants::OpenFlags include Net::SFTP::Constants def test_version assert_equal 6, @base.version end def test_parse_attrs_packet_should_use_correct_attributes_class Net::SFTP::Protocol::V06::Attributes.expects(:from_buffer).with(:packet).returns(:result) assert_equal({ :attrs => :result }, @base.parse_attrs_packet(:packet)) end undef test_link_should_raise_not_implemented_error undef test_block_should_raise_not_implemented_error undef test_unblock_should_raise_not_implemented_error undef test_symlink_should_send_symlink_packet def test_link_should_send_link_packet @session.expects(:send_packet).with(FXP_LINK, :long, 0, :string, "/path/to/link", :string, "/path/to/file", :bool, true) assert_equal 0, @base.link("/path/to/link", "/path/to/file", true) end def test_symlink_should_send_link_packet_as_symlink @session.expects(:send_packet).with(FXP_LINK, :long, 0, :string, "/path/to/link", :string, "/path/to/file", :bool, true) assert_equal 0, @base.symlink("/path/to/link", "/path/to/file") end def test_block_should_send_block_packet @session.expects(:send_packet).with(FXP_BLOCK, :long, 0, :string, "handle", :int64, 1234, :int64, 4567, :long, 0x40) assert_equal 0, @base.block("handle", 1234, 4567, 0x40) end def test_unblock_should_send_unblock_packet @session.expects(:send_packet).with(FXP_UNBLOCK, :long, 0, :string, "handle", :int64, 1234, :int64, 4567) assert_equal 0, @base.unblock("handle", 1234, 4567) end private def driver Net::SFTP::Protocol::V06::Base end def attributes Net::SFTP::Protocol::V06::Attributes end end net-sftp-2.1.2/test/protocol/04/0000755000004100000410000000000012144456021016346 5ustar www-datawww-datanet-sftp-2.1.2/test/protocol/04/test_attributes.rb0000644000004100000410000001237112144456021022124 0ustar www-datawww-datarequire 'common' module Etc; end class Protocol::V04::TestAttributes < Net::SFTP::TestCase def setup @directory = attributes_factory.new(:type => attributes_factory::T_DIRECTORY) @symlink = attributes_factory.new(:type => attributes_factory::T_SYMLINK) @file = attributes_factory.new(:type => attributes_factory::T_REGULAR) end 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 "second", attributes.extended["first"] end def test_from_buffer_should_correctly_parse_buffer_with_attribute_subset_and_return_attribute_object buffer = Net::SSH::Buffer.from(:long, 0x4, :byte, 1, :long, 0755) attributes = attributes_factory.from_buffer(buffer) assert_equal 1, attributes.type assert_equal 0755, attributes.permissions assert_nil attributes.size assert_nil attributes.owner assert_nil attributes.group assert_nil attributes.atime assert_nil attributes.atime_nseconds assert_nil attributes.createtime assert_nil attributes.createtime_nseconds assert_nil attributes.mtime assert_nil attributes.mtime_nseconds assert_nil attributes.acl assert_nil attributes.extended 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")], :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 def test_attributes_to_s_with_uid_and_gid_should_translate_to_owner_and_group attributes = attributes_factory.new(:uid => 100, :gid => 200) attributes.expects(:require).with("etc").times(2) Etc.expects(:getpwuid).with(100).returns(mock('user', :name => "jamis")) Etc.expects(:getgrgid).with(200).returns(mock('group', :name => "sftp")) assert_equal Net::SSH::Buffer.from(:long, 0x80, :byte, 1, :string, "jamis", :string, "sftp").to_s, attributes.to_s end def test_uid_should_translate_from_owner attributes = attributes_factory.new(:owner => "jamis") attributes.expects(:require).with("etc") Etc.expects(:getpwnam).with("jamis").returns(mock('user', :uid => 100)) assert_equal 100, attributes.uid end def test_gid_should_translate_from_group attributes = attributes_factory.new(:group => "sftp") attributes.expects(:require).with("etc") Etc.expects(:getgrnam).with("sftp").returns(mock('group', :gid => 200)) assert_equal 200, attributes.gid end def test_attributes_without_subsecond_times_should_serialize_without_subsecond_times attributes = attributes_factory.new(:atime => 100) assert_equal Net::SSH::Buffer.from(:long, 0x8, :byte, 1, :int64, 100).to_s, attributes.to_s end def test_directory_should_be_true_only_when_type_is_directory assert @directory.directory? assert !@symlink.directory? assert !@file.directory? end def test_symlink_should_be_true_only_when_type_is_symlink assert !@directory.symlink? assert @symlink.symlink? assert !@file.symlink? end def test_file_should_be_true_only_when_type_is_file assert !@directory.file? assert !@symlink.file? assert @file.file? end private def full_buffer Net::SSH::Buffer.from(:long, 0x800001fd, :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, 1, :string, "first", :string, "second") end def attributes_factory Net::SFTP::Protocol::V04::Attributes end endnet-sftp-2.1.2/test/protocol/04/test_base.rb0000644000004100000410000000505312144456021020647 0ustar www-datawww-datarequire 'common' require 'protocol/03/test_base' class Protocol::V04::TestBase < Protocol::V03::TestBase def test_version assert_equal 4, @base.version end def test_parse_attrs_packet_should_use_correct_attributes_class Net::SFTP::Protocol::V04::Attributes.expects(:from_buffer).with(:packet).returns(:result) assert_equal({ :attrs => :result }, @base.parse_attrs_packet(:packet)) end def test_parse_name_packet_should_use_correct_name_class packet = Net::SSH::Buffer.from(:long, 2, :string, "name1", :long, 0x4, :byte, 1, :long, 0755, :string, "name2", :long, 0x4, :byte, 1, :long, 0550) names = @base.parse_name_packet(packet)[:names] assert_not_nil names assert_equal 2, names.length assert_instance_of Net::SFTP::Protocol::V04::Name, names.first assert_equal "name1", names.first.name assert_equal 0755, names.first.attributes.permissions assert_equal "name2", names.last.name assert_equal 0550, names.last.attributes.permissions end undef test_fstat_should_ignore_flags_parameter undef test_lstat_should_ignore_flags_parameter undef test_stat_should_ignore_flags_parameter def test_lstat_should_send_lstat_packet @session.expects(:send_packet).with(FXP_LSTAT, :long, 0, :string, "/path/to/file", :long, 0x800001fd) assert_equal 0, @base.lstat("/path/to/file") end def test_lstat_with_custom_flags_should_send_lstat_packet_with_given_flags @session.expects(:send_packet).with(FXP_LSTAT, :long, 0, :string, "/path/to/file", :long, 1234) assert_equal 0, @base.lstat("/path/to/file", 1234) end def test_fstat_should_send_fstat_packet @session.expects(:send_packet).with(FXP_FSTAT, :long, 0, :string, "handle", :long, 0x800001fd) assert_equal 0, @base.fstat("handle") end def test_fstat_with_custom_flags_should_send_fstat_packet_with_given_flags @session.expects(:send_packet).with(FXP_FSTAT, :long, 0, :string, "handle", :long, 1234) assert_equal 0, @base.fstat("handle", 1234) end def test_stat_should_send_stat_packet @session.expects(:send_packet).with(FXP_STAT, :long, 0, :string, "/path/to/file", :long, 0x800001fd) assert_equal 0, @base.stat("/path/to/file") end def test_stat_with_custom_flags_should_send_stat_packet_with_given_flags @session.expects(:send_packet).with(FXP_STAT, :long, 0, :string, "/path/to/file", :long, 1234) assert_equal 0, @base.stat("/path/to/file", 1234) end private def driver Net::SFTP::Protocol::V04::Base end def attributes Net::SFTP::Protocol::V04::Attributes end end net-sftp-2.1.2/test/protocol/04/test_name.rb0000644000004100000410000000324212144456021020653 0ustar www-datawww-datarequire 'common' class Protocol::V04::TestName < Net::SFTP::TestCase def setup @save_tz = ENV['TZ'] ENV['TZ'] = 'UTC' @directory = Net::SFTP::Protocol::V04::Name.new("test", Net::SFTP::Protocol::V04::Attributes.new(:type => 2, :mtime => 1205293237, :owner => "jamis", :group => "users", :size => 1024, :permissions => 0755)) @link = Net::SFTP::Protocol::V04::Name.new("test", Net::SFTP::Protocol::V04::Attributes.new(:type => 3, :mtime => 1205293237, :owner => "jamis", :group => "users", :size => 32, :permissions => 0755)) @file = Net::SFTP::Protocol::V04::Name.new("test", Net::SFTP::Protocol::V04::Attributes.new(:type => 1, :mtime => 1205293237, :owner => "jamis", :group => "users", :size => 10240, :permissions => 0755)) end def teardown if @save_tz ENV['TZ'] = @save_tz else ENV.delete('TZ') end end def test_directory? assert @directory.directory? assert !@link.directory? assert !@file.directory? end def test_symlink? assert !@directory.symlink? assert @link.symlink? assert !@file.symlink? end def test_file? assert !@directory.file? assert !@link.file? assert @file.file? end def test_longname_for_directory_should_format_as_directory assert_equal "drwxr-xr-x jamis users 1024 Mar 12 03:40 test", @directory.longname end def test_longname_for_symlink_should_format_as_symlink assert_equal "lrwxr-xr-x jamis users 32 Mar 12 03:40 test", @link.longname end def test_longname_for_file_should_format_as_file assert_equal "-rwxr-xr-x jamis users 10240 Mar 12 03:40 test", @file.longname end end net-sftp-2.1.2/test/protocol/01/0000755000004100000410000000000012144456021016343 5ustar www-datawww-datanet-sftp-2.1.2/test/protocol/01/test_attributes.rb0000644000004100000410000000713112144456021022117 0ustar www-datawww-datarequire 'common' module Etc; end class Protocol::V01::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 1234567890, attributes.size assert_equal 100, attributes.uid assert_equal 200, attributes.gid assert_equal 0755, attributes.permissions assert_equal 1234567890, attributes.atime assert_equal 2345678901, attributes.mtime assert_equal "second", attributes.extended["first"] end def test_from_buffer_should_correctly_parse_buffer_with_attribute_subset_and_return_attribute_object buffer = Net::SSH::Buffer.from(:long, 0x4, :long, 0755) attributes = attributes_factory.from_buffer(buffer) assert_equal 0755, attributes.permissions assert_nil attributes.size assert_nil attributes.uid assert_nil attributes.gid assert_nil attributes.atime assert_nil attributes.mtime assert_nil attributes.extended end def test_attributes_to_s_should_build_binary_representation attributes = attributes_factory.new( :size => 1234567890, :uid => 100, :gid => 200, :permissions => 0755, :atime => 1234567890, :mtime => 2345678901, :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, :long, 0755).to_s, attributes.to_s end def test_attributes_to_s_with_owner_and_group_should_translate_to_uid_and_gid attributes = attributes_factory.new(:owner => "jamis", :group => "sftp") attributes.expects(:require).with("etc").times(2) Etc.expects(:getpwnam).with("jamis").returns(mock('user', :uid => 100)) Etc.expects(:getgrnam).with("sftp").returns(mock('group', :gid => 200)) assert_equal Net::SSH::Buffer.from(:long, 0x2, :long, 100, :long, 200).to_s, attributes.to_s end def test_owner_should_translate_from_uid attributes = attributes_factory.new(:uid => 100) attributes.expects(:require).with("etc") Etc.expects(:getpwuid).with(100).returns(mock('user', :name => "jamis")) assert_equal "jamis", attributes.owner end def test_group_should_translate_from_gid attributes = attributes_factory.new(:gid => 200) attributes.expects(:require).with("etc") Etc.expects(:getgrgid).with(200).returns(mock('group', :name => "sftp")) assert_equal "sftp", attributes.group end def test_type_should_infer_type_from_permissions assert_equal af::T_SOCKET, af.new(:permissions => 0140755).type assert_equal af::T_SYMLINK, af.new(:permissions => 0120755).type assert_equal af::T_REGULAR, af.new(:permissions => 0100755).type assert_equal af::T_BLOCK_DEVICE, af.new(:permissions => 060755).type assert_equal af::T_DIRECTORY, af.new(:permissions => 040755).type assert_equal af::T_CHAR_DEVICE, af.new(:permissions => 020755).type assert_equal af::T_FIFO, af.new(:permissions => 010755).type assert_equal af::T_UNKNOWN, af.new(:permissions => 0755).type assert_equal af::T_UNKNOWN, af.new.type end private def full_buffer Net::SSH::Buffer.from(:long, 0x8000000f, :int64, 1234567890, :long, 100, :long, 200, :long, 0755, :long, 1234567890, :long, 2345678901, :long, 1, :string, "first", :string, "second") end def attributes_factory Net::SFTP::Protocol::V01::Attributes end alias af attributes_factory endnet-sftp-2.1.2/test/protocol/01/test_base.rb0000644000004100000410000001773412144456021020655 0ustar www-datawww-datarequire 'common' # NOTE: these tests assume that the interface to Net::SFTP::Session#send_packet # will remain constant. If that interface ever changes, these tests will need # to be updated! class Protocol::V01::TestBase < Net::SFTP::TestCase include Net::SFTP::Constants include Net::SFTP::Constants::PacketTypes include Net::SFTP::Constants::OpenFlags def setup @session = stub('session', :logger => nil) @base = driver.new(@session) end def test_version assert_equal 1, @base.version end def test_parse_handle_packet_should_read_string_from_packet_and_return_handle_in_hash packet = Net::SSH::Buffer.from(:string, "here is a string") assert_equal({ :handle => "here is a string" }, @base.parse_handle_packet(packet)) end def test_parse_status_packet_should_read_long_from_packet_and_return_code_in_hash packet = Net::SSH::Buffer.from(:long, 15) assert_equal({ :code => 15 }, @base.parse_status_packet(packet)) end def test_parse_data_packet_should_read_string_from_packet_and_return_data_in_hash packet = Net::SSH::Buffer.from(:string, "here is a string") assert_equal({ :data => "here is a string" }, @base.parse_data_packet(packet)) end def test_parse_attrs_packet_should_use_correct_attributes_class Net::SFTP::Protocol::V01::Attributes.expects(:from_buffer).with(:packet).returns(:result) assert_equal({ :attrs => :result }, @base.parse_attrs_packet(:packet)) end def test_parse_name_packet_should_use_correct_name_class packet = Net::SSH::Buffer.from(:long, 2, :string, "name1", :string, "long1", :long, 0x4, :long, 0755, :string, "name2", :string, "long2", :long, 0x4, :long, 0550) names = @base.parse_name_packet(packet)[:names] assert_not_nil names assert_equal 2, names.length assert_instance_of Net::SFTP::Protocol::V01::Name, names.first assert_equal "name1", names.first.name assert_equal "long1", names.first.longname assert_equal 0755, names.first.attributes.permissions assert_equal "name2", names.last.name assert_equal "long2", names.last.longname assert_equal 0550, names.last.attributes.permissions end def test_open_with_numeric_flag_should_accept_IO_constants @session.expects(:send_packet).with(FXP_OPEN, :long, 0, :string, "/path/to/file", :long, FV1::READ | FV1::WRITE | FV1::CREAT | FV1::EXCL, :raw, attributes.new.to_s) assert_equal 0, @base.open("/path/to/file", IO::RDWR | IO::CREAT | IO::EXCL, {}) end { "r" => FV1::READ, "rb" => FV1::READ, "r+" => FV1::READ | FV1::WRITE, "w" => FV1::WRITE | FV1::TRUNC | FV1::CREAT, "w+" => FV1::WRITE | FV1::READ | FV1::TRUNC | FV1::CREAT, "a" => FV1::APPEND | FV1::WRITE | FV1::CREAT, "a+" => FV1::APPEND | FV1::WRITE | FV1::READ | FV1::CREAT }.each do |flags, options| safe_name = flags.sub(/\+/, "_plus") define_method("test_open_with_#{safe_name}_should_translate_correctly") do @session.expects(:send_packet).with(FXP_OPEN, :long, 0, :string, "/path/to/file", :long, options, :raw, attributes.new.to_s) assert_equal 0, @base.open("/path/to/file", flags, {}) end end def test_open_with_attributes_converts_hash_to_attribute_packet @session.expects(:send_packet).with(FXP_OPEN, :long, 0, :string, "/path/to/file", :long, FV1::READ, :raw, attributes.new(:permissions => 0755).to_s) @base.open("/path/to/file", "r", :permissions => 0755) end def test_close_should_send_close_packet @session.expects(:send_packet).with(FXP_CLOSE, :long, 0, :string, "handle") assert_equal 0, @base.close("handle") end def test_read_should_send_read_packet @session.expects(:send_packet).with(FXP_READ, :long, 0, :string, "handle", :int64, 1234, :long, 5678) assert_equal 0, @base.read("handle", 1234, 5678) end def test_write_should_send_write_packet @session.expects(:send_packet).with(FXP_WRITE, :long, 0, :string, "handle", :int64, 1234, :string, "data") assert_equal 0, @base.write("handle", 1234, "data") end def test_lstat_should_send_lstat_packet @session.expects(:send_packet).with(FXP_LSTAT, :long, 0, :string, "/path/to/file") assert_equal 0, @base.lstat("/path/to/file") end def test_lstat_should_ignore_flags_parameter @session.expects(:send_packet).with(FXP_LSTAT, :long, 0, :string, "/path/to/file") assert_equal 0, @base.lstat("/path/to/file", 12345) end def test_fstat_should_send_fstat_packet @session.expects(:send_packet).with(FXP_FSTAT, :long, 0, :string, "handle") assert_equal 0, @base.fstat("handle") end def test_fstat_should_ignore_flags_parameter @session.expects(:send_packet).with(FXP_FSTAT, :long, 0, :string, "handle") assert_equal 0, @base.fstat("handle", 12345) end def test_setstat_should_translate_hash_to_attributes_and_send_setstat_packet @session.expects(:send_packet).with(FXP_SETSTAT, :long, 0, :string, "/path/to/file", :raw, attributes.new(:atime => 1, :mtime => 2, :permissions => 0755).to_s) assert_equal 0, @base.setstat("/path/to/file", :atime => 1, :mtime => 2, :permissions => 0755) end def test_fsetstat_should_translate_hash_to_attributes_and_send_fsetstat_packet @session.expects(:send_packet).with(FXP_FSETSTAT, :long, 0, :string, "handle", :raw, attributes.new(:atime => 1, :mtime => 2, :permissions => 0755).to_s) assert_equal 0, @base.fsetstat("handle", :atime => 1, :mtime => 2, :permissions => 0755) end def test_opendir_should_send_opendir_packet @session.expects(:send_packet).with(FXP_OPENDIR, :long, 0, :string, "/path/to/dir") assert_equal 0, @base.opendir("/path/to/dir") end def test_readdir_should_send_readdir_packet @session.expects(:send_packet).with(FXP_READDIR, :long, 0, :string, "handle") assert_equal 0, @base.readdir("handle") end def test_remove_should_send_remove_packet @session.expects(:send_packet).with(FXP_REMOVE, :long, 0, :string, "/path/to/file") assert_equal 0, @base.remove("/path/to/file") end def test_mkdir_should_translate_hash_to_attributes_and_send_mkdir_packet @session.expects(:send_packet).with(FXP_MKDIR, :long, 0, :string, "/path/to/dir", :raw, attributes.new(:atime => 1, :mtime => 2, :permissions => 0755).to_s) assert_equal 0, @base.mkdir("/path/to/dir", :atime => 1, :mtime => 2, :permissions => 0755) end def test_rmdir_should_send_rmdir_packet @session.expects(:send_packet).with(FXP_RMDIR, :long, 0, :string, "/path/to/dir") assert_equal 0, @base.rmdir("/path/to/dir") end def test_realpath_should_send_realpath_packet @session.expects(:send_packet).with(FXP_REALPATH, :long, 0, :string, "/path/to/file") assert_equal 0, @base.realpath("/path/to/file") end def test_stat_should_send_stat_packet @session.expects(:send_packet).with(FXP_STAT, :long, 0, :string, "/path/to/file") assert_equal 0, @base.stat("/path/to/file") end def test_stat_should_ignore_flags_parameter @session.expects(:send_packet).with(FXP_STAT, :long, 0, :string, "/path/to/file") assert_equal 0, @base.stat("/path/to/file", 12345) end def test_rename_should_raise_not_implemented_error assert_raises(NotImplementedError) { @base.rename("/path/to/old", "/path/to/new") } end def test_readlink_should_raise_not_implemented_error assert_raises(NotImplementedError) { @base.readlink("/path/to/link") } end def test_symlink_should_raise_not_implemented_error assert_raises(NotImplementedError) { @base.symlink("/path/to/link", "/path/to/file") } end def test_link_should_raise_not_implemented_error assert_raises(NotImplementedError) { @base.link("/path/to/link", "/path/to/file", true) } end def test_block_should_raise_not_implemented_error assert_raises(NotImplementedError) { @base.block("handle", 100, 200, 0) } end def test_unblock_should_raise_not_implemented_error assert_raises(NotImplementedError) { @base.unblock("handle", 100, 200) } end private def driver Net::SFTP::Protocol::V01::Base end def attributes Net::SFTP::Protocol::V01::Attributes end endnet-sftp-2.1.2/test/protocol/01/test_name.rb0000644000004100000410000000170512144456021020652 0ustar www-datawww-datarequire 'common' class Protocol::V01::TestName < Net::SFTP::TestCase def setup @directory = Net::SFTP::Protocol::V01::Name.new("test", "drwxr-x-r-x 89 test test 3026 Mar 10 17:45 test", Net::SFTP::Protocol::V01::Attributes.new(:permissions => 040755)) @link = Net::SFTP::Protocol::V01::Name.new("test", "lrwxr-x-r-x 89 test test 3026 Mar 10 17:45 test", Net::SFTP::Protocol::V01::Attributes.new(:permissions => 0120755)) @file = Net::SFTP::Protocol::V01::Name.new("test", "-rwxr-x-r-x 89 test test 3026 Mar 10 17:45 test", Net::SFTP::Protocol::V01::Attributes.new(:permissions => 0100755)) end def test_directory? assert @directory.directory? assert !@link.directory? assert !@file.directory? end def test_symlink? assert !@directory.symlink? assert @link.symlink? assert !@file.symlink? end def test_file? assert !@directory.file? assert !@link.file? assert @file.file? end endnet-sftp-2.1.2/test/protocol/test_base.rb0000644000004100000410000000302212144456021020416 0ustar www-datawww-datarequire 'common' class Protocol::TestBase < Net::SFTP::TestCase def setup @base = Net::SFTP::Protocol::Base.new(stub('session', :logger => nil)) end def test_parse_with_status_packet_should_delegate_to_parse_status_packet packet = stub('packet', :type => FXP_STATUS) @base.expects(:parse_status_packet).with(packet).returns(:result) assert_equal :result, @base.parse(packet) end def test_parse_with_handle_packet_should_delegate_to_parse_handle_packet packet = stub('packet', :type => FXP_HANDLE) @base.expects(:parse_handle_packet).with(packet).returns(:result) assert_equal :result, @base.parse(packet) end def test_parse_with_data_packet_should_delegate_to_parse_data_packet packet = stub('packet', :type => FXP_DATA) @base.expects(:parse_data_packet).with(packet).returns(:result) assert_equal :result, @base.parse(packet) end def test_parse_with_name_packet_should_delegate_to_parse_name_packet packet = stub('packet', :type => FXP_NAME) @base.expects(:parse_name_packet).with(packet).returns(:result) assert_equal :result, @base.parse(packet) end def test_parse_with_attrs_packet_should_delegate_to_parse_attrs_packet packet = stub('packet', :type => FXP_ATTRS) @base.expects(:parse_attrs_packet).with(packet).returns(:result) assert_equal :result, @base.parse(packet) end def test_parse_with_unknown_packet_should_raise_exception packet = stub('packet', :type => FXP_WRITE) assert_raises(NotImplementedError) { @base.parse(packet) } end endnet-sftp-2.1.2/test/protocol/05/0000755000004100000410000000000012144456021016347 5ustar www-datawww-datanet-sftp-2.1.2/test/protocol/05/test_base.rb0000644000004100000410000000552612144456021020655 0ustar www-datawww-datarequire 'common' require 'protocol/04/test_base' class Protocol::V05::TestBase < Protocol::V04::TestBase include Net::SFTP::Constants::OpenFlags include Net::SFTP::Constants def test_version assert_equal 5, @base.version end undef test_rename_should_ignore_flags_parameter def test_rename_should_send_rename_packet @session.expects(:send_packet).with(FXP_RENAME, :long, 0, :string, "/old/file", :string, "/new/file", :long, 0) assert_equal 0, @base.rename("/old/file", "/new/file") end def test_rename_with_flags_should_send_rename_packet_with_flags @session.expects(:send_packet).with(FXP_RENAME, :long, 0, :string, "/old/file", :string, "/new/file", :long, RenameFlags::ATOMIC) assert_equal 0, @base.rename("/old/file", "/new/file", RenameFlags::ATOMIC) end def test_open_with_numeric_flag_should_accept_IO_constants @session.expects(:send_packet).with(FXP_OPEN, :long, 0, :string, "/path/to/file", :long, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES | ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES, :long, FV5::CREATE_NEW, :raw, attributes.new.to_s) assert_equal 0, @base.open("/path/to/file", IO::RDWR | IO::CREAT | IO::EXCL, {}) end { "r" => [FV5::OPEN_EXISTING, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES], "rb" => [FV5::OPEN_EXISTING, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES], "r+" => [FV5::OPEN_EXISTING, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES | ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES], "w" => [FV5::CREATE_TRUNCATE, ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES], "w+" => [FV5::CREATE_TRUNCATE, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES | ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES], "a" => [FV5::OPEN_OR_CREATE | FV5::APPEND_DATA, ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES | ACE::Mask::APPEND_DATA], "a+" => [FV5::OPEN_OR_CREATE | FV5::APPEND_DATA, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES | ACE::Mask::WRITE_DATA | ACE::Mask::WRITE_ATTRIBUTES | ACE::Mask::APPEND_DATA] }.each do |mode_string, (flags, access)| define_method("test_open_with_#{mode_string.sub(/\+/, '_plus')}_should_translate_correctly") do @session.expects(:send_packet).with(FXP_OPEN, :long, 0, :string, "/path/to/file", :long, access, :long, flags, :raw, attributes.new.to_s) assert_equal 0, @base.open("/path/to/file", mode_string, {}) end end def test_open_with_attributes_converts_hash_to_attribute_packet @session.expects(:send_packet).with(FXP_OPEN, :long, 0, :string, "/path/to/file", :long, ACE::Mask::READ_DATA | ACE::Mask::READ_ATTRIBUTES, :long, FV5::OPEN_EXISTING, :raw, attributes.new(:permissions => 0755).to_s) @base.open("/path/to/file", "r", :permissions => 0755) end private def driver Net::SFTP::Protocol::V05::Base end end net-sftp-2.1.2/test/protocol/03/0000755000004100000410000000000012144456021016345 5ustar www-datawww-datanet-sftp-2.1.2/test/protocol/03/test_base.rb0000644000004100000410000000142412144456021020644 0ustar www-datawww-datarequire 'common' require 'protocol/02/test_base' class Protocol::V03::TestBase < Protocol::V02::TestBase def test_version assert_equal 3, @base.version end undef test_readlink_should_raise_not_implemented_error undef test_symlink_should_raise_not_implemented_error def test_readlink_should_send_readlink_packet @session.expects(:send_packet).with(FXP_READLINK, :long, 0, :string, "/path/to/link") assert_equal 0, @base.readlink("/path/to/link") end def test_symlink_should_send_symlink_packet @session.expects(:send_packet).with(FXP_SYMLINK, :long, 0, :string, "/path/to/link", :string, "/path/to/file") assert_equal 0, @base.symlink("/path/to/link", "/path/to/file") end private def driver Net::SFTP::Protocol::V03::Base end end net-sftp-2.1.2/README.rdoc0000644000004100000410000001006012144456021015106 0ustar www-datawww-data= Net::SFTP * Docs: http://net-ssh.github.com/net-sftp * Issues: https://github.com/net-ssh/net-sftp/issues * Codes: https://github.com/net-ssh/net-sftp * Email: net-ssh@solutious.com As of v2.1.0, all gem releases are signed. See INSTALL. == DESCRIPTION: Net::SFTP is a pure-Ruby implementation of the SFTP protocol (specifically, versions 1 through 6 of the SFTP protocol). Note that this is the "Secure File Transfer Protocol", typically run over an SSH connection, and has nothing to do with the FTP protocol. == FEATURES/PROBLEMS: * Transfer files or even entire directory trees to or from a remote host via SFTP * Completely supports all six protocol versions * Asynchronous and synchronous operation * Read and write files using an IO-like interface == SYNOPSIS: In a nutshell: require 'net/sftp' Net::SFTP.start('host', 'username', :password => 'password') do |sftp| # upload a file or directory to the remote host sftp.upload!("/path/to/local", "/path/to/remote") # download a file or directory from the remote host sftp.download!("/path/to/remote", "/path/to/local") # grab data off the remote host directly to a buffer data = sftp.download!("/path/to/remote") # open and write to a pseudo-IO for a remote file sftp.file.open("/path/to/remote", "w") do |f| f.puts "Hello, world!\n" end # open and read from a pseudo-IO for a remote file sftp.file.open("/path/to/remote", "r") do |f| puts f.gets end # create a directory sftp.mkdir! "/path/to/directory" # list the entries in a directory sftp.dir.foreach("/path/to/directory") do |entry| puts entry.longname end end For the full documentation, start with Net::SFTP::Session. Also see Net::SFTP::Operations::Upload, Net::SFTP::Operations::Download, Net::SFTP::Operations::FileFactory, Net::SFTP::Operations::File, and Net::SFTP::Operations::Dir. == REQUIREMENTS: * Net::SSH 2 If you wish to run the tests, you'll need: * Echoe (if you want to use the Rakefile) * Mocha == INSTALL: * gem install net-sftp (might need sudo privileges) However, in order to be sure the code you're installing hasn't been tampered with, it's recommended that you verify the signiture[http://docs.rubygems.org/read/chapter/21]. To do this, you need to add my public key as a trusted certificate (you only need to do this once): # Add the public key as a trusted certificate # (You only need to do this once) $ curl -O https://raw.github.com/net-ssh/net-ssh/master/gem-public_cert.pem $ gem cert --add gem-public_cert.pem Then, when install the gem, do so with high security: $ gem install net-sftp -P HighSecurity If you don't add the public key, you'll see an error like "Couldn't verify data signature". If you're still having trouble let me know and I'll give you a hand. Or, if you prefer to do it the hard way (sans Rubygems): * tar xzf net-ssh-*.tgz * cd net-ssh-* * ruby setup.rb config * ruby setup.rb install (might need sudo privileges) == LICENSE: (The MIT License) Copyright (c) 2008 Jamis Buck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. net-sftp-2.1.2/Rakefile0000644000004100000410000000305412144456021014752 0ustar www-datawww-datarequire "rubygems" require "rake" require "rake/clean" require "rdoc/task" task :default => ["build"] CLEAN.include [ 'pkg', 'rdoc' ] name = "net-sftp" $:.unshift File.join(File.dirname(__FILE__), 'lib') require "net/sftp/version" version = Net::SFTP::Version::STRING.dup begin require "jeweler" Jeweler::Tasks.new do |s| s.version = version s.name = name s.rubyforge_project = s.name s.summary = "A pure Ruby implementation of the SFTP client protocol" s.description = s.summary s.email = "net-ssh@solutious.com" s.homepage = "https://github.com/net-ssh/net-sftp" s.authors = ["Jamis Buck", "Delano Mandelbaum"] s.add_dependency 'net-ssh', ">=2.6.5" s.add_development_dependency 'test-unit' s.add_development_dependency 'mocha' s.license = "MIT" s.signing_key = File.join('/mnt/gem/', 'gem-private_key.pem') s.cert_chain = ['gem-public_cert.pem'] end Jeweler::GemcutterTasks.new rescue LoadError puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" end require 'rake/testtask' Rake::TestTask.new do |t| t.libs = ["lib", "test"] end extra_files = %w[LICENSE.txt THANKS.txt CHANGES.txt ] RDoc::Task.new do |rdoc| rdoc.rdoc_dir = "rdoc" rdoc.title = "#{name} #{version}" rdoc.generator = 'hanna' # gem install hanna-nouveau rdoc.main = 'README.rdoc' rdoc.rdoc_files.include("README*") rdoc.rdoc_files.include("bin/*.rb") rdoc.rdoc_files.include("lib/**/*.rb") extra_files.each { |file| rdoc.rdoc_files.include(file) if File.exists?(file) } end net-sftp-2.1.2/LICENSE.txt0000644000004100000410000000204512144456021015127 0ustar www-datawww-dataCopyright © 2008 Jamis Buck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. net-sftp-2.1.2/gem-public_cert.pem0000644000004100000410000000223012144456021017044 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMQ8wDQYDVQQDDAZkZWxh bm8xGTAXBgoJkiaJk/IsZAEZFglzb2x1dGlvdXMxEzARBgoJkiaJk/IsZAEZFgNj b20wHhcNMTMwMjA2MTE1NzQ1WhcNMTQwMjA2MTE1NzQ1WjBBMQ8wDQYDVQQDDAZk ZWxhbm8xGTAXBgoJkiaJk/IsZAEZFglzb2x1dGlvdXMxEzARBgoJkiaJk/IsZAEZ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDg1hMtl0XsMuUK AKTgYWv3gjj7vuEsE2EjT+vyBg8/LpqVVwZziiaebJT9IZiQ+sCFqbiakj0b53pI hg1yOaBEmH6/W0L7rwzqaRV9sW1eJs9JxFYQCnd67zUnzj8nnRlOjG+hhIG+Vsij npsGbt28pefuNZJjO5q2clAlfSniIIHfIsU7/StEYu6FUGOjnwryZ0r5yJlr9RrE Gs+q0DW8QnZ9UpAfuDFQZuIqeKQFFLE7nMmCGaA+0BN1nLl3fVHNbLHq7Avk8+Z+ ZuuvkdscbHlO/l+3xCNQ5nUnHwq0ADAbMLOlmiYYzqXoWLjmeI6me/clktJCfN2R oZG3UQvvAgMBAAGjOTA3MAkGA1UdEwQCMAAwHQYDVR0OBBYEFMSJOEtHzE4l0azv M0JK0kKNToK1MAsGA1UdDwQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEAtOdE73qx OH2ydi9oT2hS5f9G0y1Z70Tlwh+VGExyfxzVE9XwC+iPpJxNraiHYgF/9/oky7ZZ R9q0/tJneuhAenZdiQkX7oi4O3v9wRS6YHoWBxMPFKVRLNTzvVJsbmfpCAlp5/5g ps4wQFy5mibElGVlOobf/ghqZ25HS9J6kd0/C/ry0AUtTogsL7TxGwT4kbCx63ub 3vywEEhsJUzfd97GCABmtQfRTldX/j7F1z/5wd8p+hfdox1iibds9ZtfaZA3KzKn kchWN9B6zg9r1XMQ8BM2Jz0XoPanPe354+lWwjpkRKbFow/ZbQHcCLCq24+N6b6g dgKfNDzwiDpqCA== -----END CERTIFICATE----- net-sftp-2.1.2/Manifest0000644000004100000410000000267012144456021015001 0ustar www-datawww-dataCHANGELOG.rdoc lib/net/sftp/constants.rb lib/net/sftp/errors.rb lib/net/sftp/operations/dir.rb lib/net/sftp/operations/download.rb lib/net/sftp/operations/file.rb lib/net/sftp/operations/file_factory.rb lib/net/sftp/operations/upload.rb lib/net/sftp/packet.rb lib/net/sftp/protocol/01/attributes.rb lib/net/sftp/protocol/01/base.rb lib/net/sftp/protocol/01/name.rb lib/net/sftp/protocol/02/base.rb lib/net/sftp/protocol/03/base.rb lib/net/sftp/protocol/04/attributes.rb lib/net/sftp/protocol/04/base.rb lib/net/sftp/protocol/04/name.rb 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/base.rb lib/net/sftp/protocol.rb lib/net/sftp/request.rb lib/net/sftp/response.rb lib/net/sftp/session.rb lib/net/sftp/version.rb lib/net/sftp.rb Rakefile README.rdoc setup.rb test/common.rb test/protocol/01/test_attributes.rb test/protocol/01/test_base.rb test/protocol/01/test_name.rb test/protocol/02/test_base.rb test/protocol/03/test_base.rb test/protocol/04/test_attributes.rb test/protocol/04/test_base.rb test/protocol/04/test_name.rb test/protocol/05/test_base.rb test/protocol/06/test_attributes.rb test/protocol/06/test_base.rb test/protocol/test_base.rb test/test_all.rb test/test_dir.rb test/test_download.rb test/test_file.rb test/test_file_factory.rb test/test_packet.rb test/test_protocol.rb test/test_request.rb test/test_response.rb test/test_session.rb test/test_upload.rb Manifest net-sftp-2.1.2/metadata.yml0000644000004100000410000001263012144456021015610 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: net-sftp version: !ruby/object:Gem::Version version: 2.1.2 prerelease: platform: ruby authors: - Jamis Buck - Delano Mandelbaum autorequire: bindir: bin cert_chain: - !binary |- LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUROakNDQWg2Z0F3SUJB Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREJCTVE4d0RRWURWUVFEREFaa1pX eGgKYm04eEdUQVhCZ29Ka2lhSmsvSXNaQUVaRmdsemIyeDFkR2x2ZFhNeEV6 QVJCZ29Ka2lhSmsvSXNaQUVaRmdOagpiMjB3SGhjTk1UTXdNakEyTVRFMU56 UTFXaGNOTVRRd01qQTJNVEUxTnpRMVdqQkJNUTh3RFFZRFZRUUREQVprClpX eGhibTh4R1RBWEJnb0praWFKay9Jc1pBRVpGZ2x6YjJ4MWRHbHZkWE14RXpB UkJnb0praWFKay9Jc1pBRVoKRmdOamIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFF QkFRVUFBNElCRHdBd2dnRUtBb0lCQVFEZzFoTXRsMFhzTXVVSwpBS1RnWVd2 M2dqajd2dUVzRTJFalQrdnlCZzgvTHBxVlZ3WnppaWFlYkpUOUlaaVErc0NG cWJpYWtqMGI1M3BJCmhnMXlPYUJFbUg2L1cwTDdyd3pxYVJWOXNXMWVKczlK eEZZUUNuZDY3elVuemo4bm5SbE9qRytoaElHK1ZzaWoKbnBzR2J0MjhwZWZ1 TlpKak81cTJjbEFsZlNuaUlJSGZJc1U3L1N0RVl1NkZVR09qbndyeVowcjV5 SmxyOVJyRQpHcytxMERXOFFuWjlVcEFmdURGUVp1SXFlS1FGRkxFN25NbUNH YUErMEJOMW5MbDNmVkhOYkxIcTdBdms4K1orClp1dXZrZHNjYkhsTy9sKzN4 Q05RNW5Vbkh3cTBBREFiTUxPbG1pWVl6cVhvV0xqbWVJNm1lL2Nsa3RKQ2ZO MlIKb1pHM1VRdnZBZ01CQUFHak9UQTNNQWtHQTFVZEV3UUNNQUF3SFFZRFZS ME9CQllFRk1TSk9FdEh6RTRsMGF6dgpNMEpLMGtLTlRvSzFNQXNHQTFVZER3 UUVBd0lFc0RBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQXRPZEU3M3F4Ck9I MnlkaTlvVDJoUzVmOUcweTFaNzBUbHdoK1ZHRXh5Znh6VkU5WHdDK2lQcEp4 TnJhaUhZZ0YvOS9va3k3WloKUjlxMC90Sm5ldWhBZW5aZGlRa1g3b2k0TzN2 OXdSUzZZSG9XQnhNUEZLVlJMTlR6dlZKc2JtZnBDQWxwNS81ZwpwczR3UUZ5 NW1pYkVsR1ZsT29iZi9naHFaMjVIUzlKNmtkMC9DL3J5MEFVdFRvZ3NMN1R4 R3dUNGtiQ3g2M3ViCjN2eXdFRWhzSlV6ZmQ5N0dDQUJtdFFmUlRsZFgvajdG MXovNXdkOHAraGZkb3gxaWliZHM5WnRmYVpBM0t6S24Ka2NoV045QjZ6Zzly MVhNUThCTTJKejBYb1BhblBlMzU0K2xXd2pwa1JLYkZvdy9aYlFIY0NMQ3Ey NCtONmI2ZwpkZ0tmTkR6d2lEcHFDQT09Ci0tLS0tRU5EIENFUlRJRklDQVRF LS0tLS0K date: 2013-05-07 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: net-ssh requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 2.6.5 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 2.6.5 - !ruby/object:Gem::Dependency name: test-unit requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: mocha requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' description: A pure Ruby implementation of the SFTP client protocol email: net-ssh@solutious.com executables: [] extensions: [] extra_rdoc_files: - LICENSE.txt - README.rdoc files: - CHANGES.txt - LICENSE.txt - Manifest - README.rdoc - Rakefile - gem-public_cert.pem - lib/net/sftp.rb - lib/net/sftp/constants.rb - lib/net/sftp/errors.rb - lib/net/sftp/operations/dir.rb - lib/net/sftp/operations/download.rb - lib/net/sftp/operations/file.rb - lib/net/sftp/operations/file_factory.rb - lib/net/sftp/operations/upload.rb - lib/net/sftp/packet.rb - lib/net/sftp/protocol.rb - lib/net/sftp/protocol/01/attributes.rb - lib/net/sftp/protocol/01/base.rb - lib/net/sftp/protocol/01/name.rb - lib/net/sftp/protocol/02/base.rb - lib/net/sftp/protocol/03/base.rb - lib/net/sftp/protocol/04/attributes.rb - lib/net/sftp/protocol/04/base.rb - lib/net/sftp/protocol/04/name.rb - 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/base.rb - lib/net/sftp/request.rb - lib/net/sftp/response.rb - lib/net/sftp/session.rb - lib/net/sftp/version.rb - net-sftp.gemspec - setup.rb - test/common.rb - test/protocol/01/test_attributes.rb - test/protocol/01/test_base.rb - test/protocol/01/test_name.rb - test/protocol/02/test_base.rb - test/protocol/03/test_base.rb - test/protocol/04/test_attributes.rb - test/protocol/04/test_base.rb - test/protocol/04/test_name.rb - test/protocol/05/test_base.rb - test/protocol/06/test_attributes.rb - test/protocol/06/test_base.rb - test/protocol/test_base.rb - test/test_all.rb - test/test_dir.rb - test/test_download.rb - test/test_file.rb - test/test_file_factory.rb - test/test_packet.rb - test/test_protocol.rb - test/test_request.rb - test/test_response.rb - test/test_session.rb - test/test_upload.rb homepage: https://github.com/net-ssh/net-sftp licenses: - MIT post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: net-sftp rubygems_version: 1.8.25 signing_key: specification_version: 3 summary: A pure Ruby implementation of the SFTP client protocol test_files: [] net-sftp-2.1.2/metadata.gz.sig0000644000004100000410000000040012144456021016200 0ustar www-datawww-dataÐA¨fK}U.šã;ÿ£; ~*3?—Þ0÷Dú´‡ŸPp›˜= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) subprefix = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix') } if c['rubylibdir'] # V < 1.6.3 stdruby = subprefix.call(c['rubylibdir']) siteruby = subprefix.call(c['sitedir']) versite = subprefix.call(c['sitelibdir']) sodir = subprefix.call(c['sitearchdir']) elsif newpath_p # 1.4.4 <= V <= 1.6.3 stdruby = "$prefix/lib/ruby/#{version}" siteruby = subprefix.call(c['sitedir']) versite = siteruby + '/' + version sodir = "$site-ruby/#{c['arch']}" else # V < 1.4.4 stdruby = "$prefix/lib/ruby/#{version}" siteruby = "$prefix/lib/ruby/#{version}/site_ruby" versite = siteruby sodir = "$site-ruby/#{c['arch']}" end if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end common_descripters = [ [ 'prefix', [ c['prefix'], 'path', 'path prefix of target environment' ] ], [ 'std-ruby', [ stdruby, 'path', 'the directory for standard ruby libraries' ] ], [ 'site-ruby-common', [ siteruby, 'path', 'the directory for version-independent non-standard ruby libraries' ] ], [ 'site-ruby', [ versite, 'path', 'the directory for non-standard ruby libraries' ] ], [ 'bin-dir', [ '$prefix/bin', 'path', 'the directory for commands' ] ], [ 'rb-dir', [ '$site-ruby', 'path', 'the directory for ruby scripts' ] ], [ 'so-dir', [ sodir, 'path', 'the directory for ruby extentions' ] ], [ 'data-dir', [ '$prefix/share', 'path', 'the directory for shared data' ] ], [ 'ruby-path', [ rubypath, 'path', 'path to set to #! line' ] ], [ 'ruby-prog', [ rubypath, 'name', 'the ruby program using for installation' ] ], [ 'make-prog', [ makeprog, 'name', 'the make program to compile ruby extentions' ] ], [ 'without-ext', [ 'no', 'yes/no', 'does not compile/install ruby extentions' ] ] ] multipackage_descripters = [ [ 'with', [ '', 'name,name...', 'package names that you want to install', 'ALL' ] ], [ 'without', [ '', 'name,name...', 'package names that you do not want to install', 'NONE' ] ] ] if multipackage_install? DESCRIPTER = common_descripters + multipackage_descripters else DESCRIPTER = common_descripters end SAVE_FILE = 'config.save' def ConfigTable.each_name(&block) keys().each(&block) end def ConfigTable.keys DESCRIPTER.map {|name, *dummy| name } end def ConfigTable.each_definition(&block) DESCRIPTER.each(&block) end def ConfigTable.get_entry(name) name, ent = DESCRIPTER.assoc(name) ent end def ConfigTable.get_entry!(name) get_entry(name) or raise ArgumentError, "no such config: #{name}" end def ConfigTable.add_entry(name, vals) ConfigTable::DESCRIPTER.push [name,vals] end def ConfigTable.remove_entry(name) get_entry(name) or raise ArgumentError, "no such config: #{name}" DESCRIPTER.delete_if {|n, arr| n == name } end def ConfigTable.config_key?(name) get_entry(name) ? true : false end def ConfigTable.bool_config?(name) ent = get_entry(name) or return false ent[1] == 'yes/no' end def ConfigTable.value_config?(name) ent = get_entry(name) or return false ent[1] != 'yes/no' end def ConfigTable.path_config?(name) ent = get_entry(name) or return false ent[1] == 'path' end class << self alias newobj new end def ConfigTable.new c = newobj() c.initialize_from_table c end def ConfigTable.load c = newobj() c.initialize_from_file c end def initialize_from_table @table = {} DESCRIPTER.each do |k, (default, vname, desc, default2)| @table[k] = default end end def initialize_from_file raise InstallError, "#{File.basename $0} config first"\ unless File.file?(SAVE_FILE) @table = {} File.foreach(SAVE_FILE) do |line| k, v = line.split(/=/, 2) @table[k] = v.strip end end def save File.open(SAVE_FILE, 'w') {|f| @table.each do |k, v| f.printf "%s=%s\n", k, v if v end } end def []=(k, v) raise InstallError, "unknown config option #{k}"\ unless ConfigTable.config_key?(k) @table[k] = v end def [](key) return nil unless @table[key] @table[key].gsub(%r<\$([^/]+)>) { self[$1] } end def set_raw(key, val) @table[key] = val end def get_raw(key) @table[key] end end module MetaConfigAPI def eval_file_ifexist(fname) instance_eval File.read(fname), fname, 1 if File.file?(fname) end def config_names ConfigTable.keys end def config?(name) ConfigTable.config_key?(name) end def bool_config?(name) ConfigTable.bool_config?(name) end def value_config?(name) ConfigTable.value_config?(name) end def path_config?(name) ConfigTable.path_config?(name) end def add_config(name, argname, default, desc) ConfigTable.add_entry name,[default,argname,desc] end def add_path_config(name, default, desc) add_config name, 'path', default, desc end def add_bool_config(name, default, desc) add_config name, 'yes/no', default ? 'yes' : 'no', desc end def set_config_default(name, default) if bool_config?(name) ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no') else ConfigTable.get_entry!(name)[0] = default end end def remove_config(name) ent = ConfigTable.get_entry(name) ConfigTable.remove_entry name ent end end # # File Operations # module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + dirname if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # does not check '/'... it's too abnormal case dirs = dirname.split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(fname) $stderr.puts "rm -f #{fname}" if verbose? return if no_harm? if File.exist?(fname) or File.symlink?(fname) File.chmod 0777, fname File.unlink fname end end def rm_rf(dn) $stderr.puts "rm -rf #{dn}" if verbose? return if no_harm? Dir.chdir dn Dir.foreach('.') do |fn| next if fn == '.' next if fn == '..' if File.dir?(fn) verbose_off { rm_rf fn } else verbose_off { rm_f fn } end end Dir.chdir '..' Dir.rmdir dn end def move_file(src, dest) File.unlink dest if File.exist?(dest) begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix + dest if prefix realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(str) $stderr.puts str if verbose? system str or raise RuntimeError, "'system #{str}' failed" end def ruby(str) command config('ruby-prog') + ' ' + str end def make(task = '') command config('make-prog') + ' ' + task end def extdir?(dir) File.exist?(dir + '/MANIFEST') end def all_files_in(dirname) Dir.open(dirname) {|d| return d.select {|ent| File.file?("#{dirname}/#{ent}") } } end REJECT_DIRS = %w( CVS SCCS RCS CVS.adm ) def all_dirs_in(dirname) Dir.open(dirname) {|d| return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS } end end # # Main Installer # class InstallError < StandardError; end module HookUtils def run_hook(name) try_run_hook "#{curr_srcdir()}/#{name}" or try_run_hook "#{curr_srcdir()}/#{name}.rb" end def try_run_hook(fname) return false unless File.file?(fname) begin instance_eval File.read(fname), fname, 1 rescue raise InstallError, "hook #{fname} failed:\n" + $!.message end true end end module HookScriptAPI def get_config(key) @config[key] end alias config get_config def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # #abstract srcdir_root #abstract objdir_root #abstract relpath def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file? srcfile(path) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.2.4' Copyright = 'Copyright (c) 2000-2004 Minero Aoki' TASKS = [ [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke instance().invoke end @singleton = nil def ToplevelInstaller.instance @singleton ||= new(File.dirname($0)) @singleton end include MetaConfigAPI def initialize(ardir_root) @config = nil @options = { 'verbose' => true } @ardir = File.expand_path(ardir_root) end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs task = parsearg_global() @config = load_config(task) __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end def run_metaconfigs eval_file_ifexist "#{@ardir}/metaconfig" end def load_config(task) case task when 'config' ConfigTable.new when 'clean', 'distclean' if File.exist?('config.save') then ConfigTable.load else ConfigTable.new end else ConfigTable.load end end def init_installers @installer = Installer.new(@config, @options, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/ while arg = ARGV.shift case arg when /\A\w+\z/ raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg return arg when '-q', '--quiet' @options['verbose'] = false when '--verbose' @options['verbose'] = true when '-h', '--help' print_usage $stdout exit 0 when '-v', '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else raise InstallError, "unknown global option '#{arg}'" end end raise InstallError, <" out.puts " ruby #{File.basename $0} [] []" fmt = " %-20s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, '-h,--help', 'print this message' out.printf fmt, '-v,--version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf " %-10s %s\n", name, desc end out.puts out.puts 'Options for config:' ConfigTable.each_definition do |name, (default, arg, desc, default2)| out.printf " %-20s %s [%s]\n", '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg), desc, default2 || default end out.printf " %-20s %s [%s]\n", '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's" out.puts out.puts 'Options for install:' out.printf " %-20s %s [%s]\n", '--no-harm', 'only display what to do if given', 'off' out.printf " %-20s %s [%s]\n", '--prefix', 'install path prefix', '$prefix' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_show ConfigTable.each_name do |k| v = @config.get_raw(k) if not v or v.empty? v = '(not specified)' end printf "%-10s %s\n", k, v end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end class ToplevelInstallerMulti < ToplevelInstaller include HookUtils include HookScriptAPI include FileOperations def initialize(ardir) super @packages = all_dirs_in("#{@ardir}/packages") raise 'no package exists' if @packages.empty? end def run_metaconfigs eval_file_ifexist "#{@ardir}/metaconfig" @packages.each do |name| eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig" end end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, @options, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| raise InstallError, "no such package: #{name}" \ unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # multi-package metaconfig API # attr_reader :packages def declare_packages(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_clean rm_f 'config.save' run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f 'config.save' run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose'] Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def verbose? @options['verbose'] end def no_harm? @options['no-harm'] end end class Installer FILETYPES = %w( bin lib ext data ) include HookScriptAPI include HookUtils include FileOperations def initialize(config, opt, srcroot, objroot) @config = config @options = opt @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end # # Hook Script API bases # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # configs/options # def no_harm? @options['no-harm'] end def verbose? @options['verbose'] end def verbose_off begin save, @options['verbose'] = @options['verbose'], false yield ensure @options['verbose'] = save end end # # TASK config # def exec_config exec_task_traverse 'config' end def config_dir_bin(rel) end def config_dir_lib(rel) end def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end def extconf opt = @options['config-opt'].join(' ') command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}" end def config_dir_data(rel) end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) all_files_in(curr_srcdir()).each do |fname| adjust_shebang "#{curr_srcdir()}/#{fname}" end end # modify: #!/usr/bin/ruby # modify: #! /usr/bin/ruby # modify: #!ruby # not modify: #!/usr/bin/env ruby SHEBANG_RE = /\A\#!\s*\S*ruby\S*/ def adjust_shebang(path) return if no_harm? tmpfile = File.basename(path) + '.tmp' begin File.open(path, 'rb') {|r| File.open(tmpfile, 'wb') {|w| first = r.gets return unless SHEBANG_RE =~ first $stderr.puts "adjusting shebang: #{File.basename path}" if verbose? w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path')) w.write r.read } } move_file tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end def setup_dir_lib(rel) end def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end def setup_dir_data(rel) end # # TASK install # def exec_install exec_task_traverse 'install' end def install_dir_bin(rel) install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755 end def install_dir_lib(rel) install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files ruby_extentions('.'), "#{config('so-dir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644 end def install_files(list, dest, mode) mkdir_p dest, @options['install-prefix'] list.each do |fname| install fname, dest, mode, @options['install-prefix'] end end def ruby_scripts collect_filenames_auto().select {|n| /\.rb\z/ =~ n || "module.yml" == n } end # picked up many entries from cvs-1.11.1/src/ignore.c reject_patterns = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) mapping = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } REJECT_PATTERNS = Regexp.new('\A(?:' + reject_patterns.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] } }.join('|') + ')\z') def collect_filenames_auto mapdir((existfiles() - hookfiles()).reject {|fname| REJECT_PATTERNS =~ fname }) end def existfiles all_files_in(curr_srcdir()) | all_files_in('.') end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean ).map {|t| sprintf(fmt, t) } }.flatten end def mapdir(filelist) filelist.map {|fname| if File.exist?(fname) # objdir fname else # srcdir File.join(curr_srcdir(), fname) end } end def ruby_extentions(dir) _ruby_extentions(dir) or raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first" end DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/ def _ruby_extentions(dir) Dir.open(dir) {|d| return d.select {|fname| DLEXT =~ fname } } end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f 'config.save' rm_f 'InstalledFiles' end def clean_dir_bin(rel) end def clean_dir_lib(rel) end def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end def clean_dir_data(rel) end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f 'config.save' rm_f 'InstalledFiles' end def distclean_dir_bin(rel) end def distclean_dir_lib(rel) end def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end # # lib # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if config('without-ext') == 'yes' and type == 'ext' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') all_dirs_in(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end end if $0 == __FILE__ begin if multipackage_install? ToplevelInstallerMulti.invoke else ToplevelInstaller.invoke end rescue raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end net-sftp-2.1.2/data.tar.gz.sig0000644000004100000410000000040012144456021016116 0ustar www-datawww-data¹©#5 p?(MY+tÇvèùš²Û{d¸ÔêÌæ;\±•Gþ4öéL]šcé„#ó_F0ž~ÚyD­±LËÙ»WgvÇLï|póv7œêÌš!«ÊYœçd,Ó«8ƒ¢jY¼ëI;¥Ø½~¢Ôç2jË EËW}Â`Ó· £ª:(¥Ùï‡$´‘ÿÔ¯„ðÿ´)±¡E¯µ¸·õ /ɵ>%&¿y¡'}CÍ­þ[WEÂ’öJYáú_Ð1a¿. ~Ÿ2©dó ãh Æ<º¶Èýl¡ÂLI¥Ê+ä>† = µÜbn ; &S³@Œ"%ê÷îuÌù=net-sftp-2.1.2/lib/0000755000004100000410000000000012144456021014051 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/0000755000004100000410000000000012144456021014637 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/sftp/0000755000004100000410000000000012144456021015613 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/sftp/session.rb0000644000004100000410000011462312144456021017632 0ustar www-datawww-datarequire 'net/ssh' require 'net/sftp/constants' require 'net/sftp/errors' require 'net/sftp/protocol' require 'net/sftp/request' require 'net/sftp/operations/dir' require 'net/sftp/operations/upload' require 'net/sftp/operations/download' require 'net/sftp/operations/file_factory' module Net; module SFTP # The Session class encapsulates a single SFTP channel on a Net::SSH # connection. Instances of this class are what most applications will # interact with most, as it provides access to both low-level (mkdir, # rename, remove, symlink, etc.) and high-level (upload, download, etc.) # SFTP operations. # # Although Session makes it easy to do SFTP operations serially, you can # also set up multiple operations to be done in parallel, too, without # needing to resort to threading. You merely need to fire off the requests, # and then run the event loop until all of the requests have completed: # # handle1 = sftp.open!("/path/to/file1") # handle2 = sftp.open!("/path/to/file2") # # r1 = sftp.read(handle1, 0, 1024) # r2 = sftp.read(handle2, 0, 1024) # sftp.loop { [r1, r2].any? { |r| r.pending? } } # # puts "chunk #1: #{r1.response[:data]}" # puts "chunk #2: #{r2.response[:data]}" # # By passing blocks to the operations, you can set up powerful state # machines, to fire off subsequent operations. In fact, the Net::SFTP::Operations::Upload # and Net::SFTP::Operations::Download classes set up such state machines, so that # multiple uploads and/or downloads can be running simultaneously. # # The convention with the names of the operations is as follows: if the method # name ends with an exclamation mark, like #read!, it will be synchronous # (e.g., it will block until the server responds). Methods without an # exclamation mark (e.g. #read) are asynchronous, and return before the # server has responded. You will need to make sure the SSH event loop is # run in order to process these requests. (See #loop.) class Session include Net::SSH::Loggable include Net::SFTP::Constants::PacketTypes # The highest protocol version supported by the Net::SFTP library. HIGHEST_PROTOCOL_VERSION_SUPPORTED = 6 # A reference to the Net::SSH session object that powers this SFTP session. attr_reader :session # The Net::SSH::Connection::Channel object that the SFTP session is being # processed by. attr_reader :channel # The state of the SFTP connection. It will be :opening, :subsystem, :init, # :open, or :closed. attr_reader :state # The protocol instance being used by this SFTP session. Useful for # querying the protocol version in effect. attr_reader :protocol # The hash of pending requests. Any requests that have been sent and which # the server has not yet responded to will be represented here. attr_reader :pending_requests # Creates a new Net::SFTP instance atop the given Net::SSH connection. # This will return immediately, before the SFTP connection has been properly # initialized. Once the connection is ready, the given block will be called. # If you want to block until the connection has been initialized, try this: # # sftp = Net::SFTP::Session.new(ssh) # sftp.loop { sftp.opening? } def initialize(session, &block) @session = session @input = Net::SSH::Buffer.new self.logger = session.logger @state = :closed connect(&block) end public # high-level SFTP operations # Initiates an upload from +local+ to +remote+, asynchronously. This # method will return a new Net::SFTP::Operations::Upload instance, and requires # the event loop to be run in order for the upload to progress. See # Net::SFTP::Operations::Upload for a full discussion of how this method can be # used. # # uploader = sftp.upload("/local/path", "/remote/path") # uploader.wait def upload(local, remote = File.basename(local), options={}, &block) Operations::Upload.new(self, local, remote, options, &block) end # Identical to #upload, but blocks until the upload is complete. def upload!(local, remote = File.basename(local), options={}, &block) upload(local, remote, options, &block).wait end # Initiates a download from +remote+ to +local+, asynchronously. This # method will return a new Net::SFTP::Operations::Download instance, and requires # that the event loop be run in order for the download to progress. See # Net::SFTP::Operations::Download for a full discussion of hos this method can be # used. # # download = sftp.download("/remote/path", "/local/path") # download.wait def download(remote, local, options={}, &block) Operations::Download.new(self, local, remote, options, &block) end # Identical to #download, but blocks until the download is complete. # If +local+ is omitted, downloads the file to an in-memory buffer # and returns the result as a string; otherwise, returns the # Net::SFTP::Operations::Download instance. def download!(remote, local=nil, options={}, &block) require 'stringio' unless defined?(StringIO) destination = local || StringIO.new result = download(remote, destination, options, &block).wait local ? result : destination.string end # Returns an Net::SFTP::Operations::FileFactory instance, which can be used to # mimic synchronous, IO-like file operations on a remote file via # SFTP. # # sftp.file.open("/path/to/file") do |file| # while line = file.gets # puts line # end # end # # See Net::SFTP::Operations::FileFactory and Net::SFTP::Operations::File for more details. def file @file ||= Operations::FileFactory.new(self) end # Returns a Net::SFTP::Operations::Dir instance, which can be used to # conveniently iterate over and search directories on the remote server. # # sftp.dir.glob("/base/path", "*/**/*.rb") do |entry| # p entry.name # end # # See Net::SFTP::Operations::Dir for a more detailed discussion of how # to use this. def dir @dir ||= Operations::Dir.new(self) end public # low-level SFTP operations # :call-seq: # open(path, flags="r", options={}) -> request # open(path, flags="r", options={}) { |response| ... } -> request # # Opens a file on the remote server. The +flags+ parameter determines # how the flag is open, and accepts the same format as IO#open (e.g., # either a string like "r" or "w", or a combination of the IO constants). # The +options+ parameter is a hash of attributes to be associated # with the file, and varies greatly depending on the SFTP protocol # version in use, but some (like :permissions) are always available. # # Returns immediately with a Request object. If a block is given, it will # be invoked when the server responds, with a Response object as the only # parameter. The :handle property of the response is the handle of the # opened file, and may be passed to other methods (like #close, #read, # #write, and so forth). # # sftp.open("/path/to/file") do |response| # raise "fail!" unless response.ok? # sftp.close(response[:handle]) # end # sftp.loop def open(path, flags="r", options={}, &callback) request :open, path, flags, options, &callback end # Identical to #open, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the handle of the newly opened file. # # handle = sftp.open!("/path/to/file") def open!(path, flags="r", options={}, &callback) wait_for(open(path, flags, options, &callback), :handle) end # :call-seq: # close(handle) -> request # close(handle) { |response| ... } -> request # # Closes an open handle, whether obtained via #open, or #opendir. Returns # immediately with a Request object. If a block is given, it will be # invoked when the server responds. # # sftp.open("/path/to/file") do |response| # raise "fail!" unless response.ok? # sftp.close(response[:handle]) # end # sftp.loop def close(handle, &callback) request :close, handle, &callback end # Identical to #close, but blocks until the server responds. It will # raise a StatusException if the request was unsuccessful. Otherwise, # it returns the Response object for this request. # # sftp.close!(handle) def close!(handle, &callback) wait_for(close(handle, &callback)) end # :call-seq: # read(handle, offset, length) -> request # read(handle, offset, length) { |response| ... } -> request # # Requests that +length+ bytes, starting at +offset+ bytes from the # beginning of the file, be read from the file identified by # +handle+. (The +handle+ should be a value obtained via the #open # method.) Returns immediately with a Request object. If a block is # given, it will be invoked when the server responds. # # The :data property of the response will contain the requested data, # assuming the call was successful. # # request = sftp.read(handle, 0, 1024) do |response| # if response.eof? # puts "end of file reached before reading any data" # elsif !response.ok? # puts "error (#{response})" # else # print(response[:data]) # end # end # request.wait # # To read an entire file will usually require multiple calls to #read, # unless you know in advance how large the file is. def read(handle, offset, length, &callback) request :read, handle, offset, length, &callback end # Identical to #read, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. If the end of the file # was reached, +nil+ will be returned. Otherwise, it returns the data that # was read, as a String. # # data = sftp.read!(handle, 0, 1024) def read!(handle, offset, length, &callback) wait_for(read(handle, offset, length, &callback), :data) end # :call-seq: # write(handle, offset, data) -> request # write(handle, offset, data) { |response| ... } -> request # # Requests that +data+ be written to the file identified by +handle+, # starting at +offset+ bytes from the start of the file. The file must # have been opened for writing via #open. Returns immediately with a # Request object. If a block is given, it will be invoked when the # server responds. # # request = sftp.write(handle, 0, "hello, world!\n") # request.wait def write(handle, offset, data, &callback) request :write, handle, offset, data, &callback end # Identical to #write, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful, or the end of the file # was reached. Otherwise, it returns the Response object for this request. # # sftp.write!(handle, 0, "hello, world!\n") def write!(handle, offset, data, &callback) wait_for(write(handle, offset, data, &callback)) end # :call-seq: # lstat(path, flags=nil) -> request # lstat(path, flags=nil) { |response| ... } -> request # # This method is identical to the #stat method, with the exception that # it will not follow symbolic links (thus allowing you to stat the # link itself, rather than what it refers to). The +flags+ parameter # is not used in SFTP protocol versions prior to 4, and will be ignored # in those versions of the protocol that do not use it. For those that # do, however, you may provide hints as to which file proprties you wish # to query (e.g., if all you want is permissions, you could pass the # Net::SFTP::Protocol::V04::Attributes::F_PERMISSIONS flag as the value # for the +flags+ parameter). # # The method returns immediately with a Request object. If a block is given, # it will be invoked when the server responds. The :attrs property of # the response will contain an Attributes instance appropriate for the # the protocol version (see Protocol::V01::Attributes, Protocol::V04::Attributes, # and Protocol::V06::Attributes). # # request = sftp.lstat("/path/to/file") do |response| # raise "fail!" unless response.ok? # puts "permissions: %04o" % response[:attrs].permissions # end # request.wait def lstat(path, flags=nil, &callback) request :lstat, path, flags, &callback end # Identical to the #lstat method, but blocks until the server responds. # It will raise a StatusException if the request was unsuccessful. # Otherwise, it will return the attribute object describing the path. # # puts sftp.lstat!("/path/to/file").permissions def lstat!(path, flags=nil, &callback) wait_for(lstat(path, flags, &callback), :attrs) end # The fstat method is identical to the #stat and #lstat methods, with # the exception that it takes a +handle+ as the first parameter, such # as would be obtained via the #open or #opendir methods. (See the #lstat # method for full documentation). def fstat(handle, flags=nil, &callback) request :fstat, handle, flags, &callback end # Identical to the #fstat method, but blocks until the server responds. # It will raise a StatusException if the request was unsuccessful. # Otherwise, it will return the attribute object describing the path. # # puts sftp.fstat!(handle).permissions def fstat!(handle, flags=nil, &callback) wait_for(fstat(handle, flags, &callback), :attrs) end # :call-seq: # setstat(path, attrs) -> request # setstat(path, attrs) { |response| ... } -> request # # This method may be used to set file metadata (such as permissions, or # user/group information) on a remote file. The exact metadata that may # be tweaked is dependent on the SFTP protocol version in use, but in # general you may set at least the permissions, user, and group. (See # Protocol::V01::Attributes, Protocol::V04::Attributes, and Protocol::V06::Attributes # for the full lists of attributes that may be set for the different # protocols.) # # The +attrs+ parameter is a hash, where the keys are symbols identifying # the attributes to set. # # The method returns immediately with a Request object. If a block is given, # it will be invoked when the server responds. # # request = sftp.setstat("/path/to/file", :permissions => 0644) # request.wait # puts "success: #{request.response.ok?}" def setstat(path, attrs, &callback) request :setstat, path, attrs, &callback end # Identical to the #setstat method, but blocks until the server responds. # It will raise a StatusException if the request was unsuccessful. # Otherwise, it will return the Response object for the request. # # sftp.setstat!("/path/to/file", :permissions => 0644) def setstat!(path, attrs, &callback) wait_for(setstat(path, attrs, &callback)) end # The fsetstat method is identical to the #setstat method, with the # exception that it takes a +handle+ as the first parameter, such as # would be obtained via the #open or #opendir methods. (See the # #setstat method for full documentation.) def fsetstat(handle, attrs, &callback) request :fsetstat, handle, attrs, &callback end # Identical to the #fsetstat method, but blocks until the server responds. # It will raise a StatusException if the request was unsuccessful. # Otherwise, it will return the Response object for the request. # # sftp.fsetstat!(handle, :permissions => 0644) def fsetstat!(handle, attrs, &callback) wait_for(fsetstat(handle, attrs, &callback)) end # :call-seq: # opendir(path) -> request # opendir(path) { |response| ... } -> request # # Attempts to open a directory on the remote host for reading. Once the # handle is obtained, directory entries may be retrieved using the # #readdir method. The method returns immediately with a Request object. # If a block is given, it will be invoked when the server responds. # # sftp.opendir("/path/to/directory") do |response| # raise "fail!" unless response.ok? # sftp.close(response[:handle]) # end # sftp.loop def opendir(path, &callback) request :opendir, path, &callback end # Identical to #opendir, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return a handle to the given path. # # handle = sftp.opendir!("/path/to/directory") def opendir!(path, &callback) wait_for(opendir(path, &callback), :handle) end # :call-seq: # readdir(handle) -> request # raeddir(handle) { |response| ... } -> request # # Reads a set of entries from the given directory handle (which must # have been obtained via #opendir). If the response is EOF, then there # are no more entries in the directory. Otherwise, the entries will be # in the :names property of the response: # # loop do # request = sftp.readdir(handle).wait # break if request.response.eof? # raise "fail!" unless request.response.ok? # request.response[:names].each do |entry| # puts entry.name # end # end # # See also Protocol::V01::Name and Protocol::V04::Name for the specific # properties of each individual entry (which vary based on the SFTP # protocol version in use). def readdir(handle, &callback) request :readdir, handle, &callback end # Identical to #readdir, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return nil if there were no more names to read, or an array of name # entries. # # while (entries = sftp.readdir!(handle)) do # entries.each { |entry| puts(entry.name) } # end def readdir!(handle, &callback) wait_for(readdir(handle, &callback), :names) end # :call-seq: # remove(filename) -> request # remove(filename) { |response| ... } -> request # # Attempts to remove the given file from the remote file system. Returns # immediately with a Request object. If a block is given, the block will # be invoked when the server responds, and will be passed a Response # object. # # sftp.remove("/path/to/file").wait def remove(filename, &callback) request :remove, filename, &callback end # Identical to #remove, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. # # sftp.remove!("/path/to/file") def remove!(filename, &callback) wait_for(remove(filename, &callback)) end # :call-seq: # mkdir(path, attrs={}) -> request # mkdir(path, attrs={}) { |response| ... } -> request # # Creates the named directory on the remote server. If an attribute hash # is given, it must map to the set of attributes supported by the version # of the SFTP protocol in use. (See Protocol::V01::Attributes, # Protocol::V04::Attributes, and Protocol::V06::Attributes.) # # sftp.mkdir("/path/to/directory", :permissions => 0550).wait def mkdir(path, attrs={}, &callback) request :mkdir, path, attrs, &callback end # Identical to #mkdir, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. # # sftp.mkdir!("/path/to/directory", :permissions => 0550) def mkdir!(path, attrs={}, &callback) wait_for(mkdir(path, attrs, &callback)) end # :call-seq: # rmdir(path) -> request # rmdir(path) { |response| ... } -> request # # Removes the named directory on the remote server. The directory must # be empty before it can be removed. # # sftp.rmdir("/path/to/directory").wait def rmdir(path, &callback) request :rmdir, path, &callback end # Identical to #rmdir, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. # # sftp.rmdir!("/path/to/directory") def rmdir!(path, &callback) wait_for(rmdir(path, &callback)) end # :call-seq: # realpath(path) -> request # realpath(path) { |response| ... } -> request # # Tries to canonicalize the given path, turning any given path into an # absolute path. This is primarily useful for converting a path with # ".." or "." segments into an identical path without those segments. # The answer will be in the response's :names attribute, as a # one-element array. # # request = sftp.realpath("/path/../to/../directory").wait # puts request[:names].first.name def realpath(path, &callback) request :realpath, path, &callback end # Identical to #realpath, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return a name object identifying the path. # # puts(sftp.realpath!("/path/../to/../directory")) def realpath!(path, &callback) wait_for(realpath(path, &callback), :names).first end # Identical to the #lstat method, except that it follows symlinks # (e.g., if you give it the path to a symlink, it will stat the target # of the symlink rather than the symlink itself). See the #lstat method # for full documentation. def stat(path, flags=nil, &callback) request :stat, path, flags, &callback end # Identical to #stat, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return an attribute object for the named path. # # attrs = sftp.stat!("/path/to/file") def stat!(path, flags=nil, &callback) wait_for(stat(path, flags, &callback), :attrs) end # :call-seq: # rename(name, new_name, flags=nil) -> request # rename(name, new_name, flags=nil) { |response| ... } -> request # # Renames the given file. This operation is only available in SFTP # protocol versions two and higher. The +flags+ parameter is ignored # in versions prior to 5. In versions 5 and higher, the +flags+ # parameter can be used to specify how the rename should be performed # (atomically, etc.). # # The following flags are defined in protocol version 5: # # * 0x0001 - overwrite an existing file if the new name specifies a file # that already exists. # * 0x0002 - perform the rewrite atomically. # * 0x0004 - allow the server to perform the rename as it prefers. def rename(name, new_name, flags=nil, &callback) request :rename, name, new_name, flags, &callback end # Identical to #rename, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. # # sftp.rename!("/path/to/old", "/path/to/new") def rename!(name, new_name, flags=nil, &callback) wait_for(rename(name, new_name, flags, &callback)) end # :call-seq: # readlink(path) -> request # readlink(path) { |response| ... } -> request # # Queries the server for the target of the specified symbolic link. # This operation is only available in protocol versions 3 and higher. # The response to this request will include a names property, a one-element # array naming the target of the symlink. # # request = sftp.readlink("/path/to/symlink").wait # puts request.response[:names].first.name def readlink(path, &callback) request :readlink, path, &callback end # Identical to #readlink, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Name object for the path that the symlink targets. # # item = sftp.readlink!("/path/to/symlink") def readlink!(path, &callback) wait_for(readlink(path, &callback), :names).first end # :call-seq: # symlink(path, target) -> request # symlink(path, target) { |response| ... } -> request # # Attempts to create a symlink to +path+ at +target+. This operation # is only available in protocol versions 3, 4, and 5, but the Net::SFTP # library mimics the symlink behavior in protocol version 6 using the # #link method, so it is safe to use this method in protocol version 6. # # sftp.symlink("/path/to/file", "/path/to/symlink").wait def symlink(path, target, &callback) request :symlink, path, target, &callback end # Identical to #symlink, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. # # sftp.symlink!("/path/to/file", "/path/to/symlink") def symlink!(path, target, &callback) wait_for(symlink(path, target, &callback)) end # :call-seq: # link(new_link_path, existing_path, symlink=true) -> request # link(new_link_path, existing_path, symlink=true) { |response| ... } -> request # # Attempts to create a link, either hard or symbolic. This operation is # only available in SFTP protocol versions 6 and higher. If the +symlink+ # paramter is true, a symbolic link will be created, otherwise a hard # link will be created. The link will be named +new_link_path+, and will # point to the path +existing_path+. # # sftp.link("/path/to/symlink", "/path/to/file", true).wait # # Note that #link is only available for SFTP protocol 6 and higher. You # can use #symlink for protocols 3 and higher. def link(new_link_path, existing_path, symlink=true, &callback) request :link, new_link_path, existing_path, symlink, &callback end # Identical to #link, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. # # sftp.link!("/path/to/symlink", "/path/to/file", true) def link!(new_link_path, existing_path, symlink=true, &callback) wait_for(link(new_link_path, existing_path, symlink, &callback)) end # :call-seq: # block(handle, offset, length, mask) -> request # block(handle, offset, length, mask) { |response| ... } -> request # # Creates a byte-range lock on the file specified by the given +handle+. # This operation is only available in SFTP protocol versions 6 and # higher. The lock may be either mandatory or advisory. # # The +handle+ parameter is a file handle, as obtained by the #open method. # # The +offset+ and +length+ parameters describe the location and size of # the byte range. # # The +mask+ describes how the lock should be defined, and consists of # some combination of the following bit masks: # # * 0x0040 - Read lock. The byte range may not be accessed for reading # by via any other handle, though it may be written to. # * 0x0080 - Write lock. The byte range may not be written to via any # other handle, though it may be read from. # * 0x0100 - Delete lock. No other handle may delete this file. # * 0x0200 - Advisory lock. The server need not honor the lock instruction. # # Once created, the lock may be removed via the #unblock method. def block(handle, offset, length, mask, &callback) request :block, handle, offset, length, mask, &callback end # Identical to #block, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. def block!(handle, offset, length, mask, &callback) wait_for(block(handle, offset, length, mask, &callback)) end # :call-seq: # unblock(handle, offset, length) -> request # unblock(handle, offset, length) { |response| ... } -> request # # Removes a previously created byte-range lock. This operation is only # available in protocol versions 6 and higher. The +offset+ and +length+ # parameters must exactly match those that were given to #block when the # lock was acquired. def unblock(handle, offset, length, &callback) request :unblock, handle, offset, length, &callback end # Identical to #unblock, but blocks until the server responds. It will raise # a StatusException if the request was unsuccessful. Otherwise, it will # return the Response object for the request. def unblock!(handle, offset, length, &callback) wait_for(unblock(handle, offset, length, &callback)) end public # miscellaneous methods # Closes the SFTP connection, but not the SSH connection. Blocks until the # session has terminated. Once the session has terminated, further operations # on this object will result in errors. You can reopen the SFTP session # via the #connect method. def close_channel return unless open? channel.close loop { !closed? } end # Returns true if the connection has been initialized. def open? state == :open end # Returns true if the connection has been closed. def closed? state == :closed end # Returns true if the connection is in the process of being initialized # (e.g., it is not closed, but is not yet fully open). def opening? !(open? || closed?) end # Attempts to establish an SFTP connection over the SSH session given when # this object was instantiated. If the object is already open, this will # simply execute the given block (if any), passing the SFTP session itself # as argument. If the session is currently being opened, this will add # the given block to the list of callbacks, to be executed when the session # is fully open. # # This method does not block, and will return immediately. If you pass a # block to it, that block will be invoked when the connection has been # fully established. Thus, you can do something like this: # # sftp.connect do # puts "open!" # end # # If you just want to block until the connection is ready, see the #connect! # method. def connect(&block) case state when :open block.call(self) if block when :closed @state = :opening @channel = session.open_channel(&method(:when_channel_confirmed)) @packet_length = nil @protocol = nil @on_ready = Array(block) else # opening @on_ready << block if block end self end # Same as the #connect method, but blocks until the SFTP connection has # been fully initialized. def connect!(&block) connect(&block) loop { opening? } self end alias :loop_forever :loop # Runs the SSH event loop while the given block returns true. This lets # you set up a state machine and then "fire it off". If you do not specify # a block, the event loop will run for as long as there are any pending # SFTP requests. This makes it easy to do thing like this: # # sftp.remove("/path/to/file") # sftp.loop def loop(&block) block ||= Proc.new { pending_requests.any? } session.loop(&block) end # Formats, constructs, and sends an SFTP packet of the given type and with # the given data. This does not block, but merely enqueues the packet for # sending and returns. # # You should probably use the operation methods, rather than building and # sending the packet directly. (See #open, #close, etc.) def send_packet(type, *args) data = Net::SSH::Buffer.from(*args) msg = Net::SSH::Buffer.from(:long, data.length+1, :byte, type, :raw, data) channel.send_data(msg.to_s) end private #-- # "ruby -w" hates private attributes, so we have to do this longhand #++ # The input buffer used to accumulate packet data def input; @input; end # Create and enqueue a new SFTP request of the given type, with the # given arguments. Returns a new Request instance that encapsulates the # request. def request(type, *args, &callback) request = Request.new(self, type, protocol.send(type, *args), &callback) info { "sending #{type} packet (#{request.id})" } pending_requests[request.id] = request end # Waits for the given request to complete. If the response is # EOF, nil is returned. If the response was not successful # (e.g., !response.ok?), a StatusException will be raised. # If +property+ is given, the corresponding property from the response # will be returned; otherwise, the response object itself will be # returned. def wait_for(request, property=nil) request.wait if request.response.eof? nil elsif !request.response.ok? raise StatusException.new(request.response) elsif property request.response[property.to_sym] else request.response end end # Called when the SSH channel is confirmed as "open" by the server. # This is one of the states of the SFTP state machine, and is followed # by the #when_subsystem_started state. def when_channel_confirmed(channel) debug { "requesting sftp subsystem" } @state = :subsystem channel.subsystem("sftp", &method(:when_subsystem_started)) end # Called when the SSH server confirms that the SFTP subsystem was # successfully started. This sets up the appropriate callbacks on the # SSH channel and then starts the SFTP protocol version negotiation # process. def when_subsystem_started(channel, success) raise Net::SFTP::Exception, "could not start SFTP subsystem" unless success debug { "sftp subsystem successfully started" } @state = :init channel.on_data { |c,data| input.append(data) } channel.on_extended_data { |c,t,data| debug { data } } channel.on_close(&method(:when_channel_closed)) channel.on_process(&method(:when_channel_polled)) send_packet(FXP_INIT, :long, HIGHEST_PROTOCOL_VERSION_SUPPORTED) end # Called when the SSH server closes the underlying channel. def when_channel_closed(channel) debug { "sftp channel closed" } @channel = nil @state = :closed end # Called whenever Net::SSH polls the SFTP channel for pending activity. # This basically checks the input buffer to see if enough input has been # accumulated to handle. If there has, the packet is parsed and # dispatched, according to its type (see #do_version and #dispatch_request). def when_channel_polled(channel) while input.length > 0 if @packet_length.nil? # make sure we've read enough data to tell how long the packet is return unless input.length >= 4 @packet_length = input.read_long end return unless input.length >= @packet_length + 4 packet = Net::SFTP::Packet.new(input.read(@packet_length)) input.consume! @packet_length = nil debug { "received sftp packet #{packet.type} len #{packet.length}" } if packet.type == FXP_VERSION do_version(packet) else dispatch_request(packet) end end end # Called to handle FXP_VERSION packets. This performs the SFTP protocol # version negotiation, instantiating the appropriate Protocol instance # and invoking the callback given to #connect, if any. def do_version(packet) debug { "negotiating sftp protocol version, mine is #{HIGHEST_PROTOCOL_VERSION_SUPPORTED}" } server_version = packet.read_long debug { "server reports sftp version #{server_version}" } negotiated_version = [server_version, HIGHEST_PROTOCOL_VERSION_SUPPORTED].min info { "negotiated version is #{negotiated_version}" } extensions = {} until packet.eof? name = packet.read_string data = packet.read_string extensions[name] = data end @protocol = Protocol.load(self, negotiated_version) @pending_requests = {} @state = :open @on_ready.each { |callback| callback.call(self) } @on_ready = nil end # Parses the packet, finds the associated Request instance, and tells # the Request instance to respond to the packet (see Request#respond_to). def dispatch_request(packet) id = packet.read_long request = pending_requests.delete(id) or raise Net::SFTP::Exception, "no such request `#{id}'" request.respond_to(packet) end end end; endnet-sftp-2.1.2/lib/net/sftp/operations/0000755000004100000410000000000012144456021017776 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/sftp/operations/download.rb0000644000004100000410000003127612144456021022143 0ustar www-datawww-datarequire 'net/ssh/loggable' module Net; module SFTP; module Operations # A general purpose downloader module for Net::SFTP. It can download files # into IO objects, or directly to files on the local file system. It can # even download entire directory trees via SFTP, and provides a flexible # progress reporting mechanism. # # To download a single file from the remote server, simply specify both the # remote and local paths: # # downloader = sftp.download("/path/to/remote.txt", "/path/to/local.txt") # # By default, this operates asynchronously, so if you want to block until # the download finishes, you can use the 'bang' variant: # # sftp.download!("/path/to/remote.txt", "/path/to/local.txt") # # Or, if you have multiple downloads that you want to run in parallel, you can # employ the #wait method of the returned object: # # dls = %w(file1 file2 file3).map { |f| sftp.download("remote/#{f}", f) } # dls.each { |d| d.wait } # # To download an entire directory tree, recursively, simply specify :recursive => true: # # sftp.download!("/path/to/remotedir", "/path/to/local", :recursive => true) # # This will download "/path/to/remotedir", it's contents, it's subdirectories, # and their contents, recursively, to "/path/to/local" on the local host. # (If you specify :recursive => true and the source is not a directory, # you'll get an error!) # # If you want to pull the contents of a file on the remote server, and store # the data in memory rather than immediately to disk, you can pass an IO # object as the destination: # # require 'stringio' # io = StringIO.new # sftp.download!("/path/to/remote", io) # # This will only work for single-file downloads. Trying to do so with # :recursive => true will cause an error. # # The following options are supported: # # * :progress - either a block or an object to act as a progress # callback. See the discussion of "progress monitoring" below. # * :requests - the number of pending SFTP requests to allow at # any given time. When downloading an entire directory tree recursively, # this will default to 16. Setting this higher might improve throughput. # Reducing it will reduce throughput. # * :read_size - the maximum number of bytes to read at a time # from the source. Increasing this value might improve throughput. It # defaults to 32,000 bytes. # # == Progress Monitoring # # Sometimes it is desirable to track the progress of a download. There are # two ways to do this: either using a callback block, or a special custom # object. # # Using a block it's pretty straightforward: # # sftp.download!("remote", "local") do |event, downloader, *args| # case event # when :open then # # args[0] : file metadata # puts "starting download: #{args[0].remote} -> #{args[0].local} (#{args[0].size} bytes}" # when :get then # # args[0] : file metadata # # args[1] : byte offset in remote file # # args[2] : data that was received # puts "writing #{args[2].length} bytes to #{args[0].local} starting at #{args[1]}" # when :close then # # args[0] : file metadata # puts "finished with #{args[0].remote}" # when :mkdir then # # args[0] : local path name # puts "creating directory #{args[0]}" # when :finish then # puts "all done!" # end # # However, for more complex implementations (e.g., GUI interfaces and such) # a block can become cumbersome. In those cases, you can create custom # handler objects that respond to certain methods, and then pass your handler # to the downloader: # # class CustomHandler # def on_open(downloader, file) # puts "starting download: #{file.remote} -> #{file.local} (#{file.size} bytes)" # end # # def on_get(downloader, file, offset, data) # puts "writing #{data.length} bytes to #{file.local} starting at #{offset}" # end # # def on_close(downloader, file) # puts "finished with #{file.remote}" # end # # def on_mkdir(downloader, path) # puts "creating directory #{path}" # end # # def on_finish(downloader) # puts "all done!" # end # end # # sftp.download!("remote", "local", :progress => CustomHandler.new) # # If you omit any of those methods, the progress updates for those missing # events will be ignored. You can create a catchall method named "call" for # those, instead. class Download include Net::SSH::Loggable # The destination of the download (the name of a file or directory on # the local server, or an IO object) attr_reader :local # The source of the download (the name of a file or directory on the # remote server) attr_reader :remote # The hash of options that was given to this Download instance. attr_reader :options # The SFTP session instance that drives this download. attr_reader :sftp # The properties hash for this object attr_reader :properties # Instantiates a new downloader process on top of the given SFTP session. # +local+ is either an IO object that should receive the data, or a string # identifying the target file or directory on the local host. +remote+ is # a string identifying the location on the remote host that the download # should source. # # This will return immediately, and requires that the SSH event loop be # run in order to effect the download. (See #wait.) def initialize(sftp, local, remote, options={}, &progress) @sftp = sftp @local = local @remote = remote @progress = progress || options[:progress] @options = options @active = 0 @properties = options[:properties] || {} self.logger = sftp.logger if recursive? && local.respond_to?(:write) raise ArgumentError, "cannot download a directory tree in-memory" end @stack = [Entry.new(remote, local, recursive?)] process_next_entry end # Returns the value of the :recursive key in the options hash that was # given when the object was instantiated. def recursive? options[:recursive] end # Returns true if there are any active requests or pending files or # directories. def active? @active > 0 || stack.any? end # Forces the transfer to stop. def abort! @active = 0 @stack.clear end # Runs the SSH event loop for as long as the downloader is active (see # #active?). This can be used to block until the download completes. def wait sftp.loop { active? } self end # Returns the property with the given name. This allows Download instances # to store their own state when used as part of a state machine. def [](name) @properties[name.to_sym] end # Sets the given property to the given name. This allows Download instances # to store their own state when used as part of a state machine. def []=(name, value) @properties[name.to_sym] = value end private # A simple struct for encapsulating information about a single remote # file or directory that needs to be downloaded. Entry = Struct.new(:remote, :local, :directory, :size, :handle, :offset, :sink) #-- # "ruby -w" hates private attributes, so we have to do these longhand #++ # The stack of Entry instances, indicating which files and directories # on the remote host remain to be downloaded. def stack; @stack; end # The progress handler for this instance. Possibly nil. def progress; @progress; end # The default read size. DEFAULT_READ_SIZE = 32_000 # The number of bytes to read at a time from remote files. def read_size options[:read_size] || DEFAULT_READ_SIZE end # The number of simultaneou SFTP requests to use to effect the download. # Defaults to 16 for recursive downloads. def requests options[:requests] || (recursive? ? 16 : 2) end # Enqueues as many files and directories from the stack as possible # (see #requests). def process_next_entry while stack.any? && requests > @active entry = stack.shift @active += 1 if entry.directory update_progress(:mkdir, entry.local) ::Dir.mkdir(entry.local) unless ::File.directory?(entry.local) request = sftp.opendir(entry.remote, &method(:on_opendir)) request[:entry] = entry else open_file(entry) end end update_progress(:finish) if !active? end # Called when a remote directory is "opened" for reading, e.g. to # enumerate its contents. Starts an readdir operation if the opendir # operation was successful. def on_opendir(response) entry = response.request[:entry] raise "opendir #{entry.remote}: #{response}" unless response.ok? entry.handle = response[:handle] request = sftp.readdir(response[:handle], &method(:on_readdir)) request[:parent] = entry end # Called when the next batch of items is read from a directory on the # remote server. If any items were read, they are added to the queue # and #process_next_entry is called. def on_readdir(response) entry = response.request[:parent] if response.eof? request = sftp.close(entry.handle, &method(:on_closedir)) request[:parent] = entry elsif !response.ok? raise "readdir #{entry.remote}: #{response}" else response[:names].each do |item| next if item.name == "." || item.name == ".." stack << Entry.new(::File.join(entry.remote, item.name), ::File.join(entry.local, item.name), item.directory?, item.attributes.size) end # take this opportunity to enqueue more requests process_next_entry request = sftp.readdir(entry.handle, &method(:on_readdir)) request[:parent] = entry end end # Called when a file is to be opened for reading from the remote server. def open_file(entry) update_progress(:open, entry) request = sftp.open(entry.remote, &method(:on_open)) request[:entry] = entry end # Called when a directory handle is closed. def on_closedir(response) @active -= 1 entry = response.request[:parent] raise "close #{entry.remote}: #{response}" unless response.ok? process_next_entry end # Called when a file has been opened. This will call #download_next_chunk # to initiate the data transfer. def on_open(response) entry = response.request[:entry] raise "open #{entry.remote}: #{response}" unless response.ok? entry.handle = response[:handle] entry.sink = entry.local.respond_to?(:write) ? entry.local : ::File.open(entry.local, "wb") entry.offset = 0 download_next_chunk(entry) end # Initiates a read of the next #read_size bytes from the file. def download_next_chunk(entry) request = sftp.read(entry.handle, entry.offset, read_size, &method(:on_read)) request[:entry] = entry request[:offset] = entry.offset end # Called when a read from a file finishes. If the read was successful # and returned data, this will call #download_next_chunk to read the # next bit from the file. Otherwise the file will be closed. def on_read(response) entry = response.request[:entry] if response.eof? update_progress(:close, entry) entry.sink.close request = sftp.close(entry.handle, &method(:on_close)) request[:entry] = entry elsif !response.ok? raise "read #{entry.remote}: #{response}" else entry.offset += response[:data].bytesize update_progress(:get, entry, response.request[:offset], response[:data]) entry.sink.write(response[:data]) download_next_chunk(entry) end end # Called when a file handle is closed. def on_close(response) @active -= 1 entry = response.request[:entry] raise "close #{entry.remote}: #{response}" unless response.ok? process_next_entry end # If a progress callback or object has been set, this will report # the progress to that callback or object. def update_progress(hook, *args) on = "on_#{hook}" if progress.respond_to?(on) progress.send(on, self, *args) elsif progress.respond_to?(:call) progress.call(hook, self, *args) end end end end; end; end net-sftp-2.1.2/lib/net/sftp/operations/upload.rb0000644000004100000410000003176512144456021021623 0ustar www-datawww-datarequire 'net/ssh/loggable' module Net; module SFTP; module Operations # A general purpose uploader module for Net::SFTP. It can upload IO objects, # files, and even entire directory trees via SFTP, and provides a flexible # progress reporting mechanism. # # To upload a single file to the remote server, simply specify both the # local and remote paths: # # uploader = sftp.upload("/path/to/local.txt", "/path/to/remote.txt") # # By default, this operates asynchronously, so if you want to block until # the upload finishes, you can use the 'bang' variant: # # sftp.upload!("/path/to/local.txt", "/path/to/remote.txt") # # Or, if you have multiple uploads that you want to run in parallel, you can # employ the #wait method of the returned object: # # uploads = %w(file1 file2 file3).map { |f| sftp.upload(f, "remote/#{f}") } # uploads.each { |u| u.wait } # # To upload an entire directory tree, recursively, simply pass the directory # path as the first parameter: # # sftp.upload!("/path/to/directory", "/path/to/remote") # # This will upload "/path/to/directory", it's contents, it's subdirectories, # and their contents, recursively, to "/path/to/remote" on the remote server. # # For uploading a directory without creating it, do # sftp.upload!("/path/to/directory", "/path/to/remote", :mkdir => false) # # If you want to send data to a file on the remote server, but the data is # in memory, you can pass an IO object and upload it's contents: # # require 'stringio' # io = StringIO.new(data) # sftp.upload!(io, "/path/to/remote") # # The following options are supported: # # * :progress - either a block or an object to act as a progress # callback. See the discussion of "progress monitoring" below. # * :requests - the number of pending SFTP requests to allow at # any given time. When uploading an entire directory tree recursively, # this will default to 16, otherwise it will default to 2. Setting this # higher might improve throughput. Reducing it will reduce throughput. # * :read_size - the maximum number of bytes to read at a time # from the source. Increasing this value might improve throughput. It # defaults to 32,000 bytes. # * :name - the filename to report to the progress monitor when # an IO object is given as +local+. This defaults to "". # # == Progress Monitoring # # Sometimes it is desirable to track the progress of an upload. There are # two ways to do this: either using a callback block, or a special custom # object. # # Using a block it's pretty straightforward: # # sftp.upload!("local", "remote") do |event, uploader, *args| # case event # when :open then # # args[0] : file metadata # puts "starting upload: #{args[0].local} -> #{args[0].remote} (#{args[0].size} bytes}" # when :put then # # args[0] : file metadata # # args[1] : byte offset in remote file # # args[2] : data being written (as string) # puts "writing #{args[2].length} bytes to #{args[0].remote} starting at #{args[1]}" # when :close then # # args[0] : file metadata # puts "finished with #{args[0].remote}" # when :mkdir then # # args[0] : remote path name # puts "creating directory #{args[0]}" # when :finish then # puts "all done!" # end # # However, for more complex implementations (e.g., GUI interfaces and such) # a block can become cumbersome. In those cases, you can create custom # handler objects that respond to certain methods, and then pass your handler # to the uploader: # # class CustomHandler # def on_open(uploader, file) # puts "starting upload: #{file.local} -> #{file.remote} (#{file.size} bytes)" # end # # def on_put(uploader, file, offset, data) # puts "writing #{data.length} bytes to #{file.remote} starting at #{offset}" # end # # def on_close(uploader, file) # puts "finished with #{file.remote}" # end # # def on_mkdir(uploader, path) # puts "creating directory #{path}" # end # # def on_finish(uploader) # puts "all done!" # end # end # # sftp.upload!("local", "remote", :progress => CustomHandler.new) # # If you omit any of those methods, the progress updates for those missing # events will be ignored. You can create a catchall method named "call" for # those, instead. class Upload include Net::SSH::Loggable # The source of the upload (on the local server) attr_reader :local # The destination of the upload (on the remote server) attr_reader :remote # The hash of options that were given when the object was instantiated attr_reader :options # The SFTP session object used by this upload instance attr_reader :sftp # The properties hash for this object attr_reader :properties # Instantiates a new uploader process on top of the given SFTP session. # +local+ is either an IO object containing data to upload, or a string # identifying a file or directory on the local host. +remote+ is a string # identifying the location on the remote host that the upload should # target. # # This will return immediately, and requires that the SSH event loop be # run in order to effect the upload. (See #wait.) def initialize(sftp, local, remote, options={}, &progress) #:nodoc: @sftp = sftp @local = local @remote = remote @progress = progress || options[:progress] @options = options @properties = options[:properties] || {} @active = 0 self.logger = sftp.logger @uploads = [] @recursive = local.respond_to?(:read) ? false : ::File.directory?(local) if recursive? @stack = [entries_for(local)] @local_cwd = local @remote_cwd = remote @active += 1 if @options[:mkdir] sftp.mkdir(remote) do |response| @active -= 1 raise StatusException.new(response, "mkdir `#{remote}'") unless response.ok? (options[:requests] || RECURSIVE_READERS).to_i.times do break unless process_next_entry end end else @active -= 1 process_next_entry end else raise ArgumentError, "expected a file to upload" unless local.respond_to?(:read) || ::File.exists?(local) @stack = [[local]] process_next_entry end end # Returns true if a directory tree is being uploaded, and false if only a # single file is being uploaded. def recursive? @recursive end # Returns true if the uploader is currently running. When this is false, # the uploader has finished processing. def active? @active > 0 || @stack.any? end # Forces the transfer to stop. def abort! @active = 0 @stack.clear @uploads.clear end # Blocks until the upload has completed. def wait sftp.loop { active? } self end # Returns the property with the given name. This allows Upload instances # to store their own state when used as part of a state machine. def [](name) @properties[name.to_sym] end # Sets the given property to the given name. This allows Upload instances # to store their own state when used as part of a state machine. def []=(name, value) @properties[name.to_sym] = value end private #-- # "ruby -w" hates private attributes, so we have to do this longhand. #++ # The progress handler for this instance. Possibly nil. def progress; @progress; end # A simple struct for recording metadata about the file currently being # uploaded. LiveFile = Struct.new(:local, :remote, :io, :size, :handle) # The default # of bytes to read from disk at a time. DEFAULT_READ_SIZE = 32_000 # The number of readers to use when uploading a single file. SINGLE_FILE_READERS = 2 # The number of readers to use when uploading a directory. RECURSIVE_READERS = 16 # Examines the stack and determines what action to take. This is the # starting point of the state machine. def process_next_entry if @stack.empty? if @uploads.any? write_next_chunk(@uploads.first) elsif !active? update_progress(:finish) end return false elsif @stack.last.empty? @stack.pop @local_cwd = ::File.dirname(@local_cwd) @remote_cwd = ::File.dirname(@remote_cwd) process_next_entry elsif recursive? entry = @stack.last.shift lpath = ::File.join(@local_cwd, entry) rpath = ::File.join(@remote_cwd, entry) if ::File.directory?(lpath) @stack.push(entries_for(lpath)) @local_cwd = lpath @remote_cwd = rpath @active += 1 update_progress(:mkdir, rpath) request = sftp.mkdir(rpath, &method(:on_mkdir)) request[:dir] = rpath else open_file(lpath, rpath) end else open_file(@stack.pop.first, remote) end return true end # Prepares to send +local+ to +remote+. def open_file(local, remote) @active += 1 if local.respond_to?(:read) file = local name = options[:name] || "" else file = ::File.open(local, "rb") name = local end if file.respond_to?(:stat) size = file.stat.size else size = file.size end metafile = LiveFile.new(name, remote, file, size) update_progress(:open, metafile) request = sftp.open(remote, "w", &method(:on_open)) request[:file] = metafile end # Called when a +mkdir+ request finishes, successfully or otherwise. # If the request failed, this will raise a StatusException, otherwise # it will call #process_next_entry to continue the state machine. def on_mkdir(response) @active -= 1 dir = response.request[:dir] raise StatusException.new(response, "mkdir #{dir}") unless response.ok? process_next_entry end # Called when an +open+ request finishes. Raises StatusException if the # open failed, otherwise it calls #write_next_chunk to begin sending # data to the remote server. def on_open(response) @active -= 1 file = response.request[:file] raise StatusException.new(response, "open #{file.remote}") unless response.ok? file.handle = response[:handle] @uploads << file write_next_chunk(file) if !recursive? (options[:requests] || SINGLE_FILE_READERS).to_i.times { write_next_chunk(file) } end end # Called when a +write+ request finishes. Raises StatusException if the # write failed, otherwise it calls #write_next_chunk to continue the # write. def on_write(response) @active -= 1 file = response.request[:file] raise StatusException.new(response, "write #{file.remote}") unless response.ok? write_next_chunk(file) end # Called when a +close+ request finishes. Raises a StatusException if the # close failed, otherwise it calls #process_next_entry to continue the # state machine. def on_close(response) @active -= 1 file = response.request[:file] raise StatusException.new(response, "close #{file.remote}") unless response.ok? process_next_entry end # Attempts to send the next chunk from the given file (where +file+ is # a LiveFile instance). def write_next_chunk(file) if file.io.nil? process_next_entry else @active += 1 offset = file.io.pos data = file.io.read(options[:read_size] || DEFAULT_READ_SIZE) if data.nil? update_progress(:close, file) request = sftp.close(file.handle, &method(:on_close)) request[:file] = file file.io.close file.io = nil @uploads.delete(file) else update_progress(:put, file, offset, data) request = sftp.write(file.handle, offset, data, &method(:on_write)) request[:file] = file end end end # Returns all directory entries for the given path, removing the '.' # and '..' relative paths. def entries_for(local) ::Dir.entries(local).reject { |v| %w(. ..).include?(v) } end # Attempts to notify the progress monitor (if one was given) about # progress made for the given event. def update_progress(event, *args) on = "on_#{event}" if progress.respond_to?(on) progress.send(on, self, *args) elsif progress.respond_to?(:call) progress.call(event, self, *args) end end end end; end; end net-sftp-2.1.2/lib/net/sftp/operations/file.rb0000644000004100000410000001176712144456021021256 0ustar www-datawww-datarequire 'net/ssh/loggable' module Net; module SFTP; module Operations # A wrapper around an SFTP file handle, that exposes an IO-like interface # for interacting with the remote file. All operations are synchronous # (blocking), making this a very convenient way to deal with remote files. # # A wrapper is usually created via the Net::SFTP::Session#file factory: # # file = sftp.file.open("/path/to/remote") # puts file.gets # file.close class File # A reference to the Net::SFTP::Session instance that drives this wrapper attr_reader :sftp # The SFTP file handle object that this object wraps attr_reader :handle # The current position within the remote file attr_reader :pos # Creates a new wrapper that encapsulates the given +handle+ (such as # would be returned by Net::SFTP::Session#open!). The +sftp+ parameter # must be the same Net::SFTP::Session instance that opened the file. def initialize(sftp, handle) @sftp = sftp @handle = handle @pos = 0 @real_pos = 0 @real_eof = false @buffer = "" end # Repositions the file pointer to the given offset (relative to the # start of the file). This will also reset the EOF flag. def pos=(offset) @real_pos = @pos = offset @buffer = "" @real_eof = false end # Closes the underlying file and sets the handle to +nil+. Subsequent # operations on this object will fail. def close sftp.close!(handle) @handle = nil end # Returns true if the end of the file has been encountered by a previous # read. Setting the current file position via #pos= will reset this # flag (useful if the file's contents have changed since the EOF was # encountered). def eof? @real_eof && @buffer.empty? end # Reads up to +n+ bytes of data from the stream. Fewer bytes will be # returned if EOF is encountered before the requested number of bytes # could be read. Without an argument (or with a nil argument) all data # to the end of the file will be read and returned. # # This will advance the file pointer (#pos). def read(n=nil) loop do break if n && @buffer.length >= n break unless fill end if n result, @buffer = @buffer[0,n], (@buffer[n..-1] || "") else result, @buffer = @buffer, "" end @pos += result.length return result end # Reads up to the next instance of +sep_string+ in the stream, and # returns the bytes read (including +sep_string+). If +sep_string+ is # omitted, it defaults to +$/+. If EOF is encountered before any data # could be read, #gets will return +nil+. def gets(sep_string=$/) delim = if sep_string.length == 0 "#{$/}#{$/}" else sep_string end loop do at = @buffer.index(delim) if at offset = at + delim.length @pos += offset line, @buffer = @buffer[0,offset], @buffer[offset..-1] return line elsif !fill return nil if @buffer.empty? @pos += @buffer.length line, @buffer = @buffer, "" return line end end end # Same as #gets, but raises EOFError if EOF is encountered before any # data could be read. def readline(sep_string=$/) line = gets(sep_string) raise EOFError if line.nil? return line end # Writes the given data to the stream, incrementing the file position and # returning the number of bytes written. def write(data) data = data.to_s sftp.write!(handle, @real_pos, data) @real_pos += data.length @pos = @real_pos data.length end # Writes each argument to the stream. If +$\+ is set, it will be written # after all arguments have been written. def print(*items) items.each { |item| write(item) } write($\) if $\ nil end # Writes each argument to the stream, appending a newline to any item # that does not already end in a newline. Array arguments are flattened. def puts(*items) items.each do |item| if Array === item puts(*item) else write(item) write("\n") unless item[-1] == ?\n end end nil end # Performs an fstat operation on the handle and returns the attribute # object (Net::SFTP::Protocol::V01::Attributes, Net::SFTP::Protool::V04::Attributes, # or Net::SFTP::Protocol::V06::Attributes, depending on the SFTP protocol # version in use). def stat sftp.fstat!(handle) end private # Fills the buffer. Returns +true+ if it succeeded, and +false+ if # EOF was encountered before any data was read. def fill data = sftp.read!(handle, @real_pos, 8192) if data.nil? @real_eof = true return false else @real_pos += data.length @buffer << data end !@real_eof end end end; end; end net-sftp-2.1.2/lib/net/sftp/operations/file_factory.rb0000644000004100000410000000376412144456021023003 0ustar www-datawww-datarequire 'net/ssh/loggable' require 'net/sftp/operations/file' module Net; module SFTP; module Operations # A factory class for opening files and returning Operations::File instances # that wrap the SFTP handles that represent them. This is a convenience # class for use when working with files synchronously. Rather than relying # on the programmer to provide callbacks that define a state machine that # describes the behavior of the program, this class (and Operations::File) # provide an interface where calls will block until they return, mimicking # the IO class' interface. class FileFactory # The SFTP session object that drives this file factory. attr_reader :sftp # Create a new instance on top of the given SFTP session instance. def initialize(sftp) @sftp = sftp end # :call-seq: # open(name, flags="r", mode=nil) -> file # open(name, flags="r", mode=nil) { |file| ... } # # Attempt to open a file on the remote server. The +flags+ parameter # accepts the same values as the standard Ruby ::File#open method. The # +mode+ parameter must be an integer describing the permissions to use # if a new file is being created. # # If a block is given, the new Operations::File instance will be yielded # to it, and closed automatically when the block terminates. Otherwise # the object will be returned, and it is the caller's responsibility to # close the file. # # sftp.file.open("/tmp/names.txt", "w") do |f| # # ... # end def open(name, flags="r", mode=nil, &block) handle = sftp.open!(name, flags, :permissions => mode) file = Operations::File.new(sftp, handle) if block_given? begin yield file ensure file.close end else return file end end # Returns +true+ if the argument refers to a directory on the remote host. def directory?(path) sftp.lstat!(path).directory? end end end; end; end net-sftp-2.1.2/lib/net/sftp/operations/dir.rb0000644000004100000410000000565612144456021021115 0ustar www-datawww-datarequire 'net/ssh/loggable' module Net; module SFTP; module Operations # A convenience class for working with remote directories. It provides methods # for searching and enumerating directory entries, similarly to the standard # ::Dir class. # # sftp.dir.foreach("/remote/path") do |entry| # puts entry.name # end # # p sftp.dir.entries("/remote/path").map { |e| e.name } # # sftp.dir.glob("/remote/path", "**/*.rb") do |entry| # puts entry.name # end class Dir # The SFTP session object that drives this directory factory. attr_reader :sftp # Create a new instance on top of the given SFTP session instance. def initialize(sftp) @sftp = sftp end # Calls the block once for each entry in the named directory on the # remote server. Yields a Name object to the block, rather than merely # the name of the entry. def foreach(path) handle = sftp.opendir!(path) while entries = sftp.readdir!(handle) entries.each { |entry| yield entry } end return nil ensure sftp.close!(handle) if handle end # Returns an array of Name objects representing the items in the given # remote directory, +path+. def entries(path) results = [] foreach(path) { |entry| results << entry } return results end # Works as ::Dir.glob, matching (possibly recursively) all directory # entries under +path+ against +pattern+. If a block is given, matches # will be yielded to the block as they are found; otherwise, they will # be returned in an array when the method finishes. # # Because working over an SFTP connection is always going to be slower than # working purely locally, don't expect this method to perform with the # same level of alacrity that ::Dir.glob does; it will work best for # shallow directory hierarchies with relatively few directories, though # it should be able to handle modest numbers of files in each directory. def glob(path, pattern, flags=0) flags |= ::File::FNM_PATHNAME path = path.chop if path[-1,1] == "/" results = [] unless block_given? queue = entries(path).reject { |e| e.name == "." || e.name == ".." } while queue.any? entry = queue.shift if entry.directory? && !%w(. ..).include?(::File.basename(entry.name)) queue += entries("#{path}/#{entry.name}").map do |e| e.name.replace("#{entry.name}/#{e.name}") e end end if ::File.fnmatch(pattern, entry.name, flags) if block_given? yield entry else results << entry end end end return results unless block_given? end # Identical to calling #glob with a +flags+ parameter of 0 and no block. # Simply returns the matched entries as an array. def [](path, pattern) glob(path, pattern, 0) end end end; end; endnet-sftp-2.1.2/lib/net/sftp/version.rb0000644000004100000410000000056212144456021017630 0ustar www-datawww-datarequire 'net/ssh/version' module Net; module SFTP # Describes the current version of the Net::SFTP library. class Version < Net::SSH::Version MAJOR = 2 MINOR = 1 TINY = 2 # The current version, as a Version instance CURRENT = new(MAJOR, MINOR, TINY) # The current version, as a String instance STRING = CURRENT.to_s end end; end net-sftp-2.1.2/lib/net/sftp/request.rb0000644000004100000410000000610312144456021017630 0ustar www-datawww-datarequire 'net/sftp/constants' require 'net/sftp/response' module Net; module SFTP # Encapsulates a single active SFTP request. This is instantiated # automatically by the Net::SFTP::Session class when an operation is # executed. # # request = sftp.open("/path/to/file") # puts request.pending? #-> true # request.wait # puts request.pending? #-> false # result = request.response class Request include Constants::PacketTypes # The Net::SFTP session object that is servicing this request attr_reader :session # The SFTP packet identifier for this request attr_reader :id # The type of this request (e.g., :open, :symlink, etc.) attr_reader :type # The callback (if any) associated with this request. When the response # is recieved for this request, the callback will be invoked. attr_reader :callback # The hash of properties associated with this request. Properties allow # programmers to associate arbitrary data with a request, making state # machines richer. attr_reader :properties # The response that was received for this request (see Net::SFTP::Response) attr_reader :response # Instantiate a new Request object, serviced by the given +session+, and # being of the given +type+. The +id+ is the packet identifier for this # request. def initialize(session, type, id, &callback) #:nodoc: @session, @id, @type, @callback = session, id, type, callback @response = nil @properties = {} end # Returns the value of property with the given +key+. If +key+ is not a # symbol, it will be converted to a symbol before lookup. def [](key) properties[key.to_sym] end # Sets the value of the property with name +key+ to +value+. If +key+ is # not a symbol, it will be converted to a symbol before lookup. def []=(key, value) properties[key.to_sym] = value end # Returns +true+ if the request is still waiting for a response from the # server, and +false+ otherwise. The SSH event loop must be run in order # for a request to be processed; see #wait. def pending? session.pending_requests.key?(id) end # Waits (blocks) until the server responds to this packet. If prior # SFTP packets were also pending, they will be processed as well (since # SFTP packets are processed in the order in which they are received by # the server). Returns the request object itself. def wait session.loop { pending? } self end public # but not "published". Internal use only # When the server responds to this request, the packet is passed to # this method, which parses the packet and builds a Net::SFTP::Response # object to encapsulate it. If a #callback has been provided for this # request, the callback is invoked with the new response object. def respond_to(packet) #:nodoc: data = session.protocol.parse(packet) data[:type] = packet.type @response = Response.new(self, data) callback.call(@response) if callback end end end; endnet-sftp-2.1.2/lib/net/sftp/errors.rb0000644000004100000410000000206612144456021017460 0ustar www-datawww-datamodule Net; module SFTP # The base exception class for the SFTP system. class Exception < RuntimeError; end # A exception class for reporting a non-success result of an operation. class StatusException < Net::SFTP::Exception # The response object that caused the exception. attr_reader :response # The error code (numeric) attr_reader :code # The description of the error attr_reader :description # Any incident-specific text given when the exception was raised attr_reader :text # Create a new status exception that reports the given code and # description. def initialize(response, text=nil) @response, @text = response, text @code = response.code @description = response.message @description = Response::MAP[@code] if @description.nil? || @description.empty? end # Override the default message format, to include the code and # description. def message m = super.dup m << " #{text}" if text m << " (#{code}, #{description.inspect})" end end end; end net-sftp-2.1.2/lib/net/sftp/protocol.rb0000644000004100000410000000201112144456021017773 0ustar www-datawww-datarequire 'net/sftp/protocol/01/base' require 'net/sftp/protocol/02/base' require 'net/sftp/protocol/03/base' require 'net/sftp/protocol/04/base' require 'net/sftp/protocol/05/base' require 'net/sftp/protocol/06/base' module Net; module SFTP # The Protocol module contains the definitions for all supported SFTP # protocol versions. module Protocol # Instantiates and returns a new protocol driver instance for the given # protocol version. +session+ must be a valid SFTP session object, and # +version+ must be an integer. If an unsupported version is given, # an exception will be raised. def self.load(session, version) case version when 1 then V01::Base.new(session) when 2 then V02::Base.new(session) 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) else raise NotImplementedError, "unsupported SFTP version #{version.inspect}" end end end end; endnet-sftp-2.1.2/lib/net/sftp/response.rb0000644000004100000410000000416712144456021020006 0ustar www-datawww-datarequire 'net/sftp/constants' module Net; module SFTP # Encapsulates a response from the remote server, to a specific client # request. Response objects are passed as parameters to callbacks when you # are performing asynchronous operations; when you call Net::SFTP::Request#wait, # you can get the corresponding response object via Net::SFTP::Request#response. # # sftp.open("/path/to/file") do |response| # p response.ok? # p response[:handle] # end # # sftp.loop class Response include Net::SFTP::Constants::StatusCodes # The request object that this object is in response to attr_reader :request # A hash of request-specific data, such as a file handle or attribute information attr_reader :data # The numeric code, one of the FX_* constants attr_reader :code # The textual message for this response (possibly blank) attr_reader :message # Create a new Response object for the given Net::SFTP::Request instance, # and with the given data. If there is no :code key in the data, the # code is assumed to be FX_OK. def initialize(request, data={}) #:nodoc: @request, @data = request, data @code, @message = data[:code] || FX_OK, data[:message] end # Retrieve the data item with the given +key+. The key is converted to a # symbol before being used to lookup the value. def [](key) data[key.to_sym] end # Returns a textual description of this response, including the status # code and name. def to_s if message && !message.empty? && message.downcase != MAP[code] "#{message} (#{MAP[code]}, #{code})" else "#{MAP[code]} (#{code})" end end alias :to_str :to_s # Returns +true+ if the status code is FX_OK; +false+ otherwise. def ok? code == FX_OK end # Returns +true+ if the status code is FX_EOF; +false+ otherwise. def eof? code == FX_EOF end #-- MAP = constants.inject({}) do |memo, name| next memo unless name =~ /^FX_(.*)/ memo[const_get(name)] = $1.downcase.tr("_", " ") memo end #++ end end; endnet-sftp-2.1.2/lib/net/sftp/constants.rb0000644000004100000410000001456312144456021020165 0ustar www-datawww-datamodule Net module SFTP # The packet types and other general constants used by the SFTP protocol. # See the specification for the SFTP protocol for a full discussion of their # meaning and usage. module Constants # The various packet types supported by SFTP protocol versions 1 through 6. # The FXP_EXTENDED and FXP_EXTENDED_REPLY packet types are not currently # understood by Net::SFTP. module PacketTypes FXP_INIT = 1 FXP_VERSION = 2 FXP_OPEN = 3 FXP_CLOSE = 4 FXP_READ = 5 FXP_WRITE = 6 FXP_LSTAT = 7 FXP_FSTAT = 8 FXP_SETSTAT = 9 FXP_FSETSTAT = 10 FXP_OPENDIR = 11 FXP_READDIR = 12 FXP_REMOVE = 13 FXP_MKDIR = 14 FXP_RMDIR = 15 FXP_REALPATH = 16 FXP_STAT = 17 FXP_RENAME = 18 FXP_READLINK = 19 FXP_SYMLINK = 20 FXP_LINK = 21 FXP_BLOCK = 22 FXP_UNBLOCK = 23 FXP_STATUS = 101 FXP_HANDLE = 102 FXP_DATA = 103 FXP_NAME = 104 FXP_ATTRS = 105 FXP_EXTENDED = 200 FXP_EXTENDED_REPLY = 201 end # Beginning in version 5 of the protocol, Net::SFTP::Session#rename accepts # an optional +flags+ argument that must be either 0 or a combination of # these constants. module RenameFlags OVERWRITE = 0x00000001 ATOMIC = 0x00000002 NATIVE = 0x00000004 end # When an FXP_STATUS packet is received from the server, the +code+ will # be one of the following constants. module StatusCodes FX_OK = 0 FX_EOF = 1 FX_NO_SUCH_FILE = 2 FX_PERMISSION_DENIED = 3 FX_FAILURE = 4 FX_BAD_MESSAGE = 5 FX_NO_CONNECTION = 6 FX_CONNECTION_LOST = 7 FX_OP_UNSUPPORTED = 8 FX_INVALID_HANDLE = 9 FX_NO_SUCH_PATH = 10 FX_FILE_ALREADY_EXISTS = 11 FX_WRITE_PROTECT = 12 FX_NO_MEDIA = 13 FX_NO_SPACE_ON_FILESYSTEM = 14 FX_QUOTA_EXCEEDED = 15 FX_UNKNOWN_PRINCIPLE = 16 FX_LOCK_CONFlICT = 17 FX_DIR_NOT_EMPTY = 18 FX_NOT_A_DIRECTORY = 19 FX_INVALID_FILENAME = 20 FX_LINK_LOOP = 21 end # The Net::SFTP::Session#open operation is one of the worst casualties of # the revisions between SFTP protocol versions. The flags change considerably # between version 1 and version 6. Net::SFTP tries to shield programmers # from the differences, so you'll almost never need to use these flags # directly, but if you ever need to specify some flag that isn't exposed # by the higher-level API, these are the ones that are available to you. module OpenFlags # These are the flags that are understood by versions 1-4 of the the # open operation. module FV1 READ = 0x00000001 WRITE = 0x00000002 APPEND = 0x00000004 CREAT = 0x00000008 TRUNC = 0x00000010 EXCL = 0x00000020 end # Version 5 of the open operation totally discarded the flags understood # by versions 1-4, and replaced them with these. module FV5 CREATE_NEW = 0x00000000 CREATE_TRUNCATE = 0x00000001 OPEN_EXISTING = 0x00000002 OPEN_OR_CREATE = 0x00000003 TRUNCATE_EXISTING = 0x00000004 APPEND_DATA = 0x00000008 APPEND_DATA_ATOMIC = 0x00000010 TEXT_MODE = 0x00000020 READ_LOCK = 0x00000040 WRITE_LOCK = 0x00000080 DELETE_LOCK = 0x00000100 end # Version 6 of the open operation added these flags, in addition to the # flags understood by version 5. module FV6 ADVISORY_LOCK = 0x00000200 NOFOLLOW = 0x00000400 DELETE_ON_CLOSE = 0x00000800 ACCESS_AUDIT_ALARM_INFO = 0x00001000 ACCESS_BACKUP = 0x00002000 BACKUP_STREAM = 0x00004000 OVERRIDE_OWNER = 0x00008000 end end # The Net::SFTP::Session#block operation, implemented in version 6 of # the protocol, understands these constants for the +mask+ parameter. module LockTypes READ = OpenFlags::FV5::READ_LOCK WRITE = OpenFlags::FV5::WRITE_LOCK DELETE = OpenFlags::FV5::DELETE_LOCK ADVISORY = OpenFlags::FV6::ADVISORY_LOCK end module ACE # Access control entry types, used from version 4 of the protocol, # onward. See Net::SFTP::Protocol::V04::Attributes::ACL. module Type ACCESS_ALLOWED = 0x00000000 ACCESS_DENIED = 0x00000001 SYSTEM_AUDIT = 0x00000002 SYSTEM_ALARM = 0x00000003 end # Access control entry flags, used from version 4 of the protocol, # onward. See Net::SFTP::Protocol::V04::Attributes::ACL. module Flag FILE_INHERIT = 0x00000001 DIRECTORY_INHERIT = 0x00000002 NO_PROPAGATE_INHERIT = 0x00000004 INHERIT_ONLY = 0x00000008 SUCCESSFUL_ACCESS = 0x00000010 FAILED_ACCESS = 0x00000020 IDENTIFIER_GROUP = 0x00000040 end # Access control entry masks, used from version 4 of the protocol, # onward. See Net::SFTP::Protocol::V04::Attributes::ACL. module Mask READ_DATA = 0x00000001 LIST_DIRECTORY = 0x00000001 WRITE_DATA = 0x00000002 ADD_FILE = 0x00000002 APPEND_DATA = 0x00000004 ADD_SUBDIRECTORY = 0x00000004 READ_NAMED_ATTRS = 0x00000008 WRITE_NAMED_ATTRS = 0x00000010 EXECUTE = 0x00000020 DELETE_CHILD = 0x00000040 READ_ATTRIBUTES = 0x00000080 WRITE_ATTRIBUTES = 0x00000100 DELETE = 0x00010000 READ_ACL = 0x00020000 WRITE_ACL = 0x00040000 WRITE_OWNER = 0x00080000 SYNCHRONIZE = 0x00100000 end end end end end net-sftp-2.1.2/lib/net/sftp/packet.rb0000644000004100000410000000117612144456021017414 0ustar www-datawww-datarequire 'net/ssh/buffer' module Net; module SFTP # A specialization of the Net::SSH::Buffer class, which simply auto-reads # the type byte from the front of every packet it represents. class Packet < Net::SSH::Buffer # The (intger) type of this packet. See Net::SFTP::Constants for all # possible packet types. attr_reader :type # Create a new Packet object that wraps the given +data+ (which should be # a String). The first byte of the data will be consumed automatically and # interpreted as the #type of this packet. def initialize(data) super @type = read_byte end end end; endnet-sftp-2.1.2/lib/net/sftp/protocol/0000755000004100000410000000000012144456021017454 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/sftp/protocol/02/0000755000004100000410000000000012144456021017675 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/sftp/protocol/02/base.rb0000644000004100000410000000177212144456021021143 0ustar www-datawww-datarequire 'net/sftp/protocol/01/base' module Net; module SFTP; module Protocol; module V02 # Wraps the low-level SFTP calls for version 2 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 Base < V01::Base # Returns the protocol version implemented by this driver. (2, in this # case) def version 2 end # Sends a FXP_RENAME packet to the server to request that the file or # directory with the given +name+ (must be a full path) be changed to # +new_name+ (which must also be a path). The +flags+ parameter is # ignored in this version of the protocol. def rename(name, new_name, flags=nil) send_request(FXP_RENAME, :string, name, :string, new_name) end end end; end; end; end net-sftp-2.1.2/lib/net/sftp/protocol/base.rb0000644000004100000410000000332312144456021020714 0ustar www-datawww-datarequire 'net/ssh/loggable' require 'net/sftp/constants' module Net; module SFTP; module Protocol # The abstract superclass of the specific implementations for each supported # SFTP protocol version. It implements general packet parsing logic, and # provides a way for subclasses to send requests. class Base include Net::SSH::Loggable include Net::SFTP::Constants include Net::SFTP::Constants::PacketTypes # The SFTP session object that acts as client to this protocol instance attr_reader :session # Create a new instance of a protocol driver, servicing the given session. def initialize(session) @session = session self.logger = session.logger @request_id_counter = -1 end # Attept to parse the given packet. If the packet is of an unsupported # type, an exception will be raised. Returns the parsed data as a hash # (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) else raise NotImplementedError, "unknown packet type: #{packet.type}" end end private # Send a new packet of the given type, and with the given data arguments. # A new request identifier will be allocated to this request, and will # be returned. def send_request(type, *args) @request_id_counter += 1 session.send_packet(type, :long, @request_id_counter, *args) return @request_id_counter end end end; end; endnet-sftp-2.1.2/lib/net/sftp/protocol/06/0000755000004100000410000000000012144456021017701 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/sftp/protocol/06/base.rb0000644000004100000410000000466212144456021021150 0ustar www-datawww-datarequire 'net/sftp/protocol/05/base' require 'net/sftp/protocol/06/attributes' 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 Base < V05::Base # Returns the protocol version implemented by this driver. (6, in this # case) def version 6 end # Sends a FXP_LINK packet to the server to request that a link be created # at +new_link_path+, pointing to +existing_path+. If +symlink+ is true, a # symbolic link will be created; otherwise a hard link will be created. def link(new_link_path, existing_path, symlink) send_request(FXP_LINK, :string, new_link_path, :string, existing_path, :bool, symlink) end # Provided for backwards compatibility; v6 of the SFTP protocol removes the # older FXP_SYMLINK packet type, so this method simply calls the #link # method. def symlink(path, target) link(path, target, true) end # Sends a FXP_BLOCK packet to the server to request that a byte-range lock # be obtained on the given +handle+, for the given byte +offset+ and # +length+. The +mask+ parameter is a bitfield indicating what kind of # lock to acquire, and must be a combination of one or more of the # Net::SFTP::Constants::LockTypes constants. def block(handle, offset, length, mask) send_request(FXP_BLOCK, :string, handle, :int64, offset, :int64, length, :long, mask) end # Sends a FXP_UNBLOCK packet to the server to request that a previously # acquired byte-range lock be released on the given +handle+, for the # given byte +offset+ and +length+. The +handle+, +offset+, and +length+ # must all exactly match the parameters that were given when the lock was # originally acquired (see #block). def unblock(handle, offset, length) send_request(FXP_UNBLOCK, :string, handle, :int64, offset, :int64, length) end protected # Returns the Attributes class used by this version of the protocol # (Net::SFTP::Protocol::V06::Attributes, in this case) def attribute_factory V06::Attributes end end end; end; end; endnet-sftp-2.1.2/lib/net/sftp/protocol/06/attributes.rb0000644000004100000410000001153312144456021022417 0ustar www-datawww-datarequire 'net/sftp/protocol/04/attributes' module Net; module SFTP; module Protocol; module V06 # 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 6 and higher 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) # * :allocation_size:: the actual number of bytes that the item uses on disk (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) # * :ctime:: the time that the file's attributes were last changed (integer) # * :ctime_nseconds:: the nanosecond component of ctime (integer) # * :acl:: an array of ACL entries for the item # * :attrib_bits:: other attributes of the file or directory (as a bit field) (integer) # * :attrib_bits_valid:: a mask describing which bits in attrib_bits are valid (integer) # * :text_hint:: whether the file may or may not contain textual data (integer) # * :mime_type:: the mime type of the file (string) # * :link_count:: the hard link count of the file (integer) # * :untranslated_name:: the value of the filename before filename translation was attempted (string) # * :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 F_ALLOCATION_SIZE = 0x00000400 F_TEXT_HINT = 0x00000800 F_MIME_TYPE = 0x00001000 F_LINK_COUNT = 0x00002000 F_UNTRANSLATED_NAME = 0x00004000 F_CTIME = 0x00008000 # The array of elements that describe this structure, in order. Used when # parsing and serializing attribute objects. def self.elements #:nodoc: @elements ||= [ [:type, :byte, 0], [:size, :int64, F_SIZE], [:allocation_size, :int64, F_ALLOCATION_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], [:ctime, :int64, F_CTIME], [:ctime_nseconds, :long, F_CTIME | F_SUBSECOND_TIMES], [:acl, :special, F_ACL], [:attrib_bits, :long, F_BITS], [:attrib_bits_valid, :long, F_BITS], [:text_hint, :byte, F_TEXT_HINT], [:mime_type, :string, F_MIME_TYPE], [:link_count, :long, F_LINK_COUNT], [:untranslated_name, :string, F_UNTRANSLATED_NAME], [:extended, :special, F_EXTENDED] ] end # The size on-disk of the file attr_accessor :allocation_size # The time at which the file's attributes were last changed attr_accessor :ctime # The nanosecond component of #ctime attr_accessor :ctime_nseconds # Other attributes of this file or directory (as a bit field) attr_accessor :attrib_bits # A bit mask describing which bits in #attrib_bits are valid attr_accessor :attrib_bits_valid # Describes whether the file may or may not contain textual data attr_accessor :text_hint # The mime-type of the file attr_accessor :mime_type # The hard link count for the file attr_accessor :link_count # The value of the file name before filename translation was attempted attr_accessor :untranslated_name end end; end; end; endnet-sftp-2.1.2/lib/net/sftp/protocol/04/0000755000004100000410000000000012144456021017677 5ustar www-datawww-datanet-sftp-2.1.2/lib/net/sftp/protocol/04/base.rb0000644000004100000410000000701712144456021021143 0ustar www-datawww-datarequire 'net/sftp/protocol/03/base' require 'net/sftp/protocol/04/attributes' require 'net/sftp/protocol/04/name' module Net; module SFTP; module Protocol; module V04 # Wraps the low-level SFTP calls for version 4 of the SFTP protocol. Also # implements the updated FXP_NAME packet parsing as mandated by v4 of the # 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 Base < V03::Base # Returns the protocol version implemented by this driver. (4, in this # case) def version 4 end # As of v4 of the SFTP protocol, the "longname" member was removed from the # FXP_NAME structure. This method is essentially the same as the previous # implementation, but omits longname. def parse_name_packet(packet) names = [] packet.read_long.times do filename = packet.read_string attrs = attribute_factory.from_buffer(packet) names << name_factory.new(filename, attrs) end { :names => names } end # Sends a FXP_STAT packet to the server for the given +path+, and with the # given +flags+. If +flags+ is nil, it defaults to F_SIZE | F_PERMISSIONS | # F_ACCESSTIME | F_CREATETIME | F_MODIFYTIME | F_ACL | F_OWNERGROUP | # F_SUBSECOND_TIMES | F_EXTENDED (see Net::SFTP::Protocol::V04::Attributes # for those constants). def stat(path, flags=nil) send_request(FXP_STAT, :string, path, :long, flags || DEFAULT_FLAGS) end # Sends a FXP_LSTAT packet to the server for the given +path+, and with the # given +flags+. If +flags+ is nil, it defaults to F_SIZE | F_PERMISSIONS | # F_ACCESSTIME | F_CREATETIME | F_MODIFYTIME | F_ACL | F_OWNERGROUP | # F_SUBSECOND_TIMES | F_EXTENDED (see Net::SFTP::Protocol::V04::Attributes # for those constants). def lstat(path, flags=nil) send_request(FXP_LSTAT, :string, path, :long, flags || DEFAULT_FLAGS) end # Sends a FXP_FSTAT packet to the server for the given +path+, and with the # given +flags+. If +flags+ is nil, it defaults to F_SIZE | F_PERMISSIONS | # F_ACCESSTIME | F_CREATETIME | F_MODIFYTIME | F_ACL | F_OWNERGROUP | # F_SUBSECOND_TIMES | F_EXTENDED (see Net::SFTP::Protocol::V04::Attributes # for those constants). def fstat(handle, flags=nil) send_request(FXP_FSTAT, :string, handle, :long, flags || DEFAULT_FLAGS) end protected # The default flags used if the +flags+ parameter is nil for any of the # #stat, #lstat, or #fstat operations. DEFAULT_FLAGS = Attributes::F_SIZE | Attributes::F_PERMISSIONS | Attributes::F_ACCESSTIME | Attributes::F_CREATETIME | Attributes::F_MODIFYTIME | Attributes::F_ACL | Attributes::F_OWNERGROUP | Attributes::F_SUBSECOND_TIMES | Attributes::F_EXTENDED # Returns the Attributes class used by this version of the protocol # (Net::SFTP::Protocol::V04::Attributes, in this case) def attribute_factory V04::Attributes end # Returns the Name class used by this version of the protocol # (Net::SFTP::Protocol::V04::Name, in this case) def name_factory V04::Name end end end; end; end; endnet-sftp-2.1.2/lib/net/sftp/protocol/04/attributes.rb0000644000004100000410000001423212144456021022414 0ustar www-datawww-datarequire 'net/sftp/protocol/01/attributes' module Net; module SFTP; module Protocol; module V04 # 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 # * :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 < V01::Attributes F_ACCESSTIME = 0x00000008 F_CREATETIME = 0x00000010 F_MODIFYTIME = 0x00000020 F_ACL = 0x00000040 F_OWNERGROUP = 0x00000080 F_SUBSECOND_TIMES = 0x00000100 # A simple struct for representing a single entry in an Access Control # List. (See Net::SFTP::Constants::ACE) ACL = Struct.new(:type, :flag, :mask, :who) class < packet.read_string } end # Parses the given FXP_STATUS packet and returns a hash with one key, # :code, which references the status code returned by the server. def parse_status_packet(packet) { :code => packet.read_long } end # Parses the given FXP_DATA packet and returns a hash with one key, # :data, which references the data returned in the packet. def parse_data_packet(packet) { :data => packet.read_string } end # Parses the given FXP_ATTRS packet and returns a hash with one key, # :attrs, which references an Attributes object. def parse_attrs_packet(packet) { :attrs => attribute_factory.from_buffer(packet) } end # Parses the given FXP_NAME packet and returns a hash with one key, :names, # which references an array of Name objects. def parse_name_packet(packet) names = [] packet.read_long.times do filename = packet.read_string longname = packet.read_string attrs = attribute_factory.from_buffer(packet) names << name_factory.new(filename, longname, attrs) end { :names => names } end # Sends a FXP_OPEN packet to the server and returns the packet identifier. # The +flags+ parameter is either an integer (in which case it must be # a combination of the IO constants) or a string (in which case it must # be one of the mode strings that IO::open accepts). The +options+ # parameter is a hash that is used to construct a new Attribute object, # to pass as part of the FXP_OPEN request. def open(path, flags, options) flags = normalize_open_flags(flags) if flags & (IO::WRONLY | IO::RDWR) != 0 sftp_flags = FV1::WRITE sftp_flags |= FV1::READ if flags & IO::RDWR != 0 sftp_flags |= FV1::APPEND if flags & IO::APPEND != 0 else sftp_flags = FV1::READ end sftp_flags |= FV1::CREAT if flags & IO::CREAT != 0 sftp_flags |= FV1::TRUNC if flags & IO::TRUNC != 0 sftp_flags |= FV1::EXCL if flags & IO::EXCL != 0 attributes = attribute_factory.new(options) send_request(FXP_OPEN, :string, path, :long, sftp_flags, :raw, attributes.to_s) end # Sends a FXP_CLOSE packet to the server for the given +handle+ (such as # would be returned via a FXP_HANDLE packet). Returns the new packet id. def close(handle) send_request(FXP_CLOSE, :string, handle) end # Sends a FXP_READ packet to the server, requesting that +length+ bytes # be read from the file identified by +handle+, starting at +offset+ bytes # within the file. The handle must be one that was returned via a # FXP_HANDLE packet. Returns the new packet id. def read(handle, offset, length) send_request(FXP_READ, :string, handle, :int64, offset, :long, length) end # Sends a FXP_WRITE packet to the server, requesting that +data+ (a string), # be written to the file identified by +handle+, starting at +offset+ bytes # from the beginning of the file. The handle must be one that was returned # via a FXP_HANDLE packet. Returns the new packet id. def write(handle, offset, data) send_request(FXP_WRITE, :string, handle, :int64, offset, :string, data) end # Sends a FXP_LSTAT packet to the server, requesting a FXP_ATTR response # for the file at the given remote +path+ (a string). The +flags+ parameter # is ignored in this version of the protocol. #lstat will not follow # symbolic links; see #stat for a version that will. def lstat(path, flags=nil) send_request(FXP_LSTAT, :string, path) end # Sends a FXP_FSTAT packet to the server, requesting a FXP_ATTR response # for the file represented by the given +handle+ (which must have been # obtained from a FXP_HANDLE packet). The +flags+ parameter is ignored in # this version of the protocol. def fstat(handle, flags=nil) send_request(FXP_FSTAT, :string, handle) end # Sends a FXP_SETSTAT packet to the server, to update the attributes for # the file at the given remote +path+ (a string). The +attrs+ parameter is # a hash that defines the attributes to set. def setstat(path, attrs) send_request(FXP_SETSTAT, :string, path, :raw, attribute_factory.new(attrs).to_s) end # Sends a FXP_FSETSTAT packet to the server, to update the attributes for # the file represented by the given +handle+ (which must have been obtained # from a FXP_HANDLE packet). The +attrs+ parameter is a hash that defines # the attributes to set. def fsetstat(handle, attrs) send_request(FXP_FSETSTAT, :string, handle, :raw, attribute_factory.new(attrs).to_s) end # Sends a FXP_OPENDIR packet to the server, to request a handle for # manipulating the directory at the given remote +path+. def opendir(path) send_request(FXP_OPENDIR, :string, path) end # Sends a FXP_READDIR packet to the server, to request a batch of # directory name entries in the directory identified by +handle+ (which # must have been obtained via a FXP_OPENDIR request). def readdir(handle) send_request(FXP_READDIR, :string, handle) end # Sends a FXP_REMOTE packet to the server, to request that the given # file be deleted from the remote server. def remove(filename) send_request(FXP_REMOVE, :string, filename) end # Sends a FXP_MKDIR packet to the server, to request that a new directory # at +path+ on the remote server be created, and with +attrs+ (a hash) # describing the attributes of the new directory. def mkdir(path, attrs) send_request(FXP_MKDIR, :string, path, :raw, attribute_factory.new(attrs).to_s) end # Sends a FXP_RMDIR packet to the server, to request that the directory # at +path+ on the remote server be deleted. def rmdir(path) send_request(FXP_RMDIR, :string, path) end # Sends a FXP_REALPATH packet to the server, to request that the given # +path+ be canonicalized, taking into account path segments like "..". def realpath(path) send_request(FXP_REALPATH, :string, path) end # Sends a FXP_STAT packet to the server, requesting a FXP_ATTR response # for the file at the given remote +path+ (a string). The +flags+ parameter # is ignored in this version of the protocol. #stat will follow # symbolic links; see #lstat for a version that will not. def stat(path, flags=nil) send_request(FXP_STAT, :string, path) end # Not implemented in version 1 of the SFTP protocol. Raises a # NotImplementedError if called. def rename(name, new_name, flags=nil) not_implemented! :rename end # Not implemented in version 1 of the SFTP protocol. Raises a # NotImplementedError if called. def readlink(path) not_implemented! :readlink end # Not implemented in version 1 of the SFTP protocol. Raises a # NotImplementedError if called. def symlink(path, target) not_implemented! :symlink end # Not implemented in version 1 of the SFTP protocol. Raises a # NotImplementedError if called. def link(*args) not_implemented! :link end # Not implemented in version 1 of the SFTP protocol. Raises a # NotImplementedError if called. def block(handle, offset, length, mask) not_implemented! :block end # Not implemented in version 1 of the SFTP protocol. Raises a # NotImplementedError if called. def unblock(handle, offset, length) not_implemented! :unblock end protected # A helper method for implementing wrappers for operations that are # not implemented by the current SFTP protocol version. Simply raises # NotImplementedError with a message based on the given operation name. def not_implemented!(operation) raise NotImplementedError, "the #{operation} operation is not available in the version of the SFTP protocol supported by your server" end # Normalizes the given flags parameter, converting it into a combination # of IO constants. def normalize_open_flags(flags) if String === flags case flags.tr("b", "") when "r" then IO::RDONLY when "r+" then IO::RDWR when "w" then IO::WRONLY | IO::TRUNC | IO::CREAT when "w+" then IO::RDWR | IO::TRUNC | IO::CREAT when "a" then IO::APPEND | IO::CREAT | IO::WRONLY when "a+" then IO::APPEND | IO::CREAT | IO::RDWR else raise ArgumentError, "unsupported flags: #{flags.inspect}" end else flags.to_i end end # Returns the Attributes class used by this version of the protocol # (Net::SFTP::Protocol::V01::Attributes, in this case) def attribute_factory V01::Attributes end # Returns the Name class used by this version of the protocol # (Net::SFTP::Protocol::V01::Name, in this case) def name_factory V01::Name end end end; end; end; endnet-sftp-2.1.2/lib/net/sftp/protocol/01/attributes.rb0000644000004100000410000002426512144456021022420 0ustar www-datawww-datarequire 'net/ssh/buffer' module Net; module SFTP; module Protocol; module V01 # 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. # # To specify new attributes, just pass a hash as the argument to the # constructor. The following keys are supported: # # * :size:: the size of the file # * :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) # * :mtime:: the modification time of the file (integer, seconds since epoch) # * :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 F_SIZE = 0x00000001 F_UIDGID = 0x00000002 F_PERMISSIONS = 0x00000004 F_ACMODTIME = 0x00000008 F_EXTENDED = 0x80000000 T_REGULAR = 1 T_DIRECTORY = 2 T_SYMLINK = 3 T_SPECIAL = 4 T_UNKNOWN = 5 T_SOCKET = 6 T_CHAR_DEVICE = 7 T_BLOCK_DEVICE = 8 T_FIFO = 9 class < anything begin session.shutdown! rescue ::Exception # swallow exceptions that occur while trying to shutdown end raise anything end end end class Net::SSH::Connection::Session # A convenience method for starting up a new SFTP connection on the current # SSH session. Blocks until the SFTP session is fully open, and then # returns the SFTP session. # # Net::SSH.start("localhost", "user", "password") do |ssh| # ssh.sftp.upload!("/local/file.tgz", "/remote/file.tgz") # ssh.exec! "cd /some/path && tar xf /remote/file.tgz && rm /remote/file.tgz" # end def sftp(wait=true) @sftp ||= begin sftp = Net::SFTP::Session.new(self) sftp.connect! if wait sftp end end end