def net_scp_transfer!(direction, recursive, *files)
      
      unless [:upload, :download].member?(direction.to_sym)
        raise "Must be one of: upload, download" 
      end
      
      if @rye_current_working_directory
        debug "CWD (#{@rye_current_working_directory})"
      end
      
      files = [files].flatten.compact || []

      # We allow a single file to be downloaded into a StringIO object
      # but only when no target has been specified. 
      if direction == :download 
        if files.size == 1
          debug "Created StringIO for download"
          target = StringIO.new
        else
          target = files.pop   # The last path is the download target.
        end
        
      elsif direction == :upload
#        p :UPLOAD, @rye_templates
        raise "Cannot upload to a StringIO object" if target.is_a?(StringIO)
        if files.size == 1
          target = self.getenv['HOME'] || guess_user_home
          debug "Assuming upload to #{target}"
        else
          target = files.pop
        end
        
        # Expand fileglobs (e.g. path/*.rb becomes [path/1.rb, path/2.rb]).
        # This should happen after checking files.size to determine the target
        unless @rye_safe
          files.collect! { |file| 
            file.is_a?(StringIO) ? file : Dir.glob(File.expand_path(file)) 
          }
          files.flatten! 
        end
      end
              
      # Fail early. We check whether the StringIO object is available to read
      files.each do |file|
        if file.is_a?(StringIO)
          raise "Cannot download a StringIO object" if direction == :download
          raise "StringIO object not opened for reading" if file.closed_read?
          # If a StringIO object is at end of file, SCP will hang. (TODO: SCP)
          file.rewind if file.eof?
        end
      end
      
      debug "FILES: " << files.join(', ')
      
      # Make sure the target directory exists. We can do this only when
      # there's more than one file because "target" could be a file name
      if files.size > 1 && !target.is_a?(StringIO)
        debug "CREATING TARGET DIRECTORY: #{target}"
        self.mkdir(:p, target) unless self.file_exists?(target)
      end
      
      Net::SCP.start(@rye_host, @rye_user, @rye_opts || {}) do |scp|
        transfers = []
        prev = ""
        files.each do |file|
          debug file.to_s
          prev = ""
          line = nil
          transfers << scp.send(direction, file, target, :recursive => recursive)  do |ch, n, s, t|
            line = "%-50s %6d/%-6d bytes" % [n, s, t]
            spaces = (prev.size > line.size) ? ' '*(prev.size - line.size) : ''
            pinfo "[%s] %s %s %s" % [direction, line, spaces, s == t ? "\n" : "\r"]   # update line: "file: sent/total"
            @rye_info.flush if @rye_info        # make sure every line is printed
            prev = line
          end
        end
        transfers.each { |t| t.wait }   # Run file transfers in parallel
      end
      
      target.is_a?(StringIO) ? target : nil
    end
def net_scp_transfer!(direction, recursive, *files)
      
      unless [:upload, :download].member?(direction.to_sym)
        raise "Must be one of: upload, download" 
      end
      
      if @rye_current_working_directory
        debug "CWD (#{@rye_current_working_directory})"
      end
      
      files = [files].flatten.compact || []

      # We allow a single file to be downloaded into a StringIO object
      # but only when no target has been specified. 
      if direction == :download 
        if files.size == 1
          debug "Created StringIO for download"
          target = StringIO.new
        else
          target = files.pop   # The last path is the download target.
        end
        
      elsif direction == :upload
#        p :UPLOAD, @rye_templates
        raise "Cannot upload to a StringIO object" if target.is_a?(StringIO)
        if files.size == 1
          target = self.getenv['HOME'] || guess_user_home
          debug "Assuming upload to #{target}"
        else
          target = files.pop
        end
        
        # Expand fileglobs (e.g. path/*.rb becomes [path/1.rb, path/2.rb]).
        # This should happen after checking files.size to determine the target
        unless @rye_safe
          files.collect! { |file| 
            file.is_a?(StringIO) ? file : Dir.glob(File.expand_path(file)) 
          }
          files.flatten! 
        end
      end
              
      # Fail early. We check whether the StringIO object is available to read
      files.each do |file|
        if file.is_a?(StringIO)
          raise "Cannot download a StringIO object" if direction == :download
          raise "StringIO object not opened for reading" if file.closed_read?
          # If a StringIO object is at end of file, SCP will hang. (TODO: SCP)
          file.rewind if file.eof?
        end
      end
      
      debug "FILES: " << files.join(', ')
      
      # Make sure the target directory exists. We can do this only when
      # there's more than one file because "target" could be a file name
      if files.size > 1 && !target.is_a?(StringIO)
        debug "CREATING TARGET DIRECTORY: #{target}"
        self.mkdir(:p, target) unless self.file_exists?(target)
      end
      
      Net::SCP.start(@rye_host, @rye_user, @rye_opts || {}) do |scp|
        transfers = []
        prev = ""
        files.each do |file|
          debug file.to_s
          prev = ""
          line = nil
          transfers << scp.send(direction, file, target, :recursive => recursive)  do |ch, n, s, t|
            line = "%-50s %6d/%-6d bytes" % [n, s, t]
            spaces = (prev.size > line.size) ? ' '*(prev.size - line.size) : ''
            pinfo "[%s] %s %s %s" % [direction, line, spaces, s == t ? "\n" : "\r"]   # update line: "file: sent/total"
            @rye_info.flush if @rye_info        # make sure every line is printed
            prev = line
          end
        end
        transfers.each { |t| t.wait }   # Run file transfers in parallel
      end
      
      target.is_a?(StringIO) ? target : nil
    end
* +direction+ is one of :upload, :download
 * +recursive+ should be true for directories and false for files.
 * +files+ is an Array of file paths, the content is direction specific.
 For downloads, +files+ is a list of files to download. The last element
 must be the local directory to download to. If downloading a single file
 the last element can be a file path. The target can also be a StringIO.
 For uploads, +files+ is a list of files to upload. The last element is
 the directory to upload to. If uploading a single file, the last element
 can be a file path. The list of files can also include StringIO objects.
 For both uploads and downloads, the target directory will be created if
 it does not exist, but only when multiple files are being transferred.
 This method will fail early if there are obvious problems with the input
 parameters. An exception is raised and no files are transferred.
 Uploads always return nil. Downloads return nil or a StringIO object if
 one is specified for the target.