net-scp-1.2.1/0000755000004100000410000000000012346240767013127 5ustar www-datawww-datanet-scp-1.2.1/Rakefile0000644000004100000410000000305012346240767014572 0ustar www-datawww-datarequire "rubygems" require "rake" require "rake/clean" require "rdoc/task" task :default => ["build"] CLEAN.include [ 'pkg', 'rdoc' ] name = "net-scp" $:.unshift File.join(File.dirname(__FILE__), 'lib') require "net/scp/version" version = Net::SCP::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 SCP client protocol" s.description = s.summary s.email = "net-ssh@solutious.com" s.homepage = "https://github.com/net-ssh/net-scp" 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-scp-1.2.1/CHANGES.txt0000644000004100000410000000216112346240767014740 0ustar www-datawww-data === 1.2.1 / 30 Apr 2014 * Resign gem with new pubkey === 1.2.0 / 11 Apr 2014 * Get the error string during download [jkeiser] === 1.1.2 / 6 Jul 2013 * Explicit convert to string in shellescape [jwils] === 1.1.1 / 13 May 2013 * Allow passing a shell to use when executing scp. [Arthur Schreiber] === 1.1.0 / 06 Feb 2013 * Added public cert. All gem releases are now signed. See INSTALL in readme. === 1.0.4 / 16 Sep 2010 * maintain filename sanitization compatibility with ruby 1.8.6 [Sung Pae, Tim Charper] === 1.0.3 / 17 Aug 2010 * replace :sanitize_file_name with a call to String#shellescape [Sung Pae] * Added gemspec file and removed echoe dependency [Miron Cuperman, Delano Mandelbaum] * Removed Hanna dependency in Rakefile [Delano Mandelbaum] === 1.0.2 / 4 Feb 2009 * Escape spaces in file names on remote server [Jamis Buck] === 1.0.1 / 29 May 2008 * Make sure downloads open the file in binary mode to appease Windows [Jamis Buck] === 1.0.0 / 1 May 2008 * Pass the channel object as the first argument to the progress callback [Jamis Buck] === 1.0 Preview Release 1 (0.99.0) / 22 Mar 2008 * Birthday! net-scp-1.2.1/data.tar.gz.sig0000644000004100000410000000040012346240767015742 0ustar www-datawww-datapӫS@ZP$Ji H 1j*vJ3IrFZ? @xu8ɼ_Please note: this project is in maintenance mode. It is not under active development but pull requests are very much welcome. Just be sure to include tests! -- delano * Docs: http://net-ssh.github.com/net-scp * Issues: https://github.com/net-ssh/net-scp/issues * Codes: https://github.com/net-ssh/net-scp * Email: net-ssh@solutious.com As of v1.0.5, all gem releases are signed. See INSTALL. == DESCRIPTION: Net::SCP is a pure-Ruby implementation of the SCP protocol. This operates over SSH (and requires the Net::SSH library), and allows files and directory trees to copied to and from a remote server. == FEATURES/PROBLEMS: * Transfer files or entire directory trees to or from a remote host via SCP * Can preserve file attributes across transfers * Can download files in-memory, or direct-to-disk * Support for SCP URI's, and OpenURI == SYNOPSIS: In a nutshell: require 'net/scp' # upload a file to a remote server Net::SCP.upload!("remote.host.com", "username", "/local/path", "/remote/path", :ssh => { :password => "password" }) # download a file from a remote server Net::SCP.download!("remote.host.com", "username", "/remote/path", "/local/path", :ssh => { :password => "password" }) # download a file to an in-memory buffer data = Net::SCP::download!("remote.host.com", "username", "/remote/path") # use a persistent connection to transfer files Net::SCP.start("remote.host.com", "username", :ssh => { :password => "password" }) do |scp| # upload a file to a remote server scp.upload! "/local/path", "/remote/path" # upload from an in-memory buffer scp.upload! StringIO.new("some data to upload"), "/remote/path" # run multiple downloads in parallel d1 = scp.download("/remote/path", "/local/path") d2 = scp.download("/remote/path2", "/local/path2") [d1, d2].each { |d| d.wait } end # You can also use open-uri to grab data via scp: require 'uri/open-scp' data = open("scp://user@host/path/to/file.txt").read For more information, see Net::SCP. == REQUIREMENTS: * Net::SSH 2 If you wish to run the tests, you'll also need: * Echoe (for Rakefile use) * Mocha (for tests) == INSTALL: * gem install net-scp (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-scp -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, you can do it the hard way (without Rubygems): * tar xzf net-scp-*.tgz * cd net-scp-* * 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-scp-1.2.1/.travis.yml0000644000004100000410000000020312346240767015233 0ustar www-datawww-datalanguage: ruby rvm: - "1.9.3" - "2.0.0" - "2.1.0" install: gem install jeweler test-unit mocha net-ssh script: rake test net-scp-1.2.1/lib/0000755000004100000410000000000012346240767013675 5ustar www-datawww-datanet-scp-1.2.1/lib/net/0000755000004100000410000000000012346240767014463 5ustar www-datawww-datanet-scp-1.2.1/lib/net/scp/0000755000004100000410000000000012346240767015250 5ustar www-datawww-datanet-scp-1.2.1/lib/net/scp/errors.rb0000644000004100000410000000010212346240767017102 0ustar www-datawww-datamodule Net; class SCP class Error < RuntimeError; end end; endnet-scp-1.2.1/lib/net/scp/upload.rb0000644000004100000410000001323712346240767017067 0ustar www-datawww-datarequire 'net/scp/errors' module Net; class SCP # This module implements the state machine for uploading information to # a remote server. It exposes no public methods. See Net::SCP#upload for # a discussion of how to use Net::SCP to upload data. module Upload private # The default read chunk size, if an explicit chunk-size is not specified # by the client. DEFAULT_CHUNK_SIZE = 16384 # The start state for uploads. Simply sets up the upload scaffolding, # sets the current item to upload, and jumps to #upload_current_state. def upload_start_state(channel) if channel[:local].respond_to?(:read) channel[:options].delete(:recursive) channel[:options].delete(:preserve) end channel[:chunk_size] = channel[:options][:chunk_size] || DEFAULT_CHUNK_SIZE set_current(channel, channel[:local]) await_response(channel, :upload_current) end # Determines what the next thing to upload is, and branches. If the next # item is a file, goes to #upload_file_state. If it is a directory, goes # to #upload_directory_state. def upload_current_state(channel) if channel[:current].respond_to?(:read) upload_file_state(channel) elsif File.directory?(channel[:current]) raise Net::SCP::Error, "can't upload directories unless :recursive" unless channel[:options][:recursive] upload_directory_state(channel) elsif File.file?(channel[:current]) upload_file_state(channel) else raise Net::SCP::Error, "not a directory or a regular file: #{channel[:current].inspect}" end end # After transferring attributes (if requested), sends a 'D' directive and # awaites the server's 0-byte response. Then goes to #next_item_state. def upload_directory_state(channel) if preserve_attributes_if_requested(channel) mode = channel[:stat].mode & 07777 directive = "D%04o %d %s\n" % [mode, 0, File.basename(channel[:current])] channel.send_data(directive) channel[:cwd] = channel[:current] channel[:stack] << Dir.entries(channel[:current]).reject { |i| i == "." || i == ".." } await_response(channel, :next_item) end end # After transferring attributes (if requested), sends a 'C' directive and # awaits the server's 0-byte response. Then goes to #send_data_state. def upload_file_state(channel) if preserve_attributes_if_requested(channel) mode = channel[:stat] ? channel[:stat].mode & 07777 : channel[:options][:mode] channel[:name] = channel[:current].respond_to?(:read) ? channel[:remote] : channel[:current] directive = "C%04o %d %s\n" % [mode || 0640, channel[:size], File.basename(channel[:name])] channel.send_data(directive) channel[:io] = channel[:current].respond_to?(:read) ? channel[:current] : File.open(channel[:current], "rb") channel[:sent] = 0 progress_callback(channel, channel[:name], channel[:sent], channel[:size]) await_response(channel, :send_data) end end # If any data remains to be transferred from the current file, sends it. # Otherwise, sends a 0-byte and transfers to #next_item_state. def send_data_state(channel) data = channel[:io].read(channel[:chunk_size]) if data.nil? channel[:io].close unless channel[:local].respond_to?(:read) channel.send_data("\0") await_response(channel, :next_item) else channel[:sent] += data.length progress_callback(channel, channel[:name], channel[:sent], channel[:size]) channel.send_data(data) end end # Checks the work queue to see what needs to be done next. If there is # nothing to do, calls Net::SCP#finish_state. If we're at the end of a # directory, sends an 'E' directive and waits for the server to respond # before moving to #next_item_state. Otherwise, sets the next thing to # upload and moves to #upload_current_state. def next_item_state(channel) if channel[:stack].empty? finish_state(channel) else next_item = channel[:stack].last.shift if next_item.nil? channel[:stack].pop channel[:cwd] = File.dirname(channel[:cwd]) channel.send_data("E\n") await_response(channel, channel[:stack].empty? ? :finish : :next_item) else set_current(channel, next_item) upload_current_state(channel) end end end # Sets the given +path+ as the new current item to upload. def set_current(channel, path) path = channel[:cwd] ? File.join(channel[:cwd], path) : path channel[:current] = path if channel[:current].respond_to?(:read) channel[:stat] = channel[:current].stat if channel[:current].respond_to?(:stat) else channel[:stat] = File.stat(channel[:current]) end channel[:size] = channel[:stat] ? channel[:stat].size : channel[:current].size end # If the :preserve option is set, send a 'T' directive and wait for the # server to respond before proceeding to either #upload_file_state or # #upload_directory_state, depending on what is being uploaded. def preserve_attributes_if_requested(channel) if channel[:options][:preserve] && !channel[:preserved] channel[:preserved] = true stat = channel[:stat] directive = "T%d %d %d %d\n" % [stat.mtime.to_i, stat.mtime.usec, stat.atime.to_i, stat.atime.usec] channel.send_data(directive) type = stat.directory? ? :directory : :file await_response(channel, "upload_#{type}") return false else channel[:preserved] = false return true end end end end; endnet-scp-1.2.1/lib/net/scp/version.rb0000644000004100000410000000055712346240767017271 0ustar www-datawww-datarequire 'net/ssh/version' module Net; class SCP # Describes the current version of the Net::SCP library. class Version < Net::SSH::Version MAJOR = 1 MINOR = 2 TINY = 1 # 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-scp-1.2.1/lib/net/scp/download.rb0000644000004100000410000001435412346240767017413 0ustar www-datawww-datarequire 'net/scp/errors' module Net; class SCP # This module implements the state machine for downloading information from # a remote server. It exposes no public methods. See Net::SCP#download for # a discussion of how to use Net::SCP to download data. module Download private # This is the starting state for the download state machine. The # #start_command method puts the state machine into this state the first # time the channel is processed. This state does some basic error checking # and scaffolding and then sends a 0-byte to the remote server, indicating # readiness to proceed. Then, the state machine is placed into the # "read directive" state (see #read_directive_state). def download_start_state(channel) if channel[:local].respond_to?(:write) && channel[:options][:recursive] raise Net::SCP::Error, "cannot recursively download to an in-memory location" elsif channel[:local].respond_to?(:write) && channel[:options][:preserve] lwarn { ":preserve option is ignored when downloading to an in-memory buffer" } channel[:options].delete(:preserve) elsif channel[:options][:recursive] && !File.exists?(channel[:local]) Dir.mkdir(channel[:local]) end channel.send_data("\0") channel[:state] = :read_directive end # This state parses the next full line (up to a new-line) for the next # directive. (See the SCP protocol documentation in Net::SCP for the # possible directives). def read_directive_state(channel) return unless line = channel[:buffer].read_to("\n") channel[:buffer].consume! directive = parse_directive(line) case directive[:type] when :OK return when :warning channel[:error_string] << directive[:message] when :error channel[:error_string] << directive[:message] when :times channel[:times] = directive when :directory read_directory(channel, directive) when :file read_file(channel, directive) when :end channel[:local] = File.dirname(channel[:local]) channel[:stack].pop channel[:state] = :finish if channel[:stack].empty? end channel.send_data("\0") end # Reads data from the channel for as long as there is data remaining to # be read. As soon as there is no more data to read for the current file, # the state machine switches to #finish_read_state. def read_data_state(channel) return if channel[:buffer].empty? data = channel[:buffer].read!(channel[:remaining]) channel[:io].write(data) channel[:remaining] -= data.length progress_callback(channel, channel[:file][:name], channel[:file][:size] - channel[:remaining], channel[:file][:size]) await_response(channel, :finish_read) if channel[:remaining] <= 0 end # Finishes off the read, sets the times for the file (if any), and then # jumps to either #finish_state (for single-file downloads) or # #read_directive_state (for recursive downloads). A 0-byte is sent to the # server to indicate that the file was recieved successfully. def finish_read_state(channel) channel[:io].close unless channel[:io] == channel[:local] if channel[:options][:preserve] && channel[:file][:times] File.utime(channel[:file][:times][:atime], channel[:file][:times][:mtime], channel[:file][:name]) end channel[:file] = nil channel[:state] = channel[:stack].empty? ? :finish : :read_directive channel.send_data("\0") end # Parses the given +text+ to extract which SCP directive it contains. It # then returns a hash with at least one key, :type, which describes what # type of directive it is. The hash may also contain other, directive-specific # data. def parse_directive(text) case type = text[0] when "\x00" # Success { :type => :OK } when "\x01" { :type => :warning, :message => text[1..-1] } when "\x02" { :type => :error, :message => text[1..-1] } when ?T parts = text[1..-1].split(/ /, 4).map { |i| i.to_i } { :type => :times, :mtime => Time.at(parts[0], parts[1]), :atime => Time.at(parts[2], parts[3]) } when ?C, ?D parts = text[1..-1].split(/ /, 3) { :type => (type == ?C ? :file : :directory), :mode => parts[0].to_i(8), :size => parts[1].to_i, :name => parts[2].chomp } when ?E { :type => :end } else raise ArgumentError, "unknown directive: #{text.inspect}" end end # Sets the new directory as the current directory, creates the directory # if it does not exist, and then falls back into #read_directive_state. def read_directory(channel, directive) if !channel[:options][:recursive] raise Net::SCP::Error, ":recursive not specified for directory download" end channel[:local] = File.join(channel[:local], directive[:name]) if File.exists?(channel[:local]) && !File.directory?(channel[:local]) raise "#{channel[:local]} already exists and is not a directory" elsif !File.exists?(channel[:local]) Dir.mkdir(channel[:local], directive[:mode] | 0700) end if channel[:options][:preserve] && channel[:times] File.utime(channel[:times][:atime], channel[:times][:mtime], channel[:local]) end channel[:stack] << directive channel[:times] = nil end # Opens the given file locally, and switches to #read_data_state to do the # actual read. def read_file(channel, directive) if !channel[:local].respond_to?(:write) directive[:name] = (channel[:options][:recursive] || File.directory?(channel[:local])) ? File.join(channel[:local], directive[:name]) : channel[:local] end channel[:file] = directive.merge(:times => channel[:times]) channel[:io] = channel[:local].respond_to?(:write) ? channel[:local] : File.new(directive[:name], "wb", directive[:mode] | 0600) channel[:times] = nil channel[:remaining] = channel[:file][:size] channel[:state] = :read_data progress_callback(channel, channel[:file][:name], 0, channel[:file][:size]) end end end; endnet-scp-1.2.1/lib/net/scp.rb0000644000004100000410000004602212346240767015601 0ustar www-datawww-datarequire 'stringio' require 'shellwords' require 'net/ssh' require 'net/scp/errors' require 'net/scp/upload' require 'net/scp/download' module Net # Net::SCP implements the SCP (Secure CoPy) client protocol, allowing Ruby # programs to securely and programmatically transfer individual files or # entire directory trees to and from remote servers. It provides support for # multiple simultaneous SCP copies working in parallel over the same # connection, as well as for synchronous, serial copies. # # Basic usage: # # require 'net/scp' # # Net::SCP.start("remote.host", "username", :password => "passwd") do |scp| # # synchronous (blocking) upload; call blocks until upload completes # scp.upload! "/local/path", "/remote/path" # # # asynchronous upload; call returns immediately and requires SSH # # event loop to run # channel = scp.upload("/local/path", "/remote/path") # channel.wait # end # # Net::SCP also provides an open-uri tie-in, so you can use the Kernel#open # method to open and read a remote file: # # # if you just want to parse SCP URL's: # require 'uri/scp' # url = URI.parse("scp://user@remote.host/path/to/file") # # # if you want to read from a URL voa SCP: # require 'uri/open-scp' # puts open("scp://user@remote.host/path/to/file").read # # Lastly, Net::SCP adds a method to the Net::SSH::Connection::Session class, # allowing you to easily grab a Net::SCP reference from an existing Net::SSH # session: # # require 'net/ssh' # require 'net/scp' # # Net::SSH.start("remote.host", "username", :password => "passwd") do |ssh| # ssh.scp.download! "/remote/path", "/local/path" # end # # == Progress Reporting # # By default, uploading and downloading proceed silently, without any # outward indication of their progress. For long running uploads or downloads # (and especially in interactive environments) it is desirable to report # to the user the progress of the current operation. # # To receive progress reports for the current operation, just pass a block # to #upload or #download (or one of their variants): # # scp.upload!("/path/to/local", "/path/to/remote") do |ch, name, sent, total| # puts "#{name}: #{sent}/#{total}" # end # # Whenever a new chunk of data is recieved for or sent to a file, the callback # will be invoked, indicating the name of the file (local for downloads, # remote for uploads), the number of bytes that have been sent or received # so far for the file, and the size of the file. # #-- # = Protocol Description # # Although this information has zero relevance to consumers of the Net::SCP # library, I'm documenting it here so that anyone else looking for documentation # of the SCP protocol won't be left high-and-dry like I was. The following is # reversed engineered from the OpenSSH SCP implementation, and so may # contain errors. You have been warned! # # The first step is to invoke the "scp" command on the server. It accepts # the following parameters, which must be set correctly to avoid errors: # # * "-t" -- tells the remote scp process that data will be sent "to" it, # e.g., that data will be uploaded and it should initialize itself # accordingly. # * "-f" -- tells the remote scp process that data should come "from" it, # e.g., that data will be downloaded and it should initialize itself # accordingly. # * "-v" -- verbose mode; the remote scp process should chatter about what # it is doing via stderr. # * "-p" -- preserve timestamps. 'T' directives (see below) should be/will # be sent to indicate the modification and access times of each file. # * "-r" -- recursive transfers should be allowed. Without this, it is an # error to upload or download a directory. # # After those flags, the name of the remote file/directory should be passed # as the sole non-switch argument to scp. # # Then the fun begins. If you're doing a download, enter the download_start_state. # Otherwise, look for upload_start_state. # # == Net::SCP::Download#download_start_state # # This is the start state for downloads. It simply sends a 0-byte to the # server. The next state is Net::SCP::Download#read_directive_state. # # == Net::SCP::Upload#upload_start_state # # Sets up the initial upload scaffolding and waits for a 0-byte from the # server, and then switches to Net::SCP::Upload#upload_current_state. # # == Net::SCP::Download#read_directive_state # # Reads a directive line from the input. The following directives are # recognized: # # * T%d %d %d %d -- a "times" packet. Indicates that the next file to be # downloaded must have mtime/usec/atime/usec attributes preserved. # * D%o %d %s -- a directory change. The process is changing to a directory # with the given permissions/size/name, and the recipient should create # a directory with the same name and permissions. Subsequent files and # directories will be children of this directory, until a matching 'E' # directive. # * C%o %d %s -- a file is being sent next. The file will have the given # permissions/size/name. Immediately following this line, +size+ bytes # will be sent, raw. # * E -- terminator directive. Indicates the end of a directory, and subsequent # files and directories should be received by the parent of the current # directory. # * \0 -- indicates a successful response from the other end. # * \1 -- warning directive. Indicates a warning from the other end. Text from # this warning will be reported if the SCP results in an error. # * \2 -- error directive. Indicates an error from the other end. Text from # this error will be reported if the SCP results in an error. # # If a 'C' directive is received, we switch over to # Net::SCP::Download#read_data_state. If an 'E' directive is received, and # there is no parent directory, we switch over to Net::SCP#finish_state. # # Regardless of what the next state is, we send a 0-byte to the server # before moving to the next state. # # == Net::SCP::Download#read_data_state # # Bytes are read to satisfy the size of the incoming file. When all pending # data has been read, we wait for the server to send a 0-byte, and then we # switch to the Net::SCP::Download#finish_read_state. # # == Net::SCP::Download#finish_read_state # # We sent a 0-byte to the server to indicate that the file was successfully # received. If there is no parent directory, then we're downloading a single # file and we switch to Net::SCP#finish_state. Otherwise we jump back to the # Net::SCP::Download#read_directive state to see what we get to download next. # # == Net::SCP::Upload#upload_current_state # # If the current item is a file, send a file. Sending a file starts with a # 'T' directive (if :preserve is true), then a wait for the server to respond, # and then a 'C' directive, and then a wait for the server to respond, and # then a jump to Net::SCP::Upload#send_data_state. # # If current item is a directory, send a 'D' directive, and wait for the # server to respond with a 0-byte. Then jump to Net::SCP::Upload#next_item_state. # # == Net::SCP::Upload#send_data_state # # Reads and sends the next chunk of data to the server. The state machine # remains in this state until all data has been sent, at which point we # send a 0-byte to the server, and wait for the server to respond with a # 0-byte of its own. Then we jump back to Net::SCP::Upload#next_item_state. # # == Net::SCP::Upload#next_item_state # # If there is nothing left to upload, and there is no parent directory, # jump to Net::SCP#finish_state. # # If there is nothing left to upload from the current directory, send an # 'E' directive and wait for the server to respond with a 0-byte. Then go # to Net::SCP::Upload#next_item_state. # # Otherwise, set the current upload source and go to # Net::SCP::Upload#upload_current_state. # # == Net::SCP#finish_state # # Tells the server that no more data is forthcoming from this end of the # pipe (via Net::SSH::Connection::Channel#eof!) and leaves the pipe to drain. # It will be terminated when the remote process closes with an exit status # of zero. #++ class SCP include Net::SSH::Loggable include Upload, Download # Starts up a new SSH connection and instantiates a new SCP session on # top of it. If a block is given, the SCP session is yielded, and the # SSH session is closed automatically when the block terminates. If no # block is given, the SCP session is returned. def self.start(host, username, options={}) session = Net::SSH.start(host, username, options) scp = new(session) if block_given? begin yield scp session.loop ensure session.close end else return scp end end # Starts up a new SSH connection using the +host+ and +username+ parameters, # instantiates a new SCP session on top of it, and then begins an # upload from +local+ to +remote+. If the +options+ hash includes an # :ssh key, the value for that will be passed to the SSH connection as # options (e.g., to set the password, etc.). All other options are passed # to the #upload! method. If a block is given, it will be used to report # progress (see "Progress Reporting", under Net::SCP). def self.upload!(host, username, local, remote, options={}, &progress) options = options.dup start(host, username, options.delete(:ssh) || {}) do |scp| scp.upload!(local, remote, options, &progress) end end # Starts up a new SSH connection using the +host+ and +username+ parameters, # instantiates a new SCP session on top of it, and then begins a # download from +remote+ to +local+. If the +options+ hash includes an # :ssh key, the value for that will be passed to the SSH connection as # options (e.g., to set the password, etc.). All other options are passed # to the #download! method. If a block is given, it will be used to report # progress (see "Progress Reporting", under Net::SCP). def self.download!(host, username, remote, local=nil, options={}, &progress) options = options.dup start(host, username, options.delete(:ssh) || {}) do |scp| return scp.download!(remote, local, options, &progress) end end # The underlying Net::SSH session that acts as transport for the SCP # packets. attr_reader :session # Creates a new Net::SCP session on top of the given Net::SSH +session+ # object. def initialize(session) @session = session self.logger = session.logger end # Inititiate a synchronous (non-blocking) upload from +local+ to +remote+. # The following options are recognized: # # * :recursive - the +local+ parameter refers to a local directory, which # should be uploaded to a new directory named +remote+ on the remote # server. # * :preserve - the atime and mtime of the file should be preserved. # * :verbose - the process should result in verbose output on the server # end (useful for debugging). # * :chunk_size - the size of each "chunk" that should be sent. Defaults # to 2048. Changing this value may improve throughput at the expense # of decreasing interactivity. # # This method will return immediately, returning the Net::SSH::Connection::Channel # object that will support the upload. To wait for the upload to finish, # you can either call the #wait method on the channel, or otherwise run # the Net::SSH event loop until the channel's #active? method returns false. # # channel = scp.upload("/local/path", "/remote/path") # channel.wait def upload(local, remote, options={}, &progress) start_command(:upload, local, remote, options, &progress) end # Same as #upload, but blocks until the upload finishes. Identical to # calling #upload and then calling the #wait method on the channel object # that is returned. The return value is not defined. def upload!(local, remote, options={}, &progress) upload(local, remote, options, &progress).wait end # Inititiate a synchronous (non-blocking) download from +remote+ to +local+. # The following options are recognized: # # * :recursive - the +remote+ parameter refers to a remote directory, which # should be downloaded to a new directory named +local+ on the local # machine. # * :preserve - the atime and mtime of the file should be preserved. # * :verbose - the process should result in verbose output on the server # end (useful for debugging). # # This method will return immediately, returning the Net::SSH::Connection::Channel # object that will support the download. To wait for the download to finish, # you can either call the #wait method on the channel, or otherwise run # the Net::SSH event loop until the channel's #active? method returns false. # # channel = scp.download("/remote/path", "/local/path") # channel.wait def download(remote, local, options={}, &progress) start_command(:download, local, remote, options, &progress) end # Same as #download, but blocks until the download finishes. Identical to # calling #download and then calling the #wait method on the channel # object that is returned. # # scp.download!("/remote/path", "/local/path") # # If +local+ is nil, and the download is not recursive (e.g., it is downloading # only a single file), the file will be downloaded to an in-memory buffer # and the resulting string returned. # # data = download!("/remote/path") def download!(remote, local=nil, options={}, &progress) destination = local ? local : StringIO.new download(remote, destination, options, &progress).wait local ? true : destination.string end private # Constructs the scp command line needed to initiate and SCP session # for the given +mode+ (:upload or :download) and with the given options # (:verbose, :recursive, :preserve). Returns the command-line as a # string, ready to execute. def scp_command(mode, options) command = "scp " command << (mode == :upload ? "-t" : "-f") command << " -v" if options[:verbose] command << " -r" if options[:recursive] command << " -p" if options[:preserve] command end # Opens a new SSH channel and executes the necessary SCP command over # it (see #scp_command). It then sets up the necessary callbacks, and # sets up a state machine to use to process the upload or download. # (See Net::SCP::Upload and Net::SCP::Download). def start_command(mode, local, remote, options={}, &callback) session.open_channel do |channel| if options[:shell] escaped_file = shellescape(remote).gsub(/'/) { |m| "'\\''" } command = "#{options[:shell]} -c '#{scp_command(mode, options)} #{escaped_file}'" else command = "#{scp_command(mode, options)} #{shellescape remote}" end channel.exec(command) do |ch, success| if success channel[:local ] = local channel[:remote ] = remote channel[:options ] = options.dup channel[:callback] = callback channel[:buffer ] = Net::SSH::Buffer.new channel[:state ] = "#{mode}_start" channel[:stack ] = [] channel[:error_string] = '' channel.on_close { |ch| send("#{channel[:state]}_state", channel); raise Net::SCP::Error, "SCP did not finish successfully (#{channel[:exit]}): #{channel[:error_string]}" if channel[:exit] != 0 } channel.on_data { |ch, data| channel[:buffer].append(data) } channel.on_extended_data { |ch, type, data| debug { data.chomp } } channel.on_request("exit-status") { |ch, data| channel[:exit] = data.read_long } channel.on_process { send("#{channel[:state]}_state", channel) } else channel.close raise Net::SCP::Error, "could not exec scp on the remote host" end end end end # Causes the state machine to enter the "await response" state, where # things just pause until the server replies with a 0 (see # #await_response_state), at which point the state machine will pick up # at +next_state+ and continue processing. def await_response(channel, next_state) channel[:state] = :await_response channel[:next ] = next_state.to_sym # check right away, to see if the response is immediately available await_response_state(channel) end # The action invoked while the state machine remains in the "await # response" state. As long as there is no data ready to process, the # machine will remain in this state. As soon as the server replies with # an integer 0 as the only byte, the state machine is kicked into the # next state (see +await_response+). If the response is not a 0, an # exception is raised. def await_response_state(channel) return if channel[:buffer].available == 0 c = channel[:buffer].read_byte raise "#{c.chr}#{channel[:buffer].read}" if c != 0 channel[:next], channel[:state] = nil, channel[:next] send("#{channel[:state]}_state", channel) end # The action invoked when the state machine is in the "finish" state. # It just tells the server not to expect any more data from this end # of the pipe, and allows the pipe to drain until the server closes it. def finish_state(channel) channel.eof! end # Invoked to report progress back to the client. If a callback was not # set, this does nothing. def progress_callback(channel, name, sent, total) channel[:callback].call(channel, name, sent, total) if channel[:callback] end # Imported from ruby 1.9.2 shellwords.rb def shellescape(path) # Convert path to a string if it isn't already one. str = path.to_s # ruby 1.8.7+ implements String#shellescape return str.shellescape if str.respond_to? :shellescape # An empty argument will be skipped, so return empty quotes. return "''" if str.empty? str = str.dup # Process as a single byte sequence because not all shell # implementations are multibyte aware. str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1") # A LF cannot be escaped with a backslash because a backslash + LF # combo is regarded as line continuation and simply ignored. str.gsub!(/\n/, "'\n'") return str end end end class Net::SSH::Connection::Session # Provides a convenient way to initialize a SCP session given a Net::SSH # session. Returns the Net::SCP instance, ready to use. def scp @scp ||= Net::SCP.new(self) end end net-scp-1.2.1/lib/uri/0000755000004100000410000000000012346240767014474 5ustar www-datawww-datanet-scp-1.2.1/lib/uri/open-scp.rb0000644000004100000410000000064612346240767016553 0ustar www-datawww-datarequire 'open-uri' require 'uri/scp' require 'net/scp' OpenURI::Options[:ssh] = nil module URI class SCP def buffer_open(buf, proxy, open_options) options = open_options.merge(:port => port, :password => password) progress = options.delete(:progress_proc) buf << Net::SCP.download!(host, user, path, nil, options, &progress) buf.io.rewind end include OpenURI::OpenRead end end net-scp-1.2.1/lib/uri/scp.rb0000644000004100000410000000136312346240767015611 0ustar www-datawww-datarequire 'uri/generic' module URI class SCP < Generic DEFAULT_PORT = 22 COMPONENT = [ :scheme, :userinfo, :host, :port, :path, :query ].freeze attr_reader :options def self.new2(user, password, host, port, path, query) new('scp', [user, password], host, port, nil, path, nil, query) end def initialize(*args) super(*args) @options = Hash.new (query || "").split(/&/).each do |pair| name, value = pair.split(/=/, 2) opt_name = name.to_sym values = value.split(/,/).map { |v| v.to_i.to_s == v ? v.to_i : v } values = values.first if values.length == 1 options[opt_name] = values end end end @@schemes['SCP'] = SCP endnet-scp-1.2.1/metadata.yml0000644000004100000410000001027212346240767015434 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: net-scp version: !ruby/object:Gem::Version version: 1.2.1 prerelease: platform: ruby authors: - Jamis Buck - Delano Mandelbaum autorequire: bindir: bin cert_chain: - !binary |- LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUROakNDQWg2Z0F3SUJB Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREJCTVE4d0RRWURWUVFEREFaa1pX eGgKYm04eEdUQVhCZ29Ka2lhSmsvSXNaQUVaRmdsemIyeDFkR2x2ZFhNeEV6 QVJCZ29Ka2lhSmsvSXNaQUVaRmdOagpiMjB3SGhjTk1UUXdORE13TVRjek5q STJXaGNOTVRVd05ETXdNVGN6TmpJMldqQkJNUTh3RFFZRFZRUUREQVprClpX eGhibTh4R1RBWEJnb0praWFKay9Jc1pBRVpGZ2x6YjJ4MWRHbHZkWE14RXpB UkJnb0praWFKay9Jc1pBRVoKRmdOamIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFF QkFRVUFBNElCRHdBd2dnRUtBb0lCQVFEZGJlRmpNNjcrVHhycQorOEhhRDR3 S0VpYWNSb0I4cHMxN1Z6dDlUQlV5b01NajVLVHRGaXB0ci8rWlR1Zy9ZZFlC cXVNcHJQc0tsWU0yCk5vRzZCenZEY3ZRSzF6cmRIbnlWb3NJREtBSGsyd255 Ti9wc1ppa1MxYm85blVIQ1M1aEpkUEVuUVp4L014VFoKK0dqdVJzaUJ4UFlC WG5xT2JMaFI4M0xCZVdzYXVXVHVvNWdKdDFEWXhEVFZyTG9CK1orY2VNVisz dmgwSEJDYgppd2Vna3g5R1dHNDVoNXdUa3NVSXB6T01CM1ZzSEd0R21CakN2 ZENnTEoySDZiOFU5cm1MN2NodW5qZHFmTmYzCnpQdG5IMzJjL3pyRnplV0pU eUgyczhJYSszRDZ2dW0yeGpqbjhGbkxnM1Y0ek9mNXg1OThHRkJKWURRdjd6 WC8KclY5ZUN6SERBZ01CQUFHak9UQTNNQWtHQTFVZEV3UUNNQUF3SFFZRFZS ME9CQllFRkErQnVjOHlTRXcycUtucQpWSDR3aDhLQW02aFVNQXNHQTFVZER3 UUVBd0lFc0RBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQVlzTTBNNDJoClpF VVh2ci9pMThna3dIS0xGREtEQWNpbWdDb1M1K3N5RzZya3VTT25LR1FvbHlL VE5jek5NNGdXSkpQSDVhVnEKbVcyQnRxcElvbTRZUlliOW0zZkROTnM2a2NC NURlZFk5VVBoVnZKOFhUVEIyWUx4THFsN1VKaWQ5Wk9pcVd6QwpPVG0wNnc3 emtBVC9sZHQ0NnA2QnF5VXk2UFc0UU1nMEJxN1NNZlJVUlZycDJsdmhRdkJk QzdvRFI5Q0dFQlpDCi9DaSsrWkVoL1FSOXF5MTFBSGNpRUlYbk5reXRpZHla dExyNE1XaHRiVjQ2OHk2c2hwUFlkS1UvdUNJTlNndnQKRnBNQU01Tml0OEw4 bkh3ZjNJSVVQZzdsc01DUnpPa1EvRkQ4N0JJM1YzU25GTm9UQ2RHZ25HajNq Zlc0elJsTAppRnlhcmVGUEE4NGJ0UT09Ci0tLS0tRU5EIENFUlRJRklDQVRF LS0tLS0K date: 2014-04-30 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 SCP client protocol email: net-ssh@solutious.com executables: [] extensions: [] extra_rdoc_files: - LICENSE.txt - README.rdoc files: - .travis.yml - CHANGES.txt - LICENSE.txt - Manifest - README.rdoc - Rakefile - gem-public_cert.pem - lib/net/scp.rb - lib/net/scp/download.rb - lib/net/scp/errors.rb - lib/net/scp/upload.rb - lib/net/scp/version.rb - lib/uri/open-scp.rb - lib/uri/scp.rb - net-scp.gemspec - setup.rb - test/common.rb - test/test_all.rb - test/test_download.rb - test/test_scp.rb - test/test_upload.rb homepage: https://github.com/net-ssh/net-scp 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-scp rubygems_version: 1.8.23 signing_key: specification_version: 3 summary: A pure Ruby implementation of the SCP client protocol test_files: [] net-scp-1.2.1/test/0000755000004100000410000000000012346240767014106 5ustar www-datawww-datanet-scp-1.2.1/test/test_download.rb0000644000004100000410000001416212346240767017305 0ustar www-datawww-datarequire 'common' class TestDownload < Net::SCP::TestCase def test_download_file_should_transfer_file file = prepare_file("/path/to/local.txt", "a" * 1234) expect_scp_session "-f /path/to/remote.txt" do |channel| simple_download(channel) end assert_scripted { scp.download!("/path/to/remote.txt", "/path/to/local.txt") } assert_equal "a" * 1234, file.io.string end def test_download_file_with_spaces_in_name_should_escape_remote_file_name file = prepare_file("/path/to/local file.txt", "") expect_scp_session "-f /path/to/remote\\ file.txt" do |channel| channel.sends_ok channel.gets_data "C0666 0 local file.txt\n" channel.sends_ok channel.gets_ok channel.sends_ok end assert_scripted { scp.download!("/path/to/remote file.txt", "/path/to/local file.txt") } end def test_download_file_with_metacharacters_in_name_should_escape_remote_file_name file = prepare_file("/path/to/local/#{awful_file_name}", "") expect_scp_session "-f /path/to/remote/#{escaped_file_name}" do |channel| channel.sends_ok channel.gets_data "C0666 0 #{awful_file_name}\n" channel.sends_ok channel.gets_ok channel.sends_ok end assert_scripted { scp.download!("/path/to/remote/#{awful_file_name}", "/path/to/local/#{awful_file_name}") } end def test_download_with_preserve_should_send_times file = prepare_file("/path/to/local.txt", "a" * 1234, 0644, Time.at(1234567890, 123456), Time.at(12121212, 232323)) expect_scp_session "-f -p /path/to/remote.txt" do |channel| channel.sends_ok channel.gets_data "T1234567890 123456 12121212 232323\n" simple_download(channel, 0644) end File.expects(:utime).with(Time.at(12121212, 232323), Time.at(1234567890, 123456), "/path/to/local.txt") assert_scripted { scp.download!("/path/to/remote.txt", "/path/to/local.txt", :preserve => true) } assert_equal "a" * 1234, file.io.string end def test_download_with_error_should_respond_with_error_text story do |session| channel = session.opens_channel channel.sends_exec "scp -f /path/to/remote.txt" channel.sends_ok channel.gets_data "\x01File not found: /path/to/remote.txt\n" channel.sends_ok channel.gets_eof channel.gets_exit_status(1) channel.gets_close channel.sends_close end error = nil assert_scripted do begin scp.download!("/path/to/remote.txt") rescue error = $! end end assert_equal Net::SCP::Error, error.class assert_equal "SCP did not finish successfully (1): File not found: /path/to/remote.txt\n", error.message end def test_download_with_progress_callback_should_invoke_callback prepare_file("/path/to/local.txt", "a" * 3000 + "b" * 3000 + "c" * 3000 + "d" * 3000) expect_scp_session "-f /path/to/remote.txt" do |channel| channel.sends_ok channel.gets_data "C0666 12000 remote.txt\n" channel.sends_ok channel.gets_data "a" * 3000 channel.inject_remote_delay! channel.gets_data "b" * 3000 channel.inject_remote_delay! channel.gets_data "c" * 3000 channel.inject_remote_delay! channel.gets_data "d" * 3000 channel.gets_ok channel.sends_ok end calls = [] progress = Proc.new { |ch, *args| calls << args } assert_scripted do scp.download!("/path/to/remote.txt", "/path/to/local.txt", &progress) end assert_equal ["/path/to/local.txt", 0, 12000], calls.shift assert_equal ["/path/to/local.txt", 3000, 12000], calls.shift assert_equal ["/path/to/local.txt", 6000, 12000], calls.shift assert_equal ["/path/to/local.txt", 9000, 12000], calls.shift assert_equal ["/path/to/local.txt", 12000, 12000], calls.shift assert calls.empty? end def test_download_io_with_recursive_should_raise_error expect_scp_session "-f -r /path/to/remote.txt" assert_raises(Net::SCP::Error) { scp.download!("/path/to/remote.txt", StringIO.new, :recursive => true) } end def test_download_io_with_preserve_should_ignore_preserve expect_scp_session "-f -p /path/to/remote.txt" do |channel| simple_download(channel) end io = StringIO.new assert_scripted { scp.download!("/path/to/remote.txt", io, :preserve => true) } assert_equal "a" * 1234, io.string end def test_download_io_should_transfer_data expect_scp_session "-f /path/to/remote.txt" do |channel| simple_download(channel) end io = StringIO.new assert_scripted { scp.download!("/path/to/remote.txt", io) } assert_equal "a" * 1234, io.string end def test_download_bang_without_target_should_return_string expect_scp_session "-f /path/to/remote.txt" do |channel| simple_download(channel) end assert_scripted do assert_equal "a" * 1234, scp.download!("/path/to/remote.txt") end end def test_download_directory_without_recursive_should_raise_error expect_scp_session "-f /path/to/remote" do |channel| channel.sends_ok channel.gets_data "D0755 0 remote\n" end assert_raises(Net::SCP::Error) { scp.download!("/path/to/remote") } end def test_download_directory_should_create_directory_and_files_locally file = nil prepare_directory "/path/to/local" do |dir| dir.directory "remote" do |dir2| dir2.directory "sub" do |dir3| file = dir3.file "remote.txt", "" end end end expect_scp_session "-f -r /path/to/remote" do |channel| channel.sends_ok channel.gets_data "D0755 0 remote\n" channel.sends_ok channel.gets_data "D0755 0 sub\n" simple_download(channel) channel.gets_data "E\n" channel.sends_ok channel.gets_data "E\n" channel.sends_ok end scp.download!("/path/to/remote", "/path/to/local", :recursive => true, :ssh => { :verbose => :debug }) assert_equal "a" * 1234, file.io.string end private def simple_download(channel, mode=0666) channel.sends_ok channel.gets_data "C%04o 1234 remote.txt\n" % mode channel.sends_ok channel.gets_data "a" * 1234 channel.gets_ok channel.sends_ok end end net-scp-1.2.1/test/test_upload.rb0000644000004100000410000002133312346240767016760 0ustar www-datawww-datarequire 'common' class TestUpload < Net::SCP::TestCase def test_upload_file_should_transfer_file prepare_file("/path/to/local.txt", "a" * 1234) expect_scp_session "-t /path/to/remote.txt" do |channel| channel.gets_ok channel.sends_data "C0666 1234 local.txt\n" channel.gets_ok channel.sends_data "a" * 1234 channel.sends_ok channel.gets_ok end assert_scripted { scp.upload!("/path/to/local.txt", "/path/to/remote.txt") } end def test_upload_file_with_spaces_in_name_should_escape_remote_file_name prepare_file("/path/to/local file.txt", "") expect_scp_session "-t /path/to/remote\\ file.txt" do |channel| channel.gets_ok channel.sends_data "C0666 0 local file.txt\n" channel.gets_ok channel.sends_ok channel.gets_ok end assert_scripted { scp.upload!("/path/to/local file.txt", "/path/to/remote file.txt") } end def test_upload_file_with_metacharacters_in_name_should_escape_remote_file_name prepare_file("/path/to/local/#{awful_file_name}", "") expect_scp_session "-t /path/to/remote/#{escaped_file_name}" do |channel| channel.gets_ok channel.sends_data "C0666 0 #{awful_file_name}\n" channel.gets_ok channel.sends_ok channel.gets_ok end assert_scripted { scp.upload!("/path/to/local/#{awful_file_name}", "/path/to/remote/#{awful_file_name}") } end def test_upload_file_with_preserve_should_send_times prepare_file("/path/to/local.txt", "a" * 1234, 0666, Time.at(1234567890, 123456), Time.at(1234543210, 345678)) expect_scp_session "-t -p /path/to/remote.txt" do |channel| channel.gets_ok channel.sends_data "T1234567890 123456 1234543210 345678\n" channel.gets_ok channel.sends_data "C0666 1234 local.txt\n" channel.gets_ok channel.sends_data "a" * 1234 channel.sends_ok channel.gets_ok end assert_scripted { scp.upload!("/path/to/local.txt", "/path/to/remote.txt", :preserve => true) } end def test_upload_file_with_progress_callback_should_invoke_callback prepare_file("/path/to/local.txt", "a" * 3000 + "b" * 3000 + "c" * 3000 + "d" * 3000) expect_scp_session "-t /path/to/remote.txt" do |channel| channel.gets_ok channel.sends_data "C0666 12000 local.txt\n" channel.gets_ok channel.sends_data "a" * 3000 channel.sends_data "b" * 3000 channel.sends_data "c" * 3000 channel.sends_data "d" * 3000 channel.sends_ok channel.gets_ok end calls = [] progress = Proc.new do |ch, name, sent, total| calls << [name, sent, total] end assert_scripted do scp.upload!("/path/to/local.txt", "/path/to/remote.txt", :chunk_size => 3000, &progress) end assert_equal ["/path/to/local.txt", 0, 12000], calls.shift assert_equal ["/path/to/local.txt", 3000, 12000], calls.shift assert_equal ["/path/to/local.txt", 6000, 12000], calls.shift assert_equal ["/path/to/local.txt", 9000, 12000], calls.shift assert_equal ["/path/to/local.txt", 12000, 12000], calls.shift assert calls.empty? end def test_upload_io_with_recursive_should_ignore_recursive expect_scp_session "-t -r /path/to/remote.txt" do |channel| channel.gets_ok channel.sends_data "C0640 1234 remote.txt\n" channel.gets_ok channel.sends_data "a" * 1234 channel.sends_ok channel.gets_ok end io = StringIO.new("a" * 1234) assert_scripted { scp.upload!(io, "/path/to/remote.txt", :recursive => true) } end def test_upload_io_with_preserve_should_ignore_preserve expect_scp_session "-t -p /path/to/remote.txt" do |channel| channel.gets_ok channel.sends_data "C0640 1234 remote.txt\n" channel.gets_ok channel.sends_data "a" * 1234 channel.sends_ok channel.gets_ok end io = StringIO.new("a" * 1234) assert_scripted { scp.upload!(io, "/path/to/remote.txt", :preserve => true) } end def test_upload_io_should_transfer_data expect_scp_session "-t /path/to/remote.txt" do |channel| channel.gets_ok channel.sends_data "C0640 1234 remote.txt\n" channel.gets_ok channel.sends_data "a" * 1234 channel.sends_ok channel.gets_ok end io = StringIO.new("a" * 1234) assert_scripted { scp.upload!(io, "/path/to/remote.txt") } end def test_upload_io_with_mode_should_honor_mode_as_permissions expect_scp_session "-t /path/to/remote.txt" do |channel| channel.gets_ok channel.sends_data "C0666 1234 remote.txt\n" channel.gets_ok channel.sends_data "a" * 1234 channel.sends_ok channel.gets_ok end io = StringIO.new("a" * 1234) assert_scripted { scp.upload!(io, "/path/to/remote.txt", :mode => 0666) } end def test_upload_directory_without_recursive_should_error prepare_directory("/path/to/local") expect_scp_session("-t /path/to/remote") do |channel| channel.gets_ok end assert_raises(Net::SCP::Error) { scp.upload!("/path/to/local", "/path/to/remote") } end def test_upload_empty_directory_should_create_directory_and_finish prepare_directory("/path/to/local") expect_scp_session("-t -r /path/to/remote") do |channel| channel.gets_ok channel.sends_data "D0777 0 local\n" channel.gets_ok channel.sends_data "E\n" channel.gets_ok end assert_scripted { scp.upload!("/path/to/local", "/path/to/remote", :recursive => true) } end def test_upload_directory_should_recursively_create_and_upload_items prepare_directory("/path/to/local") do |d| d.file "hello.txt", "hello world\n" d.directory "others" do |d2| d2.file "data.dat", "abcdefghijklmnopqrstuvwxyz" end d.file "zoo.doc", "going to the zoo\n" end expect_scp_session("-t -r /path/to/remote") do |channel| channel.gets_ok channel.sends_data "D0777 0 local\n" channel.gets_ok channel.sends_data "C0666 12 hello.txt\n" channel.gets_ok channel.sends_data "hello world\n" channel.sends_ok channel.gets_ok channel.sends_data "D0777 0 others\n" channel.gets_ok channel.sends_data "C0666 26 data.dat\n" channel.gets_ok channel.sends_data "abcdefghijklmnopqrstuvwxyz" channel.sends_ok channel.gets_ok channel.sends_data "E\n" channel.gets_ok channel.sends_data "C0666 17 zoo.doc\n" channel.gets_ok channel.sends_data "going to the zoo\n" channel.sends_ok channel.gets_ok channel.sends_data "E\n" channel.gets_ok end assert_scripted { scp.upload!("/path/to/local", "/path/to/remote", :recursive => true) } end def test_upload_directory_with_preserve_should_send_times_for_all_items prepare_directory("/path/to/local", 0755, Time.at(17171717, 191919), Time.at(18181818, 101010)) do |d| d.file "hello.txt", "hello world\n", 0640, Time.at(12345, 67890), Time.at(234567, 890) d.directory "others", 0770, Time.at(112233, 4455), Time.at(22334455, 667788) do |d2| d2.file "data.dat", "abcdefghijklmnopqrstuvwxyz", 0600, Time.at(13579135, 13131), Time.at(7654321, 654321) end d.file "zoo.doc", "going to the zoo\n", 0444, Time.at(12121212, 131313), Time.at(23232323, 242424) end expect_scp_session("-t -r -p /path/to/remote") do |channel| channel.gets_ok channel.sends_data "T17171717 191919 18181818 101010\n" channel.gets_ok channel.sends_data "D0755 0 local\n" channel.gets_ok channel.sends_data "T12345 67890 234567 890\n" channel.gets_ok channel.sends_data "C0640 12 hello.txt\n" channel.gets_ok channel.sends_data "hello world\n" channel.sends_ok channel.gets_ok channel.sends_data "T112233 4455 22334455 667788\n" channel.gets_ok channel.sends_data "D0770 0 others\n" channel.gets_ok channel.sends_data "T13579135 13131 7654321 654321\n" channel.gets_ok channel.sends_data "C0600 26 data.dat\n" channel.gets_ok channel.sends_data "abcdefghijklmnopqrstuvwxyz" channel.sends_ok channel.gets_ok channel.sends_data "E\n" channel.gets_ok channel.sends_data "T12121212 131313 23232323 242424\n" channel.gets_ok channel.sends_data "C0444 17 zoo.doc\n" channel.gets_ok channel.sends_data "going to the zoo\n" channel.sends_ok channel.gets_ok channel.sends_data "E\n" channel.gets_ok end assert_scripted { scp.upload!("/path/to/local", "/path/to/remote", :preserve => true, :recursive => true) } end def test_upload_should_not_block prepare_file("/path/to/local.txt", "data") story { |s| s.opens_channel(false) } assert_scripted { scp.upload("/path/to/local.txt", "/path/to/remote.txt") } end end net-scp-1.2.1/test/test_scp.rb0000644000004100000410000000354512346240767016266 0ustar www-datawww-datarequire 'common' class TestSCP < Net::SCP::TestCase def test_start_without_block_should_return_scp_instance ssh = stub('session', :logger => nil) Net::SSH.expects(:start). with("remote.host", "username", :password => "foo"). returns(ssh) ssh.expects(:close).never scp = Net::SCP.start("remote.host", "username", :password => "foo") assert_instance_of Net::SCP, scp assert_equal ssh, scp.session end def test_start_with_block_should_yield_scp_and_close_ssh_session ssh = stub('session', :logger => nil) Net::SSH.expects(:start). with("remote.host", "username", :password => "foo"). returns(ssh) ssh.expects(:loop) ssh.expects(:close) yielded = false Net::SCP.start("remote.host", "username", :password => "foo") do |scp| yielded = true assert_instance_of Net::SCP, scp assert_equal ssh, scp.session end assert yielded end def test_self_upload_should_instatiate_scp_and_invoke_synchronous_upload scp = stub('scp') scp.expects(:upload!).with("/path/to/local", "/path/to/remote", :recursive => true) Net::SCP.expects(:start). with("remote.host", "username", :password => "foo"). yields(scp) Net::SCP.upload!("remote.host", "username", "/path/to/local", "/path/to/remote", :ssh => { :password => "foo" }, :recursive => true) end def test_self_download_should_instatiate_scp_and_invoke_synchronous_download scp = stub('scp') scp.expects(:download!).with("/path/to/remote", "/path/to/local", :recursive => true).returns(:result) Net::SCP.expects(:start). with("remote.host", "username", :password => "foo"). yields(scp) result = Net::SCP.download!("remote.host", "username", "/path/to/remote", "/path/to/local", :ssh => { :password => "foo" }, :recursive => true) assert_equal :result, result end end net-scp-1.2.1/test/common.rb0000644000004100000410000001076412346240767015733 0ustar www-datawww-datarequire 'test/unit' require 'mocha/setup' begin gem 'net-ssh', ">= 2.0.0" require 'net/ssh' rescue LoadError $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../net-ssh/lib" begin require 'net/ssh' require 'net/ssh/version' raise LoadError, "wrong version" unless Net::SSH::Version::STRING >= '1.99.0' rescue LoadError => e abort "could not load net/ssh v2 (#{e.inspect})" end end $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" require 'net/scp' require 'net/ssh/test' class Net::SSH::Test::Channel def gets_ok gets_data "\0" end def sends_ok sends_data "\0" end end class Net::SCP::TestCase < Test::Unit::TestCase include Net::SSH::Test def default_test # do nothing, this is just a hacky-hack to work around Test::Unit's # insistence that all TestCase subclasses have at least one test # method defined. end protected def prepare_file(path, contents="", mode=0666, mtime=Time.now, atime=Time.now) entry = FileEntry.new(path, contents, mode, mtime, atime) entry.stub! entry end def prepare_directory(path, mode=0777, mtime=Time.now, atime=Time.now) directory = DirectoryEntry.new(path, mode, mtime, atime) yield directory if block_given? directory.stub! end # The POSIX spec unfortunately allows all characters in file names except # ASCII 0x00(NUL) and 0x2F(/) # # Ideally, we should be testing filenames with newlines, but Mocha doesn't # like this at all, so we leave them out. However, the Shellwords module # handles newlines just fine, so we can be reasonably confident that they # will work in practice def awful_file_name (((0x00..0x7f).to_a - [0x00, 0x0a, 0x2f]).map { |n| n.chr }).join + '.txt' end def escaped_file_name "\\\001\\\002\\\003\\\004\\\005\\\006\\\a\\\b\\\t\\\v\\\f\\\r\\\016\\\017\\\020\\\021\\\022\\\023\\\024\\\025\\\026\\\027\\\030\\\031\\\032\\\e\\\034\\\035\\\036\\\037\\ \\!\\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+,-.0123456789:\\;\\<\\=\\>\\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[\\\\\\]\\^_\\`abcdefghijklmnopqrstuvwxyz\\{\\|\\}\\~\\\177.txt" end class FileEntry attr_reader :path, :contents, :mode, :mtime, :atime, :io def initialize(path, contents, mode=0666, mtime=Time.now, atime=Time.now) @path, @contents, @mode = path, contents, mode @mtime, @atime = mtime, atime end def name @name ||= File.basename(path) end def stub! stat = Mocha::Mock.new("file::stat") stat.stubs(:size => contents.length, :mode => mode, :mtime => mtime, :atime => atime, :directory? => false) File.stubs(:stat).with(path).returns(stat) File.stubs(:directory?).with(path).returns(false) File.stubs(:file?).with(path).returns(true) File.stubs(:open).with(path, "rb").returns(StringIO.new(contents)) @io = StringIO.new File.stubs(:new).with(path, "wb", mode).returns(io) end end class DirectoryEntry attr_reader :path, :mode, :mtime, :atime attr_reader :entries def initialize(path, mode=0777, mtime=Time.now, atime=Time.now) @path, @mode = path, mode @mtime, @atime = mtime, atime @entries = [] end def name @name ||= File.basename(path) end def file(name, *args) (entries << FileEntry.new(File.join(path, name), *args)).last end def directory(name, *args) entry = DirectoryEntry.new(File.join(path, name), *args) yield entry if block_given? (entries << entry).last end def stub! Dir.stubs(:mkdir).with { |*a| a.first == path } stat = Mocha::Mock.new("file::stat") stat.stubs(:size => 1024, :mode => mode, :mtime => mtime, :atime => atime, :directory? => true) File.stubs(:stat).with(path).returns(stat) File.stubs(:directory?).with(path).returns(true) File.stubs(:file?).with(path).returns(false) Dir.stubs(:entries).with(path).returns(%w(. ..) + entries.map { |e| e.name }.sort) entries.each { |e| e.stub! } end end def expect_scp_session(arguments) story do |session| channel = session.opens_channel channel.sends_exec "scp #{arguments}" yield channel if block_given? channel.sends_eof channel.gets_exit_status channel.gets_eof channel.gets_close channel.sends_close end end def scp(options={}) @scp ||= Net::SCP.new(connection(options)) end end net-scp-1.2.1/test/test_all.rb0000644000004100000410000000013412346240767016240 0ustar www-datawww-dataDir.chdir(File.dirname(__FILE__)) do Dir['**/test_*.rb'].each { |file| require(file) } endnet-scp-1.2.1/Manifest0000644000004100000410000000043612346240767014623 0ustar www-datawww-dataCHANGELOG.rdoc lib/net/scp/download.rb lib/net/scp/errors.rb lib/net/scp/upload.rb lib/net/scp/version.rb lib/net/scp.rb lib/uri/open-scp.rb lib/uri/scp.rb Rakefile README.rdoc setup.rb test/common.rb test/test_all.rb test/test_download.rb test/test_scp.rb test/test_upload.rb Manifest net-scp-1.2.1/gem-public_cert.pem0000644000004100000410000000223012346240767016670 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMQ8wDQYDVQQDDAZkZWxh bm8xGTAXBgoJkiaJk/IsZAEZFglzb2x1dGlvdXMxEzARBgoJkiaJk/IsZAEZFgNj b20wHhcNMTQwNDMwMTczNjI2WhcNMTUwNDMwMTczNjI2WjBBMQ8wDQYDVQQDDAZk ZWxhbm8xGTAXBgoJkiaJk/IsZAEZFglzb2x1dGlvdXMxEzARBgoJkiaJk/IsZAEZ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdbeFjM67+Txrq +8HaD4wKEiacRoB8ps17Vzt9TBUyoMMj5KTtFiptr/+ZTug/YdYBquMprPsKlYM2 NoG6BzvDcvQK1zrdHnyVosIDKAHk2wnyN/psZikS1bo9nUHCS5hJdPEnQZx/MxTZ +GjuRsiBxPYBXnqObLhR83LBeWsauWTuo5gJt1DYxDTVrLoB+Z+ceMV+3vh0HBCb iwegkx9GWG45h5wTksUIpzOMB3VsHGtGmBjCvdCgLJ2H6b8U9rmL7chunjdqfNf3 zPtnH32c/zrFzeWJTyH2s8Ia+3D6vum2xjjn8FnLg3V4zOf5x598GFBJYDQv7zX/ rV9eCzHDAgMBAAGjOTA3MAkGA1UdEwQCMAAwHQYDVR0OBBYEFA+Buc8ySEw2qKnq VH4wh8KAm6hUMAsGA1UdDwQEAwIEsDANBgkqhkiG9w0BAQUFAAOCAQEAYsM0M42h ZEUXvr/i18gkwHKLFDKDAcimgCoS5+syG6rkuSOnKGQolyKTNczNM4gWJJPH5aVq mW2BtqpIom4YRYb9m3fDNNs6kcB5DedY9UPhVvJ8XTTB2YLxLql7UJid9ZOiqWzC OTm06w7zkAT/ldt46p6BqyUy6PW4QMg0Bq7SMfRURVrp2lvhQvBdC7oDR9CGEBZC /Ci++ZEh/QR9qy11AHciEIXnNkytidyZtLr4MWhtbV468y6shpPYdKU/uCINSgvt FpMAM5Nit8L8nHwf3IIUPg7lsMCRzOkQ/FD87BI3V3SnFNoTCdGgnGj3jfW4zRlL iFyareFPA84btQ== -----END CERTIFICATE----- net-scp-1.2.1/setup.rb0000644000004100000410000006723712346240767014633 0ustar www-datawww-data# # setup.rb # # Copyright (c) 2000-2004 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU Lesser General Public License version 2.1. # # # For backward compatibility # unless Enumerable.method_defined?(:map) module Enumerable alias map collect end end unless Enumerable.method_defined?(:detect) module Enumerable alias detect find end end unless Enumerable.method_defined?(:select) module Enumerable alias select find_all end end unless Enumerable.method_defined?(:reject) module Enumerable def reject result = [] each do |i| result.push i unless yield(i) end result end end end unless Enumerable.method_defined?(:inject) module Enumerable def inject(result) each do |i| result = yield(result, i) end result end end end unless Enumerable.method_defined?(:any?) module Enumerable def any? each do |i| return true if yield(i) end false end end end unless File.respond_to?(:read) def File.read(fname) open(fname) {|f| return f.read } end end # # Application independent utilities # def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted windows stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end # # Config # if arg = ARGV.detect{|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) require arg.split(/=/, 2)[1] $".push 'rbconfig.rb' else require 'rbconfig' end def multipackage_install? FileTest.directory?(File.dirname($0) + '/packages') end class ConfigTable c = ::Config::CONFIG rubypath = c['bindir'] + '/' + c['ruby_install_name'] major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 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-scp-1.2.1/net-scp.gemspec0000644000004100000410000000401012346240767016040 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-scp" s.version = "1.2.1" 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 = "2014-04-30" s.description = "A pure Ruby implementation of the SCP client protocol" s.email = "net-ssh@solutious.com" s.extra_rdoc_files = [ "LICENSE.txt", "README.rdoc" ] s.files = [ ".travis.yml", "CHANGES.txt", "LICENSE.txt", "Manifest", "README.rdoc", "Rakefile", "gem-public_cert.pem", "lib/net/scp.rb", "lib/net/scp/download.rb", "lib/net/scp/errors.rb", "lib/net/scp/upload.rb", "lib/net/scp/version.rb", "lib/uri/open-scp.rb", "lib/uri/scp.rb", "net-scp.gemspec", "setup.rb", "test/common.rb", "test/test_all.rb", "test/test_download.rb", "test/test_scp.rb", "test/test_upload.rb" ] s.homepage = "https://github.com/net-ssh/net-scp" s.licenses = ["MIT"] s.require_paths = ["lib"] s.rubyforge_project = "net-scp" s.rubygems_version = "1.8.23" s.signing_key = "/mnt/gem/gem-private_key.pem" s.summary = "A pure Ruby implementation of the SCP 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-scp-1.2.1/metadata.gz.sig0000644000004100000410000000040012346240767016024 0ustar www-datawww-data)_ @nƾM9&4=H15qɬIy?az]Z)8Z[lU*j!)q;8ZN4 Qr |rM%kL"rBOl tצ@ֈDdVFl4rw~m3_ZO:TOYݘxV4nJUM,!g *3Rw*2z$4=yҞi @9?]BEI