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.
This is the starting state for the download state machine. The # 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 #).
# File lib/net/scp/download.rb, line 17 17: def download_start_state(channel) 18: if channel[:local].respond_to?(:write) && channel[:options][:recursive] 19: raise Net::SCP::Error, "cannot recursively download to an in-memory location" 20: elsif channel[:local].respond_to?(:write) && channel[:options][:preserve] 21: lwarn { ":preserve option is ignored when downloading to an in-memory buffer" } 22: channel[:options].delete(:preserve) 23: elsif channel[:options][:recursive] && !File.exists?(channel[:local]) 24: Dir.mkdir(channel[:local]) 25: end 26: 27: channel.send_data("\00"") 28: channel[:state] = :read_directive 29: end
Finishes off the read, sets the times for the file (if any), and then jumps to either # (for single-file downloads) or # (for recursive downloads). A 0-byte is sent to the server to indicate that the file was recieved successfully.
# File lib/net/scp/download.rb, line 71 71: def finish_read_state(channel) 72: channel[:io].close unless channel[:io] == channel[:local] 73: 74: if channel[:options][:preserve] && channel[:file][:times] 75: File.utime(channel[:file][:times][:atime], 76: channel[:file][:times][:mtime], channel[:file][:name]) 77: end 78: 79: channel[:file] = nil 80: channel[:state] = channel[:stack].empty? ? :finish : :read_directive 81: channel.send_data("\00"") 82: 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.
# File lib/net/scp/download.rb, line 88 88: def parse_directive(text) 89: case type = text[0] 90: when TT 91: parts = text[1..1].split(/ /, 4).map { |i| i.to_i } 92: { :type => :times, 93: :mtime => Time.at(parts[0], parts[1]), 94: :atime => Time.at(parts[2], parts[3]) } 95: when CC, DD 96: parts = text[1..1].split(/ /, 3) 97: { :type => (type == CC ? :file : :directory), 98: :mode => parts[0].to_i(8), 99: :size => parts[1].to_i, 100: :name => parts[2].chomp } 101: when EE 102: { :type => :end } 103: else raise ArgumentError, "unknown directive: #{text.inspect}" 104: end 105: 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 #.
# File lib/net/scp/download.rb, line 58 58: def read_data_state(channel) 59: return if channel[:buffer].empty? 60: data = channel[:buffer].read!(channel[:remaining]) 61: channel[:io].write(data) 62: channel[:remaining] -= data.length 63: progress_callback(channel, channel[:file][:name], channel[:file][:size] - channel[:remaining], channel[:file][:size]) 64: await_response(channel, :finish_read) if channel[:remaining] <= 0 65: 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).
# File lib/net/scp/download.rb, line 34 34: def read_directive_state(channel) 35: return unless line = channel[:buffer].read_to("\n") 36: channel[:buffer].consume! 37: 38: directive = parse_directive(line) 39: case directive[:type] 40: when :times then 41: channel[:times] = directive 42: when :directory 43: read_directory(channel, directive) 44: when :file 45: read_file(channel, directive) 46: when :end 47: channel[:local] = File.dirname(channel[:local]) 48: channel[:stack].pop 49: channel[:state] = :finish if channel[:stack].empty? 50: end 51: 52: channel.send_data("\00"") 53: end
Sets the new directory as the current directory, creates the directory if it does not exist, and then falls back into #.
# File lib/net/scp/download.rb, line 109 109: def read_directory(channel, directive) 110: if !channel[:options][:recursive] 111: raise Net::SCP::Error, ":recursive not specified for directory download" 112: end 113: 114: channel[:local] = File.join(channel[:local], directive[:name]) 115: 116: if File.exists?(channel[:local]) && !File.directory?(channel[:local]) 117: raise "#{channel[:local]} already exists and is not a directory" 118: elsif !File.exists?(channel[:local]) 119: Dir.mkdir(channel[:local], directive[:mode] | 0700) 120: end 121: 122: if channel[:options][:preserve] && channel[:times] 123: File.utime(channel[:times][:atime], channel[:times][:mtime], channel[:local]) 124: end 125: 126: channel[:stack] << directive 127: channel[:times] = nil 128: end
Opens the given file locally, and switches to # to do the actual read.
# File lib/net/scp/download.rb, line 132 132: def read_file(channel, directive) 133: if !channel[:local].respond_to?(:write) 134: directive[:name] = (channel[:options][:recursive] || File.directory?(channel[:local])) ? 135: File.join(channel[:local], directive[:name]) : 136: channel[:local] 137: end 138: 139: channel[:file] = directive.merge(:times => channel[:times]) 140: channel[:io] = channel[:local].respond_to?(:write) ? channel[:local] : 141: File.new(directive[:name], "wb", directive[:mode] | 0600) 142: channel[:times] = nil 143: channel[:remaining] = channel[:file][:size] 144: channel[:state] = :read_data 145: 146: progress_callback(channel, channel[:file][:name], 0, channel[:file][:size]) 147: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.