delete: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/ironruby-tags/core/io;C807294 delete: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/ironruby-tags/core/env/element_set_tags.txt;C807294 File: element_set_tags.txt =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/ironruby-tags/core/env/element_set_tags.txt;C807294 (server) 12/30/2009 2:31 PM +++ [no target file] @@ -1,1 +1,0 @@ -fails:ENV.[]= sets the environment variable to the given value =================================================================== delete: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/ironruby-tags/core/env/store_tags.txt;C807294 File: store_tags.txt =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/ironruby-tags/core/env/store_tags.txt;C807294 (server) 12/30/2009 2:31 PM +++ [no target file] @@ -1,1 +1,0 @@ -fails:ENV.store sets the environment variable to the given value =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/cache/net-scp-1.0.2.gem net-scp-1.0.2.gem: files differ add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/cache/net-ssh-2.0.13.gem net-ssh-2.0.13.gem: files differ add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/cache/tzinfo-0.3.15.gem tzinfo-0.3.15.gem: files differ add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/cache/win32-api-1.4.5.gem win32-api-1.4.5.gem: files differ add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2 add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13 add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5 add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/CHANGELOG.rdoc File: CHANGELOG.rdoc =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/CHANGELOG.rdoc;tzinfo2 @@ -1,0 +1,18 @@ +=== 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! =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/Manifest File: Manifest =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/Manifest;tzinfo2 @@ -1,0 +1,17 @@ +CHANGELOG.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 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/net-scp.gemspec File: net-scp.gemspec =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/net-scp.gemspec;tzinfo2 @@ -1,0 +1,36 @@ +Gem::Specification.new do |s| + s.name = %q{net-scp} + s.version = "1.0.2" + + s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version= + s.authors = ["Jamis Buck"] + s.date = %q{2009-02-04} + s.description = %q{A pure Ruby implementation of the SCP client protocol} + s.email = %q{jamis@jamisbuck.org} + s.extra_rdoc_files = ["CHANGELOG.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", "README.rdoc"] + s.files = ["CHANGELOG.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.gemspec"] + s.has_rdoc = true + s.homepage = %q{http://net-ssh.rubyforge.org/scp} + s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Net-scp", "--main", "README.rdoc"] + s.require_paths = ["lib"] + s.rubyforge_project = %q{net-ssh} + s.rubygems_version = %q{1.2.0} + s.summary = %q{A pure Ruby implementation of the SCP client protocol} + s.test_files = ["test/test_all.rb"] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 2 + + if current_version >= 3 then + s.add_runtime_dependency(%q, [">= 1.99.1"]) + s.add_development_dependency(%q, [">= 0"]) + else + s.add_dependency(%q, [">= 1.99.1"]) + s.add_dependency(%q, [">= 0"]) + end + else + s.add_dependency(%q, [">= 1.99.1"]) + s.add_dependency(%q, [">= 0"]) + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/Rakefile File: Rakefile =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/Rakefile;tzinfo2 @@ -1,0 +1,30 @@ +$LOAD_PATH.unshift "../net-ssh/lib" +require './lib/net/scp/version' + +begin + require 'echoe' +rescue LoadError + abort "You'll need to have `echoe' installed to use Net::SCP's Rakefile" +end + +version = Net::SCP::Version::STRING.dup +if ENV['SNAPSHOT'].to_i == 1 + version << "." << Time.now.utc.strftime("%Y%m%d%H%M%S") +end + +Echoe.new('net-scp', version) do |p| + p.project = "net-ssh" + p.changelog = "CHANGELOG.rdoc" + + p.author = "Jamis Buck" + p.email = "jamis@jamisbuck.org" + p.summary = "A pure Ruby implementation of the SCP client protocol" + p.url = "http://net-ssh.rubyforge.org/scp" + + p.dependencies = ["net-ssh >=1.99.1"] + + p.need_zip = true + p.include_rakefile = true + + p.rdoc_pattern = /^(lib|README.rdoc|CHANGELOG.rdoc)/ +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/README.rdoc File: README.rdoc =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/README.rdoc;tzinfo2 @@ -1,0 +1,98 @@ += Net::SCP + +* http://net-ssh.rubyforge.org/scp + +== 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", + :password => "password") + + # download a file from a remote server + Net::SCP.download!("remote.host.com", "username", + "/remote/path", "/local/path", + :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", :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) + +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. =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/setup.rb File: setup.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/setup.rb;tzinfo2 @@ -1,0 +1,1331 @@ +# +# 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 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/uri add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp.rb File: scp.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp.rb;tzinfo2 @@ -1,0 +1,414 @@ +require 'stringio' + +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 + # outword 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. + # + # 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| + command = "#{scp_command(mode, options)} #{sanitize_file_name(remote)}" + 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.on_close { |ch| raise Net::SCP::Error, "SCP did not finish successfully (#{ch[:exit]})" if ch[: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 + + def sanitize_file_name(file_name) + file_name.gsub(/[ ]/) { |m| "\\#{m}" } + 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 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/download.rb File: download.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/download.rb;tzinfo2 @@ -1,0 +1,150 @@ +require '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 :times then + 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 ?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; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/errors.rb File: errors.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/errors.rb;tzinfo2 @@ -1,0 +1,5 @@ +module Net; class SCP + + class Error < RuntimeError; end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/upload.rb File: upload.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/upload.rb;tzinfo2 @@ -1,0 +1,142 @@ +require '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 = 2048 + + # 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; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/version.rb File: version.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/net/scp/version.rb;tzinfo2 @@ -1,0 +1,18 @@ +require 'net/ssh/version' + +module Net; class SCP + + # Describes the current version of the Net::SCP library. + class Version < Net::SSH::Version + MAJOR = 1 + MINOR = 0 + TINY = 2 + + # The current version, as a Version instance + CURRENT = new(MAJOR, MINOR, TINY) + + # The current version, as a String instance + STRING = CURRENT.to_s + end + +end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/uri/open-scp.rb File: open-scp.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/uri/open-scp.rb;tzinfo2 @@ -1,0 +1,18 @@ +require 'open-uri' +require 'uri/scp' +require 'net/scp' + +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, open_options, &progress) + buf.io.rewind + end + + include OpenURI::OpenRead + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/uri/scp.rb File: scp.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/lib/uri/scp.rb;tzinfo2 @@ -1,0 +1,35 @@ +require '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 +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/common.rb File: common.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/common.rb;tzinfo2 @@ -1,0 +1,138 @@ +require 'test/unit' +require 'mocha' + +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 + + 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 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_all.rb File: test_all.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_all.rb;tzinfo2 @@ -1,0 +1,3 @@ +Dir.chdir(File.dirname(__FILE__)) do + Dir['**/test_*.rb'].each { |file| require(file) } +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_download.rb File: test_download.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_download.rb;tzinfo2 @@ -1,0 +1,156 @@ +require '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_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_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 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_scp.rb File: test_scp.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_scp.rb;tzinfo2 @@ -1,0 +1,60 @@ +require '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 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_upload.rb File: test_upload.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-scp-1.0.2/test/test_upload.rb;tzinfo2 @@ -1,0 +1,255 @@ +require '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_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 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/CHANGELOG.rdoc File: CHANGELOG.rdoc =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/CHANGELOG.rdoc;tzinfo2 @@ -1,0 +1,149 @@ + + +=== 2.0.13 / 17 Aug 2009 + +* Added fix for hanging in ServerVersion#negotiate! when using SOCKS5 proxy (GH-9) [Gerald Talton] + +* Added support for specifying a list of hosts in .ssh/config, with tests (GH-6) [ckoehler, Delano Mandelbaum] + +* Added tests for arcfour128/256/512 lengths, encryption, and decryption [Delano Mandelbaum] + +* Skip packet stream tests for arcfour128/256/512 [Delano Mandelbaum] + +* Fix for OpenSSL cipher key length because it always returns 16, even when 32 byte keys are required, e.g. for arcfour256 and arcfour512 ciphers [Karl Varga] + + +=== 2.0.12 / 08 Jun 2009 + +* Applied patch for arcfour128 and arcfour256 support [Denis Bernard] + +* Use unbuffered reads when negotiating the protocol version [Steven Hazel] + + +=== 2.0.11 / 24 Feb 2009 + +* Add :key_data option for specifying raw private keys in PEM format [Alex Holems, Andrew Babkin] + + +=== 2.0.10 / 4 Feb 2009 + +* Added Net::SSH.configuration_for to make it easier to query the SSH configuration file(s) [Jamis Buck] + + +=== 2.0.9 / 1 Feb 2009 + +* Specifying non-nil user argument overrides user in .ssh/config [Jamis Buck] + +* Ignore requests for non-existent channels (workaround ssh server bug) [Jamis Buck] + +* Add terminate! method for hard shutdown scenarios [Jamis Buck] + +* Revert to pre-2.0.7 key-loading behavior by default, but load private-key if public-key doesn't exist [Jamis Buck] + +* Make sure :passphrase option gets passed to key manager [Bob Cotton] + + +=== 2.0.8 / 29 December 2008 + +* Fix private key change from 2.0.7 so that keys are loaded just-in-time, avoiding unecessary prompts from encrypted keys. [Jamis Buck] + + +=== 2.0.7 / 29 December 2008 + +* Make key manager use private keys instead of requiring public key to exist [arilerner@mac.com] + +* Fix failing tests [arilerner@mac.com] + +* Don't include pageant when running under JRuby [Angel N. Sciortino] + + +=== 2.0.6 / 6 December 2008 + +* Update the Manifest file so that the gem includes all necessary files [Jamis Buck] + + +=== 2.0.5 / 6 December 2008 + +* Make the Pageant interface comply with more of the Socket interface to avoid related errors [Jamis Buck] + +* Don't busy-wait on session close for remaining channels to close [Will Bryant] + +* Ruby 1.9 compatibility [Jamis Buck] + +* Fix Cipher#final to correctly flag a need for a cipher reset [Jamis Buck] + + +=== 2.0.4 / 27 Aug 2008 + +* Added Connection::Session#closed? and Transport::Session#closed? [Jamis Buck] + +* Numeric host names in .ssh/config are now parsed correct [Yanko Ivanov] + +* Make sure the error raised when a public key file is malformed is more informative than a MethodMissing error [Jamis Buck] + +* Cipher#reset is now called after Cipher#final, with the last n bytes used as the next initialization vector [Jamis Buck] + + +=== 2.0.3 / 27 Jun 2008 + +* Make Net::SSH::Version comparable [Brian Candler] + +* Fix errors in port forwarding when a channel could not be opened due to a typo in the exception name [Matthew Todd] + +* Use #chomp instead of #strip when cleaning the version string reported by the remote host, so that trailing whitespace is preserved (this is to play nice with servers like Mocana SSH) [Timo Gatsonides] + +* Correctly parse ssh_config entries with eq-sign delimiters [Jamis Buck] + +* Ignore malformed ssh_config entries [Jamis Buck] + +=== 2.0.2 / 29 May 2008 + +* Make sure the agent client understands both RSA "identities answers" [Jamis Buck] + +* Fixed key truncation bug that caused hmacs other than SHA1 to fail with "corrupt hmac" errors [Jamis Buck] + +* Fix detection and loading of public keys when the keys don't actually exist [David Dollar] + + +=== 2.0.1 / 5 May 2008 + +* Teach Net::SSH about a handful of default key names [Jamis Buck] + + +=== 2.0.0 / 1 May 2008 + +* Allow the :verbose argument to accept symbols (:debug, etc.) as well as Logger level constants (Logger::DEBUG, etc.) [Jamis Buck] + + +=== 2.0 Preview Release 4 (1.99.3) / 19 Apr 2008 + +* Make sure HOME is set to something sane, even on OS's that don't set it by default [Jamis Buck] + +* Add a :passphrase option to specify the passphrase to use with private keys [Francis Sullivan] + +* Open a new auth agent connection for every auth-agent channel request [Jamis Buck] + + +=== 2.0 Preview Release 3 (1.99.2) / 10 Apr 2008 + +* Session properties [Jamis Buck] + +* Make channel open failure work with a callback so that failures can be handled similarly to successes [Jamis Buck] + + +=== 2.0 Preview Release 2 (1.99.1) / 22 Mar 2008 + +* Partial support for ~/.ssh/config (and related) SSH configuration files [Daniel J. Berger, Jamis Buck] + +* Added Net::SSH::Test to facilitate testing complex SSH state machines [Jamis Buck] + +* Reworked Net::SSH::Prompt to use conditionally-selected modules [Jamis Buck, suggested by James Rosen] + +* Added Channel#eof? and Channel#eof! [Jamis Buck] + +* Fixed bug in strict host key verifier on cache miss [Mike Timm] + + +=== 2.0 Preview Release 1 (1.99.0) / 21 Aug 2007 + +* First preview release of Net::SSH v2 =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/Manifest File: Manifest =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/Manifest;tzinfo2 @@ -1,0 +1,107 @@ +CHANGELOG.rdoc +Manifest +README.rdoc +Rakefile +Rudyfile +THANKS.rdoc +lib/net/ssh.rb +lib/net/ssh/authentication/agent.rb +lib/net/ssh/authentication/constants.rb +lib/net/ssh/authentication/key_manager.rb +lib/net/ssh/authentication/methods/abstract.rb +lib/net/ssh/authentication/methods/hostbased.rb +lib/net/ssh/authentication/methods/keyboard_interactive.rb +lib/net/ssh/authentication/methods/password.rb +lib/net/ssh/authentication/methods/publickey.rb +lib/net/ssh/authentication/pageant.rb +lib/net/ssh/authentication/session.rb +lib/net/ssh/buffer.rb +lib/net/ssh/buffered_io.rb +lib/net/ssh/config.rb +lib/net/ssh/connection/channel.rb +lib/net/ssh/connection/constants.rb +lib/net/ssh/connection/session.rb +lib/net/ssh/connection/term.rb +lib/net/ssh/errors.rb +lib/net/ssh/key_factory.rb +lib/net/ssh/known_hosts.rb +lib/net/ssh/loggable.rb +lib/net/ssh/packet.rb +lib/net/ssh/prompt.rb +lib/net/ssh/proxy/errors.rb +lib/net/ssh/proxy/http.rb +lib/net/ssh/proxy/socks4.rb +lib/net/ssh/proxy/socks5.rb +lib/net/ssh/ruby_compat.rb +lib/net/ssh/service/forward.rb +lib/net/ssh/test.rb +lib/net/ssh/test/channel.rb +lib/net/ssh/test/extensions.rb +lib/net/ssh/test/kex.rb +lib/net/ssh/test/local_packet.rb +lib/net/ssh/test/packet.rb +lib/net/ssh/test/remote_packet.rb +lib/net/ssh/test/script.rb +lib/net/ssh/test/socket.rb +lib/net/ssh/transport/algorithms.rb +lib/net/ssh/transport/cipher_factory.rb +lib/net/ssh/transport/constants.rb +lib/net/ssh/transport/hmac.rb +lib/net/ssh/transport/hmac/abstract.rb +lib/net/ssh/transport/hmac/md5.rb +lib/net/ssh/transport/hmac/md5_96.rb +lib/net/ssh/transport/hmac/none.rb +lib/net/ssh/transport/hmac/sha1.rb +lib/net/ssh/transport/hmac/sha1_96.rb +lib/net/ssh/transport/identity_cipher.rb +lib/net/ssh/transport/kex.rb +lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +lib/net/ssh/transport/openssl.rb +lib/net/ssh/transport/packet_stream.rb +lib/net/ssh/transport/server_version.rb +lib/net/ssh/transport/session.rb +lib/net/ssh/transport/state.rb +lib/net/ssh/verifiers/lenient.rb +lib/net/ssh/verifiers/null.rb +lib/net/ssh/verifiers/strict.rb +lib/net/ssh/version.rb +net-ssh.gemspec +setup.rb +support/arcfour_check.rb +test/authentication/methods/common.rb +test/authentication/methods/test_abstract.rb +test/authentication/methods/test_hostbased.rb +test/authentication/methods/test_keyboard_interactive.rb +test/authentication/methods/test_password.rb +test/authentication/methods/test_publickey.rb +test/authentication/test_agent.rb +test/authentication/test_key_manager.rb +test/authentication/test_session.rb +test/common.rb +test/configs/eqsign +test/configs/exact_match +test/configs/multihost +test/configs/wild_cards +test/connection/test_channel.rb +test/connection/test_session.rb +test/test_all.rb +test/test_buffer.rb +test/test_buffered_io.rb +test/test_config.rb +test/test_key_factory.rb +test/transport/hmac/test_md5.rb +test/transport/hmac/test_md5_96.rb +test/transport/hmac/test_none.rb +test/transport/hmac/test_sha1.rb +test/transport/hmac/test_sha1_96.rb +test/transport/kex/test_diffie_hellman_group1_sha1.rb +test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +test/transport/test_algorithms.rb +test/transport/test_cipher_factory.rb +test/transport/test_hmac.rb +test/transport/test_identity_cipher.rb +test/transport/test_packet_stream.rb +test/transport/test_server_version.rb +test/transport/test_session.rb +test/transport/test_state.rb =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/net-ssh.gemspec File: net-ssh.gemspec =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/net-ssh.gemspec;tzinfo2 @@ -1,0 +1,131 @@ +@spec = Gem::Specification.new do |s| + s.name = "net-ssh" + s.rubyforge_project = 'net-ssh' + s.version = "2.0.13" + s.summary = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol." + s.description = s.summary + s.authors = ["Jamis Buck", "Delano Mandelbaum"] + s.email = ["net-ssh@solutious.com", "net-ssh@solutious.com"] + s.homepage = "http://rubyforge.org/projects/net-ssh/" + + s.extra_rdoc_files = %w[README.rdoc THANKS.rdoc CHANGELOG.rdoc] + s.has_rdoc = true + s.rdoc_options = ["--line-numbers", "--title", s.summary, "--main", "README.rdoc"] + s.require_paths = %w[lib] + s.rubygems_version = '1.3.2' + + s.executables = %w[] + + # = MANIFEST = + s.files = %w( + CHANGELOG.rdoc + Manifest + README.rdoc + Rakefile + Rudyfile + THANKS.rdoc + lib/net/ssh.rb + lib/net/ssh/authentication/agent.rb + lib/net/ssh/authentication/constants.rb + lib/net/ssh/authentication/key_manager.rb + lib/net/ssh/authentication/methods/abstract.rb + lib/net/ssh/authentication/methods/hostbased.rb + lib/net/ssh/authentication/methods/keyboard_interactive.rb + lib/net/ssh/authentication/methods/password.rb + lib/net/ssh/authentication/methods/publickey.rb + lib/net/ssh/authentication/pageant.rb + lib/net/ssh/authentication/session.rb + lib/net/ssh/buffer.rb + lib/net/ssh/buffered_io.rb + lib/net/ssh/config.rb + lib/net/ssh/connection/channel.rb + lib/net/ssh/connection/constants.rb + lib/net/ssh/connection/session.rb + lib/net/ssh/connection/term.rb + lib/net/ssh/errors.rb + lib/net/ssh/key_factory.rb + lib/net/ssh/known_hosts.rb + lib/net/ssh/loggable.rb + lib/net/ssh/packet.rb + lib/net/ssh/prompt.rb + lib/net/ssh/proxy/errors.rb + lib/net/ssh/proxy/http.rb + lib/net/ssh/proxy/socks4.rb + lib/net/ssh/proxy/socks5.rb + lib/net/ssh/ruby_compat.rb + lib/net/ssh/service/forward.rb + lib/net/ssh/test.rb + lib/net/ssh/test/channel.rb + lib/net/ssh/test/extensions.rb + lib/net/ssh/test/kex.rb + lib/net/ssh/test/local_packet.rb + lib/net/ssh/test/packet.rb + lib/net/ssh/test/remote_packet.rb + lib/net/ssh/test/script.rb + lib/net/ssh/test/socket.rb + lib/net/ssh/transport/algorithms.rb + lib/net/ssh/transport/cipher_factory.rb + lib/net/ssh/transport/constants.rb + lib/net/ssh/transport/hmac.rb + lib/net/ssh/transport/hmac/abstract.rb + lib/net/ssh/transport/hmac/md5.rb + lib/net/ssh/transport/hmac/md5_96.rb + lib/net/ssh/transport/hmac/none.rb + lib/net/ssh/transport/hmac/sha1.rb + lib/net/ssh/transport/hmac/sha1_96.rb + lib/net/ssh/transport/identity_cipher.rb + lib/net/ssh/transport/kex.rb + lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb + lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb + lib/net/ssh/transport/openssl.rb + lib/net/ssh/transport/packet_stream.rb + lib/net/ssh/transport/server_version.rb + lib/net/ssh/transport/session.rb + lib/net/ssh/transport/state.rb + lib/net/ssh/verifiers/lenient.rb + lib/net/ssh/verifiers/null.rb + lib/net/ssh/verifiers/strict.rb + lib/net/ssh/version.rb + net-ssh.gemspec + setup.rb + support/arcfour_check.rb + test/authentication/methods/common.rb + test/authentication/methods/test_abstract.rb + test/authentication/methods/test_hostbased.rb + test/authentication/methods/test_keyboard_interactive.rb + test/authentication/methods/test_password.rb + test/authentication/methods/test_publickey.rb + test/authentication/test_agent.rb + test/authentication/test_key_manager.rb + test/authentication/test_session.rb + test/common.rb + test/configs/eqsign + test/configs/exact_match + test/configs/multihost + test/configs/wild_cards + test/connection/test_channel.rb + test/connection/test_session.rb + test/test_all.rb + test/test_buffer.rb + test/test_buffered_io.rb + test/test_config.rb + test/test_key_factory.rb + test/transport/hmac/test_md5.rb + test/transport/hmac/test_md5_96.rb + test/transport/hmac/test_none.rb + test/transport/hmac/test_sha1.rb + test/transport/hmac/test_sha1_96.rb + test/transport/kex/test_diffie_hellman_group1_sha1.rb + test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb + test/transport/test_algorithms.rb + test/transport/test_cipher_factory.rb + test/transport/test_hmac.rb + test/transport/test_identity_cipher.rb + test/transport/test_packet_stream.rb + test/transport/test_server_version.rb + test/transport/test_session.rb + test/transport/test_state.rb + ) + + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/Rakefile File: Rakefile =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/Rakefile;tzinfo2 @@ -1,0 +1,79 @@ +require 'rubygems' +require 'rake/clean' +require 'rake/gempackagetask' +require 'hanna/rdoctask' +require 'fileutils' +include FileUtils + +task :default => :package + +# CONFIG ============================================================= + +# Change the following according to your needs +README = "README.rdoc" +CHANGES = "CHANGELOG.rdoc" +THANKS = 'THANKS.rdoc' + +# Files and directories to be deleted when you run "rake clean" +CLEAN.include [ 'pkg', '*.gem', '.config', 'doc'] + +# Virginia assumes your project and gemspec have the same name +name = 'net-ssh' +load "#{name}.gemspec" +version = @spec.version + +# That's it! The following defaults should allow you to get started +# on other things. + + +# TESTS/SPECS ========================================================= + + + +# INSTALL ============================================================= + +Rake::GemPackageTask.new(@spec) do |p| + p.need_tar = true if RUBY_PLATFORM !~ /mswin/ +end + +task :release => [ :rdoc, :package ] +task :install => [ :rdoc, :package ] do + sh %{sudo gem install pkg/#{name}-#{version}.gem} +end +task :uninstall => [ :clean ] do + sh %{sudo gem uninstall #{name}} +end + + +# RUBYFORGE RELEASE / PUBLISH TASKS ================================== + +if @spec.rubyforge_project + desc 'Publish website to rubyforge' + task 'publish:rdoc' => 'doc/index.html' do + sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/ssh/v2/api/" + end + + desc 'Public release to rubyforge' + task 'publish:gem' => [:package] do |t| + sh <<-end + rubyforge add_release -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem && + rubyforge add_file -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz + end + end +end + + + +# RUBY DOCS TASK ================================== + +Rake::RDocTask.new do |t| + t.rdoc_dir = 'doc' + t.title = @spec.summary + t.options << '--line-numbers' << '-A cattr_accessor=object' + t.options << '--charset' << 'utf-8' + t.rdoc_files.include(README) + t.rdoc_files.include(CHANGES) + t.rdoc_files.include(THANKS) + t.rdoc_files.include('lib/**/*.rb') +end + =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/README.rdoc File: README.rdoc =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/README.rdoc;tzinfo2 @@ -1,0 +1,140 @@ += Net::SSH + +* http://net-ssh.rubyforge.org/ssh + +== DESCRIPTION: + +Net::SSH is a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2. + +== FEATURES: + +* Execute processes on remote servers and capture their output +* Run multiple processes in parallel over a single SSH connection +* Support for SSH subsystems +* Forward local and remote ports via an SSH connection + +== SYNOPSIS: + +In a nutshell: + + require 'net/ssh' + + Net::SSH.start('host', 'user', :password => "password") do |ssh| + # capture all stderr and stdout output from a remote process + output = ssh.exec!("hostname") + + # capture only stdout matching a particular pattern + stdout = "" + ssh.exec!("ls -l /home/jamis") do |channel, stream, data| + stdout << data if stream == :stdout + end + puts stdout + + # run multiple processes in parallel to completion + ssh.exec "sed ..." + ssh.exec "awk ..." + ssh.exec "rm -rf ..." + ssh.loop + + # open a new channel and configure a minimal set of callbacks, then run + # the event loop until the channel finishes (closes) + channel = ssh.open_channel do |ch| + ch.exec "/usr/local/bin/ruby /path/to/file.rb" do |ch, success| + raise "could not execute command" unless success + + # "on_data" is called when the process writes something to stdout + ch.on_data do |c, data| + $STDOUT.print data + end + + # "on_extended_data" is called when the process writes something to stderr + ch.on_extended_data do |c, type, data| + $STDERR.print data + end + + ch.on_close { puts "done!" } + end + end + + channel.wait + + # forward connections on local port 1234 to port 80 of www.capify.org + ssh.forward.local(1234, "www.capify.org", 80) + ssh.loop { true } + end + +See Net::SSH for more documentation, and links to further information. + +== REQUIREMENTS: + +The only requirement you might be missing is the OpenSSL bindings for Ruby. These are built by default on most platforms, but you can verify that they're built and installed on your system by running the following command line: + + ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION' + +If that spits out something like "OpenSSL 0.9.8g 19 Oct 2007", then you're set. If you get an error, then you'll need to see about rebuilding ruby with OpenSSL support, or (if your platform supports it) installing the OpenSSL bindings separately. + +Additionally: if you are going to be having Net::SSH prompt you for things like passwords or certificate passphrases, you'll want to have either the Highline (recommended) or Termios (unix systems only) gem installed, so that the passwords don't echo in clear text. + +Lastly, if you want to run the tests or use any of the Rake tasks, you'll need: + +* Echoe (for the Rakefile) +* Mocha (for the tests) + + +== INSTALL: + +* gem install net-ssh (might need sudo privileges) + + +== ARCFOUR SUPPORT: + +from Karl Varga: + +Ruby's OpenSSL bindings always return a key length of 16 for RC4 ciphers, which means that when we try to use ARCFOUR256 or higher, Net::SSH generates keys which are consistently too short - 16 bytes as opposed to 32 bytes - resulting in the following error: + + OpenSSL::CipherError: key length too short + +My patch simply instructs Net::SSH to build keys of the the proper length, regardless of the required key length reported by OpenSSL. + +You should also be aware that your OpenSSL C libraries may also contain this bug. I've updated to 0.9.8k, but according to this thread[https://bugzilla.mindrot.org/show_bug.cgi?id=1291], the bug existed as recently as 0.9.8e! I've manually taken a look at my header files and they look ok, which is what makes me think it's a bug in the Ruby implementation. + +To see your OpenSSL version: + + $ openssl version + OpenSSL 0.9.8k 25 Mar 2009 + +After installing this gem, verify that Net::SSH is generating keys of the correct length by running the script support/arcfour_check.rb: + + $ ruby arcfour_support.rb + +which should produce the following: + + arcfour128: [16, 8] OpenSSL::Cipher::Cipher + arcfour256: [32, 8] OpenSSL::Cipher::Cipher + arcfour512: [64, 8] OpenSSL::Cipher::Cipher + + +== 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. =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/Rudyfile File: Rudyfile =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/Rudyfile;tzinfo2 @@ -1,0 +1,110 @@ +# Rudyfile +# +# This configuration is used to test installing +# and running net-ssh on a clean machine. +# +# Usage: +# +# $ rudy -vv startup +# $ rudy -vv testsuite +# $ rudy -vv shutdown +# +# Requires: Rudy 0.9 (http://code.google.com/p/rudy/) +# + +defaults do + color true + environment :test + role :netssh +end + +machines do + region :'us-east-1' do + ami 'ami-e348af8a' # Alestic Debian 5.0, 32-bit (US) + end + env :test do + role :netssh do + user :root + end + end +end + +commands do + allow :apt_get, "apt-get", :y, :q + allow :gem_install, "/usr/bin/gem", "install", :n, '/usr/bin', :y, :V, "--no-rdoc", "--no-ri" + allow :gem_sources, "/usr/bin/gem", "sources" + allow :gem_uninstall, "/usr/bin/gem", "uninstall", :V + allow :update_rubygems + allow :rm +end + +routines do + + testsuite do + before :sysupdate, :installdeps, :install_gem + + remote :root do + directory_upload 'test', '/tmp/' + cd '/tmp' + ruby :I, 'lib/', :I, 'test/', :r, 'rubygems', 'test/test_all.rb' + end + + after :install_rubyforge, :install_github + end + + install_rubyforge do + remote :root do + gem_install 'net-ssh', '--version', '2.0.7' + gem_install 'net-ssh' + end + end + + install_github do + remote :root do + gem_sources :a, "http://gems.github.com" + gem_install 'net-ssh-net-ssh' + end + end + + install_gem do + before :package_gem + remote :root do + disable_safe_mode + file_upload "pkg/net-ssh-*.gem", "/tmp/" + gem_install "/tmp/net-ssh-*.gem" + end + end + + package_gem do + local do + rm :r, :f, 'pkg' + rake 'package' + end + end + + remove do + remote :root do + gem_uninstall 'net-ssh' + end + end + + installdeps do + remote :root do + gem_install "rye", "test-unit", "mocha" + rye 'authorize-local' + end + end + + sysupdate do + remote :root do + apt_get "update" + apt_get "install", "build-essential", "git-core" + apt_get "install", "ruby1.8-dev", "rdoc", "libzlib-ruby", "rubygems" + mkdir :p, "/var/lib/gems/1.8/bin" # Doesn't get created, but causes Rubygems to fail + gem_install "builder", "session" + gem_install 'rubygems-update', "-v=1.3.4" # circular issue with 1.3.5 and hoe + update_rubygems + end + end +end + =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/setup.rb File: setup.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/setup.rb;tzinfo2 @@ -1,0 +1,1585 @@ +# +# setup.rb +# +# Copyright (c) 2000-2005 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU LGPL, Lesser General Public License version 2.1. +# + +unless Enumerable.method_defined?(:map) # Ruby 1.4.6 + module Enumerable + alias map collect + end +end + +unless File.respond_to?(:read) # Ruby 1.6 + def File.read(fname) + open(fname) {|f| + return f.read + } + end +end + +unless Errno.const_defined?(:ENOTEMPTY) # Windows? + module Errno + class ENOTEMPTY + # We do not raise this exception, implementation is not needed. + end + end +end + +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 + + +class ConfigTable + + include Enumerable + + def initialize(rbconfig) + @rbconfig = rbconfig + @items = [] + @table = {} + # options + @install_prefix = nil + @config_opt = nil + @verbose = true + @no_harm = false + end + + attr_accessor :install_prefix + attr_accessor :config_opt + + attr_writer :verbose + + def verbose? + @verbose + end + + attr_writer :no_harm + + def no_harm? + @no_harm + end + + def [](key) + lookup(key).resolve(self) + end + + def []=(key, val) + lookup(key).set val + end + + def names + @items.map {|i| i.name } + end + + def each(&block) + @items.each(&block) + end + + def key?(name) + @table.key?(name) + end + + def lookup(name) + @table[name] or setup_rb_error "no such config item: #{name}" + end + + def add(item) + @items.push item + @table[item.name] = item + end + + def remove(name) + item = lookup(name) + @items.delete_if {|i| i.name == name } + @table.delete_if {|name, i| i.name == name } + item + end + + def load_script(path, inst = nil) + if File.file?(path) + MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path + end + end + + def savefile + '.config' + end + + def load_savefile + begin + File.foreach(savefile()) do |line| + k, v = *line.split(/=/, 2) + self[k] = v.strip + end + rescue Errno::ENOENT + setup_rb_error $!.message + "\n#{File.basename($0)} config first" + end + end + + def save + @items.each {|i| i.value } + File.open(savefile(), 'w') {|f| + @items.each do |i| + f.printf "%s=%s\n", i.name, i.value if i.value? and i.value + end + } + end + + def load_standard_entries + standard_entries(@rbconfig).each do |ent| + add ent + end + end + + def standard_entries(rbconfig) + c = rbconfig + + rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) + + 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))))) + + if c['rubylibdir'] + # V > 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = c['rubylibdir'] + librubyverarch = c['archdir'] + siteruby = c['sitedir'] + siterubyver = c['sitelibdir'] + siterubyverarch = c['sitearchdir'] + elsif newpath_p + # 1.4.4 <= V <= 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = c['sitedir'] + siterubyver = "$siteruby/#{version}" + siterubyverarch = "$siterubyver/#{c['arch']}" + else + # V < 1.4.4 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" + siterubyver = siteruby + siterubyverarch = "$siterubyver/#{c['arch']}" + end + parameterize = lambda {|path| + path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') + } + + if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } + makeprog = arg.sub(/'/, '').split(/=/, 2)[1] + else + makeprog = 'make' + end + + [ + ExecItem.new('installdirs', 'std/site/home', + 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ + {|val, table| + case val + when 'std' + table['rbdir'] = '$librubyver' + table['sodir'] = '$librubyverarch' + when 'site' + table['rbdir'] = '$siterubyver' + table['sodir'] = '$siterubyverarch' + when 'home' + setup_rb_error '$HOME was not set' unless ENV['HOME'] + table['prefix'] = ENV['HOME'] + table['rbdir'] = '$libdir/ruby' + table['sodir'] = '$libdir/ruby' + end + }, + PathItem.new('prefix', 'path', c['prefix'], + 'path prefix of target environment'), + PathItem.new('bindir', 'path', parameterize.call(c['bindir']), + 'the directory for commands'), + PathItem.new('libdir', 'path', parameterize.call(c['libdir']), + 'the directory for libraries'), + PathItem.new('datadir', 'path', parameterize.call(c['datadir']), + 'the directory for shared data'), + PathItem.new('mandir', 'path', parameterize.call(c['mandir']), + 'the directory for man pages'), + PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), + 'the directory for system configuration files'), + PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), + 'the directory for local state data'), + PathItem.new('libruby', 'path', libruby, + 'the directory for ruby libraries'), + PathItem.new('librubyver', 'path', librubyver, + 'the directory for standard ruby libraries'), + PathItem.new('librubyverarch', 'path', librubyverarch, + 'the directory for standard ruby extensions'), + PathItem.new('siteruby', 'path', siteruby, + 'the directory for version-independent aux ruby libraries'), + PathItem.new('siterubyver', 'path', siterubyver, + 'the directory for aux ruby libraries'), + PathItem.new('siterubyverarch', 'path', siterubyverarch, + 'the directory for aux ruby binaries'), + PathItem.new('rbdir', 'path', '$siterubyver', + 'the directory for ruby scripts'), + PathItem.new('sodir', 'path', '$siterubyverarch', + 'the directory for ruby extentions'), + PathItem.new('rubypath', 'path', rubypath, + 'the path to set to #! line'), + ProgramItem.new('rubyprog', 'name', rubypath, + 'the ruby program using for installation'), + ProgramItem.new('makeprog', 'name', makeprog, + 'the make program to compile ruby extentions'), + SelectItem.new('shebang', 'all/ruby/never', 'ruby', + 'shebang line (#!) editing mode'), + BoolItem.new('without-ext', 'yes/no', 'no', + 'does not compile/install ruby extentions') + ] + end + private :standard_entries + + def load_multipackage_entries + multipackage_entries().each do |ent| + add ent + end + end + + def multipackage_entries + [ + PackageSelectionItem.new('with', 'name,name...', '', 'ALL', + 'package names that you want to install'), + PackageSelectionItem.new('without', 'name,name...', '', 'NONE', + 'package names that you do not want to install') + ] + end + private :multipackage_entries + + ALIASES = { + 'std-ruby' => 'librubyver', + 'stdruby' => 'librubyver', + 'rubylibdir' => 'librubyver', + 'archdir' => 'librubyverarch', + 'site-ruby-common' => 'siteruby', # For backward compatibility + 'site-ruby' => 'siterubyver', # For backward compatibility + 'bin-dir' => 'bindir', + 'bin-dir' => 'bindir', + 'rb-dir' => 'rbdir', + 'so-dir' => 'sodir', + 'data-dir' => 'datadir', + 'ruby-path' => 'rubypath', + 'ruby-prog' => 'rubyprog', + 'ruby' => 'rubyprog', + 'make-prog' => 'makeprog', + 'make' => 'makeprog' + } + + def fixup + ALIASES.each do |ali, name| + @table[ali] = @table[name] + end + @items.freeze + @table.freeze + @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ + end + + def parse_opt(opt) + m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" + m.to_a[1,2] + end + + def dllext + @rbconfig['DLEXT'] + end + + def value_config?(name) + lookup(name).value? + end + + class Item + def initialize(name, template, default, desc) + @name = name.freeze + @template = template + @value = default + @default = default + @description = desc + end + + attr_reader :name + attr_reader :description + + attr_accessor :default + alias help_default default + + def help_opt + "--#{@name}=#{@template}" + end + + def value? + true + end + + def value + @value + end + + def resolve(table) + @value.gsub(%r<\$([^/]+)>) { table[$1] } + end + + def set(val) + @value = check(val) + end + + private + + def check(val) + setup_rb_error "config: --#{name} requires argument" unless val + val + end + end + + class BoolItem < Item + def config_type + 'bool' + end + + def help_opt + "--#{@name}" + end + + private + + def check(val) + return 'yes' unless val + case val + when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' + when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' + else + setup_rb_error "config: --#{@name} accepts only yes/no for argument" + end + end + end + + class PathItem < Item + def config_type + 'path' + end + + private + + def check(path) + setup_rb_error "config: --#{@name} requires argument" unless path + path[0,1] == '$' ? path : File.expand_path(path) + end + end + + class ProgramItem < Item + def config_type + 'program' + end + end + + class SelectItem < Item + def initialize(name, selection, default, desc) + super + @ok = selection.split('/') + end + + def config_type + 'select' + end + + private + + def check(val) + unless @ok.include?(val.strip) + setup_rb_error "config: use --#{@name}=#{@template} (#{val})" + end + val.strip + end + end + + class ExecItem < Item + def initialize(name, selection, desc, &block) + super name, selection, nil, desc + @ok = selection.split('/') + @action = block + end + + def config_type + 'exec' + end + + def value? + false + end + + def resolve(table) + setup_rb_error "$#{name()} wrongly used as option value" + end + + undef set + + def evaluate(val, table) + v = val.strip.downcase + unless @ok.include?(v) + setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" + end + @action.call v, table + end + end + + class PackageSelectionItem < Item + def initialize(name, template, default, help_default, desc) + super name, template, default, desc + @help_default = help_default + end + + attr_reader :help_default + + def config_type + 'package' + end + + private + + def check(val) + unless File.dir?("packages/#{val}") + setup_rb_error "config: no such package: #{val}" + end + val + end + end + + class MetaConfigEnvironment + def initialize(config, installer) + @config = config + @installer = installer + end + + def config_names + @config.names + end + + def config?(name) + @config.key?(name) + end + + def bool_config?(name) + @config.lookup(name).config_type == 'bool' + end + + def path_config?(name) + @config.lookup(name).config_type == 'path' + end + + def value_config?(name) + @config.lookup(name).config_type != 'exec' + end + + def add_config(item) + @config.add item + end + + def add_bool_config(name, default, desc) + @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) + end + + def add_path_config(name, default, desc) + @config.add PathItem.new(name, 'path', default, desc) + end + + def set_config_default(name, default) + @config.lookup(name).default = default + end + + def remove_config(name) + @config.remove(name) + end + + # For only multipackage + def packages + raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer + @installer.packages + end + + # For only multipackage + def declare_packages(list) + raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer + @installer.packages = list + end + end + +end # class ConfigTable + + +# This module requires: #verbose?, #no_harm? +module FileOperations + + def mkdir_p(dirname, prefix = nil) + dirname = prefix + File.expand_path(dirname) if prefix + $stderr.puts "mkdir -p #{dirname}" if verbose? + return if no_harm? + + # Does not check '/', it's too abnormal. + dirs = File.expand_path(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(path) + $stderr.puts "rm -f #{path}" if verbose? + return if no_harm? + force_remove_file path + end + + def rm_rf(path) + $stderr.puts "rm -rf #{path}" if verbose? + return if no_harm? + remove_tree path + end + + def remove_tree(path) + if File.symlink?(path) + remove_file path + elsif File.dir?(path) + remove_tree0 path + else + force_remove_file path + end + end + + def remove_tree0(path) + Dir.foreach(path) do |ent| + next if ent == '.' + next if ent == '..' + entpath = "#{path}/#{ent}" + if File.symlink?(entpath) + remove_file entpath + elsif File.dir?(entpath) + remove_tree0 entpath + else + force_remove_file entpath + end + end + begin + Dir.rmdir path + rescue Errno::ENOTEMPTY + # directory may not be empty + end + end + + def move_file(src, dest) + force_remove_file 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 force_remove_file(path) + begin + remove_file path + rescue + end + end + + def remove_file(path) + File.chmod 0777, path + File.unlink path + end + + def install(from, dest, mode, prefix = nil) + $stderr.puts "install #{from} #{dest}" if verbose? + return if no_harm? + + realdest = prefix ? prefix + File.expand_path(dest) : dest + 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(*args) + $stderr.puts args.join(' ') if verbose? + system(*args) or raise RuntimeError, + "system(#{args.map{|a| a.inspect }.join(' ')}) failed" + end + + def ruby(*args) + command config('rubyprog'), *args + end + + def make(task = nil) + command(*[config('makeprog'), task].compact) + end + + def extdir?(dir) + File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") + end + + def files_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.file?("#{dir}/#{ent}") } + } + end + + DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) + + def directories_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT + } + end + +end + + +# This module requires: #srcdir_root, #objdir_root, #relpath +module HookScriptAPI + + def get_config(key) + @config[key] + end + + alias config get_config + + # obsolete: use metaconfig to change configuration + def set_config(key, val) + @config[key] = val + end + + # + # srcdir/objdir (works only in the package directory) + # + + 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.4.1' + Copyright = 'Copyright (c) 2000-2005 Minero Aoki' + + TASKS = [ + [ 'all', 'do config, setup, then install' ], + [ 'config', 'saves your configurations' ], + [ 'show', 'shows current configuration' ], + [ 'setup', 'compiles ruby extentions and others' ], + [ 'install', 'installs files' ], + [ 'test', 'run all tests in test/' ], + [ 'clean', "does `make clean' for each extention" ], + [ 'distclean',"does `make distclean' for each extention" ] + ] + + def ToplevelInstaller.invoke + config = ConfigTable.new(load_rbconfig()) + config.load_standard_entries + config.load_multipackage_entries if multipackage? + config.fixup + klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) + klass.new(File.dirname($0), config).invoke + end + + def ToplevelInstaller.multipackage? + File.dir?(File.dirname($0) + '/packages') + end + + def ToplevelInstaller.load_rbconfig + if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } + ARGV.delete(arg) + load File.expand_path(arg.split(/=/, 2)[1]) + $".push 'rbconfig.rb' + else + require 'rbconfig' + end + ::Config::CONFIG + end + + def initialize(ardir_root, config) + @ardir = File.expand_path(ardir_root) + @config = config + # cache + @valid_task_re = nil + end + + def config(key) + @config[key] + end + + def inspect + "#<#{self.class} #{__id__()}>" + end + + def invoke + run_metaconfigs + case task = parsearg_global() + when nil, 'all' + parsearg_config + init_installers + exec_config + exec_setup + exec_install + else + case task + when 'config', 'test' + ; + when 'clean', 'distclean' + @config.load_savefile if File.exist?(@config.savefile) + else + @config.load_savefile + end + __send__ "parsearg_#{task}" + init_installers + __send__ "exec_#{task}" + end + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig" + end + + def init_installers + @installer = Installer.new(@config, @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 + while arg = ARGV.shift + case arg + when /\A\w+\z/ + setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) + return arg + when '-q', '--quiet' + @config.verbose = false + when '--verbose' + @config.verbose = true + when '--help' + print_usage $stdout + exit 0 + when '--version' + puts "#{File.basename($0)} version #{Version}" + exit 0 + when '--copyright' + puts Copyright + exit 0 + else + setup_rb_error "unknown global option '#{arg}'" + end + end + nil + end + + def valid_task?(t) + valid_task_re() =~ t + end + + def valid_task_re + @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ + end + + def parsearg_no_options + unless ARGV.empty? + task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) + setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" + end + end + + alias parsearg_show parsearg_no_options + alias parsearg_setup parsearg_no_options + alias parsearg_test parsearg_no_options + alias parsearg_clean parsearg_no_options + alias parsearg_distclean parsearg_no_options + + def parsearg_config + evalopt = [] + set = [] + @config.config_opt = [] + while i = ARGV.shift + if /\A--?\z/ =~ i + @config.config_opt = ARGV.dup + break + end + name, value = *@config.parse_opt(i) + if @config.value_config?(name) + @config[name] = value + else + evalopt.push [name, value] + end + set.push name + end + evalopt.each do |name, value| + @config.lookup(name).evaluate value, @config + end + # Check if configuration is valid + set.each do |n| + @config[n] if @config.value_config?(n) + end + end + + def parsearg_install + @config.no_harm = false + @config.install_prefix = '' + while a = ARGV.shift + case a + when '--no-harm' + @config.no_harm = true + when /\A--prefix=/ + path = a.split(/=/, 2)[1] + path = File.expand_path(path) unless path[0,1] == '/' + @config.install_prefix = path + else + setup_rb_error "install: unknown option #{a}" + end + end + end + + def print_usage(out) + out.puts 'Typical Installation Procedure:' + out.puts " $ ruby #{File.basename $0} config" + out.puts " $ ruby #{File.basename $0} setup" + out.puts " # ruby #{File.basename $0} install (may require root privilege)" + out.puts + out.puts 'Detailed Usage:' + out.puts " ruby #{File.basename $0} " + out.puts " ruby #{File.basename $0} [] []" + + fmt = " %-24s %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, ' --help', 'print this message' + out.printf fmt, ' --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 fmt, name, desc + end + + fmt = " %-24s %s [%s]\n" + out.puts + out.puts 'Options for CONFIG or ALL:' + @config.each do |item| + out.printf fmt, item.help_opt, item.description, item.help_default + end + out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" + out.puts + out.puts 'Options for INSTALL:' + out.printf fmt, '--no-harm', 'only display what to do if given', 'off' + out.printf fmt, '--prefix=path', 'install path 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_test + @installer.exec_test + end + + def exec_show + @config.each do |i| + printf "%-20s %s\n", i.name, i.value if i.value? + end + end + + def exec_clean + @installer.exec_clean + end + + def exec_distclean + @installer.exec_distclean + end + +end # class ToplevelInstaller + + +class ToplevelInstallerMulti < ToplevelInstaller + + include FileOperations + + def initialize(ardir_root, config) + super + @packages = directories_of("#{@ardir}/packages") + raise 'no package exists' if @packages.empty? + @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig", self + @packages.each do |name| + @config.load_script "#{@ardir}/packages/#{name}/metaconfig" + end + end + + attr_reader :packages + + def 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 + + def init_installers + @installers = {} + @packages.each do |pack| + @installers[pack] = Installer.new(@config, + "#{@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| + setup_rb_error "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 + + # + # 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_test + run_hook 'pre-test' + each_selected_installers {|inst| inst.exec_test } + run_hook 'post-test' + end + + def exec_clean + rm_f @config.savefile + run_hook 'pre-clean' + each_selected_installers {|inst| inst.exec_clean } + run_hook 'post-clean' + end + + def exec_distclean + rm_f @config.savefile + 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 verbose? + Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") + Dir.chdir "packages/#{pack}" + yield @installers[pack] + Dir.chdir '../..' + end + end + + def run_hook(id) + @root_installer.run_hook id + end + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + +end # class ToplevelInstallerMulti + + +class Installer + + FILETYPES = %w( bin lib ext data conf man ) + + include FileOperations + include HookScriptAPI + + def initialize(config, srcroot, objroot) + @config = config + @srcdir = File.expand_path(srcroot) + @objdir = File.expand_path(objroot) + @currdir = '.' + end + + def inspect + "#<#{self.class} #{File.basename(@srcdir)}>" + end + + def noop(rel) + end + + # + # Hook Script API base methods + # + + def srcdir_root + @srcdir + end + + def objdir_root + @objdir + end + + def relpath + @currdir + end + + # + # Config Access + # + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + + def verbose_off + begin + save, @config.verbose = @config.verbose?, false + yield + ensure + @config.verbose = save + end + end + + # + # TASK config + # + + def exec_config + exec_task_traverse 'config' + end + + alias config_dir_bin noop + alias config_dir_lib noop + + def config_dir_ext(rel) + extconf if extdir?(curr_srcdir()) + end + + alias config_dir_data noop + alias config_dir_conf noop + alias config_dir_man noop + + def extconf + ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt + end + + # + # TASK setup + # + + def exec_setup + exec_task_traverse 'setup' + end + + def setup_dir_bin(rel) + files_of(curr_srcdir()).each do |fname| + update_shebang_line "#{curr_srcdir()}/#{fname}" + end + end + + alias setup_dir_lib noop + + def setup_dir_ext(rel) + make if extdir?(curr_srcdir()) + end + + alias setup_dir_data noop + alias setup_dir_conf noop + alias setup_dir_man noop + + def update_shebang_line(path) + return if no_harm? + return if config('shebang') == 'never' + old = Shebang.load(path) + if old + $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 + new = new_shebang(old) + return if new.to_s == old.to_s + else + return unless config('shebang') == 'all' + new = Shebang.new(config('rubypath')) + end + $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? + open_atomic_writer(path) {|output| + File.open(path, 'rb') {|f| + f.gets if old # discard + output.puts new.to_s + output.print f.read + } + } + end + + def new_shebang(old) + if /\Aruby/ =~ File.basename(old.cmd) + Shebang.new(config('rubypath'), old.args) + elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' + Shebang.new(config('rubypath'), old.args[1..-1]) + else + return old unless config('shebang') == 'all' + Shebang.new(config('rubypath')) + end + end + + def open_atomic_writer(path, &block) + tmpfile = File.basename(path) + '.tmp' + begin + File.open(tmpfile, 'wb', &block) + File.rename tmpfile, File.basename(path) + ensure + File.unlink tmpfile if File.exist?(tmpfile) + end + end + + class Shebang + def Shebang.load(path) + line = nil + File.open(path) {|f| + line = f.gets + } + return nil unless /\A#!/ =~ line + parse(line) + end + + def Shebang.parse(line) + cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') + new(cmd, args) + end + + def initialize(cmd, args = []) + @cmd = cmd + @args = args + end + + attr_reader :cmd + attr_reader :args + + def to_s + "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") + end + end + + # + # TASK install + # + + def exec_install + rm_f 'InstalledFiles' + exec_task_traverse 'install' + end + + def install_dir_bin(rel) + install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 + end + + def install_dir_lib(rel) + install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 + end + + def install_dir_ext(rel) + return unless extdir?(curr_srcdir()) + install_files rubyextentions('.'), + "#{config('sodir')}/#{File.dirname(rel)}", + 0555 + end + + def install_dir_data(rel) + install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 + end + + def install_dir_conf(rel) + # FIXME: should not remove current config files + # (rename previous file to .old/.org) + install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 + end + + def install_dir_man(rel) + install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 + end + + def install_files(list, dest, mode) + mkdir_p dest, @config.install_prefix + list.each do |fname| + install fname, dest, mode, @config.install_prefix + end + end + + def libfiles + glob_reject(%w(*.y *.output), targetfiles()) + end + + def rubyextentions(dir) + ents = glob_select("*.#{@config.dllext}", targetfiles()) + if ents.empty? + setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" + end + ents + end + + def targetfiles + mapdir(existfiles() - hookfiles()) + end + + def mapdir(ents) + ents.map {|ent| + if File.exist?(ent) + then ent # objdir + else "#{curr_srcdir()}/#{ent}" # srcdir + end + } + end + + # picked up many entries from cvs-1.11.1/src/ignore.c + JUNK_FILES = %w( + core RCSLOG tags TAGS .make.state + .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb + *~ *.old *.bak *.BAK *.orig *.rej _$* *$ + + *.org *.in .* + ) + + def existfiles + glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) + 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 glob_select(pat, ents) + re = globs2re([pat]) + ents.select {|ent| re =~ ent } + end + + def glob_reject(pats, ents) + re = globs2re(pats) + ents.reject {|ent| re =~ ent } + end + + GLOB2REGEX = { + '.' => '\.', + '$' => '\$', + '#' => '\#', + '*' => '.*' + } + + def globs2re(pats) + /\A(?:#{ + pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') + })\z/ + end + + # + # TASK test + # + + TESTDIR = 'test' + + def exec_test + unless File.directory?('test') + $stderr.puts 'no test in this package' if verbose? + return + end + $stderr.puts 'Running tests...' if verbose? + begin + require 'test/unit' + rescue LoadError + setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' + end + runner = Test::Unit::AutoRunner.new(true) + runner.to_run << TESTDIR + runner.run + end + + # + # TASK clean + # + + def exec_clean + exec_task_traverse 'clean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias clean_dir_bin noop + alias clean_dir_lib noop + alias clean_dir_data noop + alias clean_dir_conf noop + alias clean_dir_man noop + + def clean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'clean' if File.file?('Makefile') + end + + # + # TASK distclean + # + + def exec_distclean + exec_task_traverse 'distclean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias distclean_dir_bin noop + alias distclean_dir_lib noop + + def distclean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'distclean' if File.file?('Makefile') + end + + alias distclean_dir_data noop + alias distclean_dir_conf noop + alias distclean_dir_man noop + + # + # Traversing + # + + def exec_task_traverse(task) + run_hook "pre-#{task}" + FILETYPES.each do |type| + if type == 'ext' and config('without-ext') == 'yes' + $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)], '') + directories_of(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 + + def run_hook(id) + path = [ "#{curr_srcdir()}/#{id}", + "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } + return unless path + begin + instance_eval File.read(path), path, 1 + rescue + raise if $DEBUG + setup_rb_error "hook #{path} failed:\n" + $!.message + end + end + +end # class Installer + + +class SetupError < StandardError; end + +def setup_rb_error(msg) + raise SetupError, msg +end + +if $0 == __FILE__ + begin + ToplevelInstaller.invoke + rescue SetupError + raise if $DEBUG + $stderr.puts $!.message + $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." + exit 1 + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/support add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/THANKS.rdoc File: THANKS.rdoc =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/THANKS.rdoc;tzinfo2 @@ -1,0 +1,16 @@ +Net::SSH was originally written by Jamis Buck . In +addition, the following individuals are gratefully acknowledged for their +contributions: + +GOTOU Yuuzou + * help and code related to OpenSSL + +Guillaume Marçais + * support for communicating with the the PuTTY "pageant" process + +Daniel Berger + * help getting unit tests in earlier Net::SSH versions to pass in Windows + * initial version of Net::SSH::Config provided inspiration and encouragement + +Chris Andrews and Lee Jensen + * support for ssh agent forwarding =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh.rb File: ssh.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh.rb;tzinfo2 @@ -1,0 +1,215 @@ +# Make sure HOME is set, regardless of OS, so that File.expand_path works +# as expected with tilde characters. +ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : "." + +require 'logger' + +require 'net/ssh/config' +require 'net/ssh/errors' +require 'net/ssh/loggable' +require 'net/ssh/transport/session' +require 'net/ssh/authentication/session' +require 'net/ssh/connection/session' + +module Net + + # Net::SSH is a library for interacting, programmatically, with remote + # processes via the SSH2 protocol. Sessions are always initiated via + # Net::SSH.start. From there, a program interacts with the new SSH session + # via the convenience methods on Net::SSH::Connection::Session, by opening + # and interacting with new channels (Net::SSH::Connection:Session#open_channel + # and Net::SSH::Connection::Channel), or by forwarding local and/or + # remote ports through the connection (Net::SSH::Service::Forward). + # + # The SSH protocol is very event-oriented. Requests are sent from the client + # to the server, and are answered asynchronously. This gives great flexibility + # (since clients can have multiple requests pending at a time), but it also + # adds complexity. Net::SSH tries to manage this complexity by providing + # some simpler methods of synchronous communication (see Net::SSH::Connection::Session#exec!). + # + # In general, though, and if you want to do anything more complicated than + # simply executing commands and capturing their output, you'll need to use + # channels (Net::SSH::Connection::Channel) to build state machines that are + # executed while the event loop runs (Net::SSH::Connection::Session#loop). + # + # Net::SSH::Connection::Session and Net::SSH::Connection::Channel have more + # information about this technique. + # + # = "Um, all I want to do is X, just show me how!" + # + # == X == "execute a command and capture the output" + # + # Net::SSH.start("host", "user", :password => "password") do |ssh| + # result = ssh.exec!("ls -l") + # puts result + # end + # + # == X == "forward connections on a local port to a remote host" + # + # Net::SSH.start("host", "user", :password => "password") do |ssh| + # ssh.forward.local(1234, "www.google.com", 80) + # ssh.loop { true } + # end + # + # == X == "forward connections on a remote port to the local host" + # + # Net::SSH.start("host", "user", :password => "password") do |ssh| + # ssh.forward.remote(80, "www.google.com", 1234) + # ssh.loop { true } + # end + module SSH + # This is the set of options that Net::SSH.start recognizes. See + # Net::SSH.start for a description of each option. + VALID_OPTIONS = [ + :auth_methods, :compression, :compression_level, :config, :encryption, + :forward_agent, :hmac, :host_key, :kex, :keys, :key_data, :languages, + :logger, :paranoid, :password, :port, :proxy, :rekey_blocks_limit, + :rekey_limit, :rekey_packet_limit, :timeout, :verbose, + :global_known_hosts_file, :user_known_hosts_file, :host_key_alias, + :host_name, :user, :properties, :passphrase + ] + + # The standard means of starting a new SSH connection. When used with a + # block, the connection will be closed when the block terminates, otherwise + # the connection will just be returned. The yielded (or returned) value + # will be an instance of Net::SSH::Connection::Session (q.v.). (See also + # Net::SSH::Connection::Channel and Net::SSH::Service::Forward.) + # + # Net::SSH.start("host", "user") do |ssh| + # ssh.exec! "cp /some/file /another/location" + # hostname = ssh.exec!("hostname") + # + # ssh.open_channel do |ch| + # ch.exec "sudo -p 'sudo password: ' ls" do |ch, success| + # abort "could not execute sudo ls" unless success + # + # ch.on_data do |ch, data| + # print data + # if data =~ /sudo password: / + # ch.send_data("password\n") + # end + # end + # end + # end + # + # ssh.loop + # end + # + # This method accepts the following options (all are optional): + # + # * :auth_methods => an array of authentication methods to try + # * :compression => the compression algorithm to use, or +true+ to use + # whatever is supported. + # * :compression_level => the compression level to use when sending data + # * :config => set to +true+ to load the default OpenSSH config files + # (~/.ssh/config, /etc/ssh_config), or to +false+ to not load them, or to + # a file-name (or array of file-names) to load those specific configuration + # files. Defaults to +true+. + # * :encryption => the encryption cipher (or ciphers) to use + # * :forward_agent => set to true if you want the SSH agent connection to + # be forwarded + # * :global_known_hosts_file => the location of the global known hosts + # file. Set to an array if you want to specify multiple global known + # hosts files. Defaults to %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2). + # * :hmac => the hmac algorithm (or algorithms) to use + # * :host_key => the host key algorithm (or algorithms) to use + # * :host_key_alias => the host name to use when looking up or adding a + # host to a known_hosts dictionary file + # * :host_name => the real host name or IP to log into. This is used + # instead of the +host+ parameter, and is primarily only useful when + # specified in an SSH configuration file. It lets you specify an + # "alias", similarly to adding an entry in /etc/hosts but without needing + # to modify /etc/hosts. + # * :kex => the key exchange algorithm (or algorithms) to use + # * :keys => an array of file names of private keys to use for publickey + # and hostbased authentication + # * :key_data => an array of strings, with each element of the array being + # a raw private key in PEM format. + # * :logger => the logger instance to use when logging + # * :paranoid => either true, false, or :very, specifying how strict + # host-key verification should be + # * :passphrase => the passphrase to use when loading a private key (default + # is +nil+, for no passphrase) + # * :password => the password to use to login + # * :port => the port to use when connecting to the remote host + # * :properties => a hash of key/value pairs to add to the new connection's + # properties (see Net::SSH::Connection::Session#properties) + # * :proxy => a proxy instance (see Proxy) to use when connecting + # * :rekey_blocks_limit => the max number of blocks to process before rekeying + # * :rekey_limit => the max number of bytes to process before rekeying + # * :rekey_packet_limit => the max number of packets to process before rekeying + # * :timeout => how long to wait for the initial connection to be made + # * :user => the user name to log in as; this overrides the +user+ + # parameter, and is primarily only useful when provided via an SSH + # configuration file. + # * :user_known_hosts_file => the location of the user known hosts file. + # Set to an array to specify multiple user known hosts files. + # Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2). + # * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG + # is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the + # default. The symbols :debug, :info, :warn, :error, and :fatal are also + # supported and are translated to the corresponding Logger constant. + def self.start(host, user, options={}, &block) + invalid_options = options.keys - VALID_OPTIONS + if invalid_options.any? + raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}" + end + + options[:user] = user if user + options = configuration_for(host, options.fetch(:config, true)).merge(options) + host = options.fetch(:host_name, host) + + if !options.key?(:logger) + options[:logger] = Logger.new(STDERR) + options[:logger].level = Logger::FATAL + end + + if options[:verbose] + options[:logger].level = case options[:verbose] + when Fixnum then options[:verbose] + when :debug then Logger::DEBUG + when :info then Logger::INFO + when :warn then Logger::WARN + when :error then Logger::ERROR + when :fatal then Logger::FATAL + else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants" + end + end + + transport = Transport::Session.new(host, options) + auth = Authentication::Session.new(transport, options) + + user = options.fetch(:user, user) + if auth.authenticate("ssh-connection", user, options[:password]) + connection = Connection::Session.new(transport, options) + if block_given? + yield connection + connection.close + else + return connection + end + else + raise AuthenticationFailed, user + end + end + + # Returns a hash of the configuration options for the given host, as read + # from the SSH configuration file(s). If +use_ssh_config+ is true (the + # default), this will load configuration from both ~/.ssh/config and + # /etc/ssh_config. If +use_ssh_config+ is nil or false, nothing will be + # loaded (and an empty hash returned). Otherwise, +use_ssh_config+ may + # be a file name (or array of file names) of SSH configuration file(s) + # to read. + # + # See Net::SSH::Config for the full description of all supported options. + def self.configuration_for(host, use_ssh_config=true) + files = case use_ssh_config + when true then Net::SSH::Config.default_files + when false, nil then return {} + else Array(use_ssh_config) + end + + Net::SSH::Config.for(host, files) + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/buffer.rb File: buffer.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/buffer.rb;tzinfo2 @@ -1,0 +1,340 @@ +require 'net/ssh/ruby_compat' +require 'net/ssh/transport/openssl' + +module Net; module SSH + + # Net::SSH::Buffer is a flexible class for building and parsing binary + # data packets. It provides a stream-like interface for sequentially + # reading data items from the buffer, as well as a useful helper method + # for building binary packets given a signature. + # + # Writing to a buffer always appends to the end, regardless of where the + # read cursor is. Reading, on the other hand, always begins at the first + # byte of the buffer and increments the read cursor, with subsequent reads + # taking up where the last left off. + # + # As a consumer of the Net::SSH library, you will rarely come into contact + # with these buffer objects directly, but it could happen. Also, if you + # are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer + # class can be quite handy. + class Buffer + # This is a convenience method for creating and populating a new buffer + # from a single command. The arguments must be even in length, with the + # first of each pair of arguments being a symbol naming the type of the + # data that follows. If the type is :raw, the value is written directly + # to the hash. + # + # b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4") + # #-> "\1\0\0\0\5hello\1\2\3\4" + # + # The supported data types are: + # + # * :raw => write the next value verbatim (#write) + # * :int64 => write an 8-byte integer (#write_int64) + # * :long => write a 4-byte integer (#write_long) + # * :byte => write a single byte (#write_byte) + # * :string => write a 4-byte length followed by character data (#write_string) + # * :bool => write a single byte, interpreted as a boolean (#write_bool) + # * :bignum => write an SSH-encoded bignum (#write_bignum) + # * :key => write an SSH-encoded key value (#write_key) + # + # Any of these, except for :raw, accepts an Array argument, to make it + # easier to write multiple values of the same type in a briefer manner. + def self.from(*args) + raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0 + + buffer = new + 0.step(args.length-1, 2) do |index| + type = args[index] + value = args[index+1] + if type == :raw + buffer.append(value.to_s) + elsif Array === value + buffer.send("write_#{type}", *value) + else + buffer.send("write_#{type}", value) + end + end + + buffer + end + + # exposes the raw content of the buffer + attr_reader :content + + # the current position of the pointer in the buffer + attr_accessor :position + + # Creates a new buffer, initialized to the given content. The position + # is initialized to the beginning of the buffer. + def initialize(content="") + @content = content.to_s + @position = 0 + end + + # Returns the length of the buffer's content. + def length + @content.length + end + + # Returns the number of bytes available to be read (e.g., how many bytes + # remain between the current position and the end of the buffer). + def available + length - position + end + + # Returns a copy of the buffer's content. + def to_s + (@content || "").dup + end + + # Compares the contents of the two buffers, returning +true+ only if they + # are identical in size and content. + def ==(buffer) + to_s == buffer.to_s + end + + # Returns +true+ if the buffer contains no data (e.g., it is of zero length). + def empty? + @content.empty? + end + + # Resets the pointer to the start of the buffer. Subsequent reads will + # begin at position 0. + def reset! + @position = 0 + end + + # Returns true if the pointer is at the end of the buffer. Subsequent + # reads will return nil, in this case. + def eof? + @position >= length + end + + # Resets the buffer, making it empty. Also, resets the read position to + # 0. + def clear! + @content = "" + @position = 0 + end + + # Consumes n bytes from the buffer, where n is the current position + # unless otherwise specified. This is useful for removing data from the + # buffer that has previously been read, when you are expecting more data + # to be appended. It helps to keep the size of buffers down when they + # would otherwise tend to grow without bound. + # + # Returns the buffer object itself. + def consume!(n=position) + if n >= length + # optimize for a fairly common case + clear! + elsif n > 0 + @content = @content[n..-1] || "" + @position -= n + @position = 0 if @position < 0 + end + self + end + + # Appends the given text to the end of the buffer. Does not alter the + # read position. Returns the buffer object itself. + def append(text) + @content << text + self + end + + # Returns all text from the current pointer to the end of the buffer as + # a new Net::SSH::Buffer object. + def remainder_as_buffer + Buffer.new(@content[@position..-1]) + end + + # Reads all data up to and including the given pattern, which may be a + # String, Fixnum, or Regexp and is interpreted exactly as String#index + # does. Returns nil if nothing matches. Increments the position to point + # immediately after the pattern, if it does match. Returns all data up to + # and including the text that matched the pattern. + def read_to(pattern) + index = @content.index(pattern, @position) or return nil + length = case pattern + when String then pattern.length + when Fixnum then 1 + when Regexp then $&.length + end + index && read(index+length) + end + + # Reads and returns the next +count+ bytes from the buffer, starting from + # the read position. If +count+ is +nil+, this will return all remaining + # text in the buffer. This method will increment the pointer. + def read(count=nil) + count ||= length + count = length - @position if @position + count > length + @position += count + @content[@position-count, count] + end + + # Reads (as #read) and returns the given number of bytes from the buffer, + # and then consumes (as #consume!) all data up to the new read position. + def read!(count=nil) + data = read(count) + consume! + data + end + + # Return the next 8 bytes as a 64-bit integer (in network byte order). + # Returns nil if there are less than 8 bytes remaining to be read in the + # buffer. + def read_int64 + hi = read_long or return nil + lo = read_long or return nil + return (hi << 32) + lo + end + + # Return the next four bytes as a long integer (in network byte order). + # Returns nil if there are less than 4 bytes remaining to be read in the + # buffer. + def read_long + b = read(4) or return nil + b.unpack("N").first + end + + # Read and return the next byte in the buffer. Returns nil if called at + # the end of the buffer. + def read_byte + b = read(1) or return nil + b.getbyte(0) + end + + # Read and return an SSH2-encoded string. The string starts with a long + # integer that describes the number of bytes remaining in the string. + # Returns nil if there are not enough bytes to satisfy the request. + def read_string + length = read_long or return nil + read(length) + end + + # Read a single byte and convert it into a boolean, using 'C' rules + # (i.e., zero is false, non-zero is true). + def read_bool + b = read_byte or return nil + b != 0 + end + + # Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is + # essentially just a string, which is reinterpreted to be a bignum in + # binary format. + def read_bignum + data = read_string + return unless data + OpenSSL::BN.new(data, 2) + end + + # Read a key from the buffer. The key will start with a string + # describing its type. The remainder of the key is defined by the + # type that was read. + def read_key + type = read_string + return (type ? read_keyblob(type) : nil) + end + + # Read a keyblob of the given type from the buffer, and return it as + # a key. Only RSA and DSA keys are supported. + def read_keyblob(type) + case type + when "ssh-dss" + key = OpenSSL::PKey::DSA.new + key.p = read_bignum + key.q = read_bignum + key.g = read_bignum + key.pub_key = read_bignum + + when "ssh-rsa" + key = OpenSSL::PKey::RSA.new + key.e = read_bignum + key.n = read_bignum + + else + raise NotImplementedError, "unsupported key type `#{type}'" + end + + return key + end + + # Reads the next string from the buffer, and returns a new Buffer + # object that wraps it. + def read_buffer + Buffer.new(read_string) + end + + # Writes the given data literally into the string. Does not alter the + # read position. Returns the buffer object. + def write(*data) + data.each { |datum| @content << datum } + self + end + + # Writes each argument to the buffer as a network-byte-order-encoded + # 64-bit integer (8 bytes). Does not alter the read position. Returns the + # buffer object. + def write_int64(*n) + n.each do |i| + hi = (i >> 32) & 0xFFFFFFFF + lo = i & 0xFFFFFFFF + @content << [hi, lo].pack("N2") + end + self + end + + # Writes each argument to the buffer as a network-byte-order-encoded + # long (4-byte) integer. Does not alter the read position. Returns the + # buffer object. + def write_long(*n) + @content << n.pack("N*") + self + end + + # Writes each argument to the buffer as a byte. Does not alter the read + # position. Returns the buffer object. + def write_byte(*n) + n.each { |b| @content << b.chr } + self + end + + # Writes each argument to the buffer as an SSH2-encoded string. Each + # string is prefixed by its length, encoded as a 4-byte long integer. + # Does not alter the read position. Returns the buffer object. + def write_string(*text) + text.each do |string| + s = string.to_s + write_long(s.length) + write(s) + end + self + end + + # Writes each argument to the buffer as a (C-style) boolean, with 1 + # meaning true, and 0 meaning false. Does not alter the read position. + # Returns the buffer object. + def write_bool(*b) + b.each { |v| @content << (v ? "\1" : "\0") } + self + end + + # Writes each argument to the buffer as a bignum (SSH2-style). No + # checking is done to ensure that the arguments are, in fact, bignums. + # Does not alter the read position. Returns the buffer object. + def write_bignum(*n) + @content << n.map { |b| b.to_ssh }.join + self + end + + # Writes the given arguments to the buffer as SSH2-encoded keys. Does not + # alter the read position. Returns the buffer object. + def write_key(*key) + key.each { |k| append(k.to_blob) } + self + end + end +end; end; \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/buffered_io.rb File: buffered_io.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/buffered_io.rb;tzinfo2 @@ -1,0 +1,149 @@ +require 'net/ssh/buffer' +require 'net/ssh/loggable' + +module Net; module SSH + + # This module is used to extend sockets and other IO objects, to allow + # them to be buffered for both read and write. This abstraction makes it + # quite easy to write a select-based event loop + # (see Net::SSH::Connection::Session#listen_to). + # + # The general idea is that instead of calling #read directly on an IO that + # has been extended with this module, you call #fill (to add pending input + # to the internal read buffer), and then #read_available (to read from that + # buffer). Likewise, you don't call #write directly, you call #enqueue to + # add data to the write buffer, and then #send_pending or #wait_for_pending_sends + # to actually send the data across the wire. + # + # In this way you can easily use the object as an argument to IO.select, + # calling #fill when it is available for read, or #send_pending when it is + # available for write, and then call #enqueue and #read_available during + # the idle times. + # + # socket = TCPSocket.new(address, port) + # socket.extend(Net::SSH::BufferedIo) + # + # ssh.listen_to(socket) + # + # ssh.loop do + # if socket.available > 0 + # puts socket.read_available + # socket.enqueue("response\n") + # end + # end + # + # Note that this module must be used to extend an instance, and should not + # be included in a class. If you do want to use it via an include, then you + # must make sure to invoke the private #initialize_buffered_io method in + # your class' #initialize method: + # + # class Foo < IO + # include Net::SSH::BufferedIo + # + # def initialize + # initialize_buffered_io + # # ... + # end + # end + module BufferedIo + include Loggable + + # Called when the #extend is called on an object, with this module as the + # argument. It ensures that the modules instance variables are all properly + # initialized. + def self.extended(object) #:nodoc: + # need to use __send__ because #send is overridden in Socket + object.__send__(:initialize_buffered_io) + end + + # Tries to read up to +n+ bytes of data from the remote end, and appends + # the data to the input buffer. It returns the number of bytes read, or 0 + # if no data was available to be read. + def fill(n=8192) + input.consume! + data = recv(n) + debug { "read #{data.length} bytes" } + input.append(data) + return data.length + end + + # Read up to +length+ bytes from the input buffer. If +length+ is nil, + # all available data is read from the buffer. (See #available.) + def read_available(length=nil) + input.read(length || available) + end + + # Returns the number of bytes available to be read from the input buffer. + # (See #read_available.) + def available + input.available + end + + # Enqueues data in the output buffer, to be written when #send_pending + # is called. Note that the data is _not_ sent immediately by this method! + def enqueue(data) + output.append(data) + end + + # Returns +true+ if there is data waiting in the output buffer, and + # +false+ otherwise. + def pending_write? + output.length > 0 + end + + # Sends as much of the pending output as possible. Returns +true+ if any + # data was sent, and +false+ otherwise. + def send_pending + if output.length > 0 + sent = send(output.to_s, 0) + debug { "sent #{sent} bytes" } + output.consume!(sent) + return sent > 0 + else + return false + end + end + + # Calls #send_pending repeatedly, if necessary, blocking until the output + # buffer is empty. + def wait_for_pending_sends + send_pending + while output.length > 0 + result = IO.select(nil, [self]) or next + next unless result[1].any? + send_pending + end + end + + public # these methods are primarily for use in tests + + def write_buffer #:nodoc: + output.to_s + end + + def read_buffer #:nodoc: + input.to_s + end + + private + + #-- + # Can't use attr_reader here (after +private+) without incurring the + # wrath of "ruby -w". We hates it. + #++ + + def input; @input; end + def output; @output; end + + # Initializes the intput and output buffers for this object. This method + # is called automatically when the module is mixed into an object via + # Object#extend (see Net::SSH::BufferedIo.extended), but must be called + # explicitly in the +initialize+ method of any class that uses + # Module#include to add this module. + def initialize_buffered_io + @input = Net::SSH::Buffer.new + @output = Net::SSH::Buffer.new + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/config.rb File: config.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/config.rb;tzinfo2 @@ -1,0 +1,185 @@ +module Net; module SSH + + # The Net::SSH::Config class is used to parse OpenSSH configuration files, + # and translates that syntax into the configuration syntax that Net::SSH + # understands. This lets Net::SSH scripts read their configuration (to + # some extent) from OpenSSH configuration files (~/.ssh/config, /etc/ssh_config, + # and so forth). + # + # Only a subset of OpenSSH configuration options are understood: + # + # * Ciphers => maps to the :encryption option + # * Compression => :compression + # * CompressionLevel => :compression_level + # * ConnectTimeout => maps to the :timeout option + # * ForwardAgent => :forward_agent + # * GlobalKnownHostsFile => :global_known_hosts_file + # * HostBasedAuthentication => maps to the :auth_methods option + # * HostKeyAlgorithms => maps to :host_key option + # * HostKeyAlias => :host_key_alias + # * HostName => :host_name + # * IdentityFile => maps to the :keys option + # * Macs => maps to the :hmac option + # * PasswordAuthentication => maps to the :auth_methods option + # * Port => :port + # * PreferredAuthentications => maps to the :auth_methods option + # * RekeyLimit => :rekey_limit + # * User => :user + # * UserKnownHostsFile => :user_known_hosts_file + # + # Note that you will never need to use this class directly--you can control + # whether the OpenSSH configuration files are read by passing the :config + # option to Net::SSH.start. (They are, by default.) + class Config + class < OpenSSL::PKey::DH, + "rsa" => OpenSSL::PKey::RSA, + "dsa" => OpenSSL::PKey::DSA + } + + class < e + if encrypted_key + tries += 1 + if tries <= 3 + passphrase = prompt("Enter passphrase for #{filename}:", false) + retry + else + raise + end + else + raise + end + end + end + + # Loads a public key from a file. It will correctly determine whether + # the file describes an RSA or DSA key, and will load it + # appropriately. The new public key is returned. + def load_public_key(filename) + data = File.read(File.expand_path(filename)) + load_data_public_key(data, filename) + end + + # Loads a public key. It will correctly determine whether + # the file describes an RSA or DSA key, and will load it + # appropriately. The new public key is returned. + def load_data_public_key(data, filename="") + type, blob = data.split(/ /) + + raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil? + + blob = blob.unpack("m*").first + reader = Net::SSH::Buffer.new(blob) + reader.read_key or raise OpenSSL::PKey::PKeyError, "not a public key #{filename.inspect}" + end + end + + end + +end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/known_hosts.rb File: known_hosts.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/known_hosts.rb;tzinfo2 @@ -1,0 +1,129 @@ +require 'strscan' +require 'net/ssh/buffer' + +module Net; module SSH + + # Searches an OpenSSH-style known-host file for a given host, and returns all + # matching keys. This is used to implement host-key verification, as well as + # to determine what key a user prefers to use for a given host. + # + # This is used internally by Net::SSH, and will never need to be used directly + # by consumers of the library. + class KnownHosts + class < 98 (CHANNEL_REQUEST) + # p packet[:request] + # p packet[:want_reply] + # + # This is used exclusively internally by Net::SSH, and unless you're doing + # protocol-level manipulation or are extending Net::SSH in some way, you'll + # never need to use this class directly. + class Packet < Buffer + @@types = {} + + # Register a new packet type that should be recognized and auto-parsed by + # Net::SSH::Packet. Note that any packet type that is not preregistered + # will not be autoparsed. + # + # The +pairs+ parameter must be either empty, or an array of two-element + # tuples, where the first element of each tuple is the name of the field, + # and the second is the type. + # + # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string] + def self.register(type, *pairs) + @@types[type] = pairs + end + + include Transport::Constants, Authentication::Constants, Connection::Constants + + #-- + # These are the recognized packet types. All other packet types will be + # accepted, but not auto-parsed, requiring the client to parse the + # fields using the methods provided by Net::SSH::Buffer. + #++ + + register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string] + register IGNORE, [:data, :string] + register UNIMPLEMENTED, [:number, :long] + register DEBUG, [:always_display, :bool], [:message, :string], [:language, :string] + register SERVICE_ACCEPT, [:service_name, :string] + register USERAUTH_BANNER, [:message, :string], [:language, :string] + register USERAUTH_FAILURE, [:authentications, :string], [:partial_success, :bool] + register GLOBAL_REQUEST, [:request_type, :string], [:want_reply, :bool], [:request_data, :buffer] + register CHANNEL_OPEN, [:channel_type, :string], [:remote_id, :long], [:window_size, :long], [:packet_size, :long] + register CHANNEL_OPEN_CONFIRMATION, [:local_id, :long], [:remote_id, :long], [:window_size, :long], [:packet_size, :long] + register CHANNEL_OPEN_FAILURE, [:local_id, :long], [:reason_code, :long], [:description, :string], [:language, :string] + register CHANNEL_WINDOW_ADJUST, [:local_id, :long], [:extra_bytes, :long] + register CHANNEL_DATA, [:local_id, :long], [:data, :string] + register CHANNEL_EXTENDED_DATA, [:local_id, :long], [:data_type, :long], [:data, :string] + register CHANNEL_EOF, [:local_id, :long] + register CHANNEL_CLOSE, [:local_id, :long] + register CHANNEL_REQUEST, [:local_id, :long], [:request, :string], [:want_reply, :bool], [:request_data, :buffer] + register CHANNEL_SUCCESS, [:local_id, :long] + register CHANNEL_FAILURE, [:local_id, :long] + + # The (integer) type of this packet. + attr_reader :type + + # Create a new packet from the given payload. This will automatically + # parse the packet if it is one that has been previously registered with + # Packet.register; otherwise, the packet will need to be manually parsed + # using the methods provided in the Net::SSH::Buffer superclass. + def initialize(payload) + @named_elements = {} + super + @type = read_byte + instantiate! + end + + # Access one of the auto-parsed fields by name. Raises an error if no + # element by the given name exists. + def [](name) + name = name.to_sym + raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name) + @named_elements[name] + end + + private + + # Parse the packet's contents and assign the named elements, as described + # by the registered format for the packet. + def instantiate! + (@@types[type] || []).each do |name, datatype| + @named_elements[name.to_sym] = if datatype == :buffer + remainder_as_buffer + else + send("read_#{datatype}") + end + end + end + end +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/prompt.rb File: prompt.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/prompt.rb;tzinfo2 @@ -1,0 +1,93 @@ +module Net; module SSH + + # A basic prompt module that can be mixed into other objects. If HighLine is + # installed, it will be used to display prompts and read input from the + # user. Otherwise, the termios library will be used. If neither HighLine + # nor termios is installed, a simple prompt that echos text in the clear + # will be used. + + module PromptMethods + + # Defines the prompt method to use if the Highline library is installed. + module Highline + # Uses Highline#ask to present a prompt and accept input. If +echo+ is + # +false+, the characters entered by the user will not be echoed to the + # screen. + def prompt(prompt, echo=true) + @highline ||= ::HighLine.new + @highline.ask(prompt + " ") { |q| q.echo = echo } + end + end + + # Defines the prompt method to use if the Termios library is installed. + module Termios + # Displays the prompt to $stdout. If +echo+ is false, the Termios + # library will be used to disable keystroke echoing for the duration of + # this method. + def prompt(prompt, echo=true) + $stdout.print(prompt) + $stdout.flush + + set_echo(false) unless echo + $stdin.gets.chomp + ensure + if !echo + set_echo(true) + $stdout.puts + end + end + + private + + # Enables or disables keystroke echoing using the Termios library. + def set_echo(enable) + term = ::Termios.getattr($stdin) + + if enable + term.c_lflag |= (::Termios::ECHO | ::Termios::ICANON) + else + term.c_lflag &= ~::Termios::ECHO + end + + ::Termios.setattr($stdin, ::Termios::TCSANOW, term) + end + end + + # Defines the prompt method to use when neither Highline nor Termios are + # installed. + module Clear + # Displays the prompt to $stdout and pulls the response from $stdin. + # Text is always echoed in the clear, regardless of the +echo+ setting. + # The first time a prompt is given and +echo+ is false, a warning will + # be written to $stderr recommending that either Highline or Termios + # be installed. + def prompt(prompt, echo=true) + @seen_warning ||= false + if !echo && !@seen_warning + $stderr.puts "Text will be echoed in the clear. Please install the HighLine or Termios libraries to suppress echoed text." + @seen_warning = true + end + + $stdout.print(prompt) + $stdout.flush + $stdin.gets.chomp + end + end + end + + # Try to load Highline and Termios in turn, selecting the corresponding + # PromptMethods module to use. If neither are available, choose PromptMethods::Clear. + Prompt = begin + require 'highline' + HighLine.track_eof = false + PromptMethods::Highline + rescue LoadError + begin + require 'termios' + PromptMethods::Termios + rescue LoadError + PromptMethods::Clear + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/ruby_compat.rb File: ruby_compat.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/ruby_compat.rb;tzinfo2 @@ -1,0 +1,7 @@ +class String + if RUBY_VERSION < "1.9" + def getbyte(index) + self[index] + end + end +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/service add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test.rb File: test.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test.rb;tzinfo2 @@ -1,0 +1,89 @@ +require 'net/ssh/transport/session' +require 'net/ssh/connection/session' +require 'net/ssh/test/kex' +require 'net/ssh/test/socket' + +module Net; module SSH + + # This module may be used in unit tests, for when you want to test that your + # SSH state machines are really doing what you expect they are doing. You will + # typically include this module in your unit test class, and then build a + # "story" of expected sends and receives: + # + # require 'test/unit' + # require 'net/ssh/test' + # + # class MyTest < Test::Unit::TestCase + # include Net::SSH::Test + # + # def test_exec_via_channel_works + # story do |session| + # channel = session.opens_channel + # channel.sends_exec "ls" + # channel.gets_data "result of ls" + # channel.gets_close + # channel.sends_close + # end + # + # assert_scripted do + # result = nil + # + # connection.open_channel do |ch| + # ch.exec("ls") do |success| + # ch.on_data { |c, data| result = data } + # ch.on_close { |c| c.close } + # end + # end + # + # connection.loop + # assert_equal "result of ls", result + # end + # end + # end + # + # See Net::SSH::Test::Channel and Net::SSH::Test::Script for more options. + # + # Note that the Net::SSH::Test system is rather finicky yet, and can be kind + # of frustrating to get working. Any suggestions for improvement will be + # welcome! + module Test + # If a block is given, yields the script for the test socket (#socket). + # Otherwise, simply returns the socket's script. See Net::SSH::Test::Script. + def story + yield socket.script if block_given? + return socket.script + end + + # Returns the test socket instance to use for these tests (see + # Net::SSH::Test::Socket). + def socket(options={}) + @socket ||= Net::SSH::Test::Socket.new + end + + # Returns the connection session (Net::SSH::Connection::Session) for use + # in these tests. It is a fully functional SSH session, operating over + # a mock socket (#socket). + def connection(options={}) + @connection ||= Net::SSH::Connection::Session.new(transport(options), options) + end + + # Returns the transport session (Net::SSH::Transport::Session) for use + # in these tests. It is a fully functional SSH transport session, operating + # over a mock socket (#socket). + def transport(options={}) + @transport ||= Net::SSH::Transport::Session.new(options[:host] || "localhost", options.merge(:kex => "test", :host_key => "ssh-rsa", :paranoid => false, :proxy => socket(options))) + end + + # First asserts that a story has been described (see #story). Then yields, + # and then asserts that all items described in the script have been + # processed. Typically, this is called immediately after a story has + # been built, and the SSH commands being tested are then executed within + # the block passed to this assertion. + def assert_scripted + raise "there is no script to be processed" if socket.script.events.empty? + yield + assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still #{socket.script.events.length} pending" + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/verifiers add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/version.rb File: version.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/version.rb;tzinfo2 @@ -1,0 +1,62 @@ +module Net; module SSH + # A class for describing the current version of a library. The version + # consists of three parts: the +major+ number, the +minor+ number, and the + # +tiny+ (or +patch+) number. + # + # Two Version instances may be compared, so that you can test that a version + # of a library is what you require: + # + # require 'net/ssh/version' + # + # if Net::SSH::Version::CURRENT < Net::SSH::Version[2,1,0] + # abort "your software is too old!" + # end + class Version + include Comparable + + # A convenience method for instantiating a new Version instance with the + # given +major+, +minor+, and +tiny+ components. + def self.[](major, minor, tiny) + new(major, minor, tiny) + end + + attr_reader :major, :minor, :tiny + + # Create a new Version object with the given components. + def initialize(major, minor, tiny) + @major, @minor, @tiny = major, minor, tiny + end + + # Compare this version to the given +version+ object. + def <=>(version) + to_i <=> version.to_i + end + + # Converts this version object to a string, where each of the three + # version components are joined by the '.' character. E.g., 2.0.0. + def to_s + @to_s ||= [@major, @minor, @tiny].join(".") + end + + # Converts this version to a canonical integer that may be compared + # against other version objects. + def to_i + @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny + end + + # The major component of this version of the Net::SSH library + MAJOR = 2 + + # The minor component of this version of the Net::SSH library + MINOR = 0 + + # The tiny component of this version of the Net::SSH library + TINY = 13 + + # The current version of the Net::SSH library as a Version instance + CURRENT = new(MAJOR, MINOR, TINY) + + # The current version of the Net::SSH library as a String + STRING = CURRENT.to_s + end +end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/agent.rb File: agent.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/agent.rb;tzinfo2 @@ -1,0 +1,176 @@ +require 'net/ssh/buffer' +require 'net/ssh/errors' +require 'net/ssh/loggable' +require 'net/ssh/transport/server_version' + +require 'net/ssh/authentication/pageant' if File::ALT_SEPARATOR && !(RUBY_PLATFORM =~ /java/) + +module Net; module SSH; module Authentication + + # A trivial exception class for representing agent-specific errors. + class AgentError < Net::SSH::Exception; end + + # An exception for indicating that the SSH agent is not available. + class AgentNotAvailable < AgentError; end + + # This class implements a simple client for the ssh-agent protocol. It + # does not implement any specific protocol, but instead copies the + # behavior of the ssh-agent functions in the OpenSSH library (3.8). + # + # This means that although it behaves like a SSH1 client, it also has + # some SSH2 functionality (like signing data). + class Agent + include Loggable + + # A simple module for extending keys, to allow comments to be specified + # for them. + module Comment + attr_accessor :comment + end + + SSH2_AGENT_REQUEST_VERSION = 1 + SSH2_AGENT_REQUEST_IDENTITIES = 11 + SSH2_AGENT_IDENTITIES_ANSWER = 12 + SSH2_AGENT_SIGN_REQUEST = 13 + SSH2_AGENT_SIGN_RESPONSE = 14 + SSH2_AGENT_FAILURE = 30 + SSH2_AGENT_VERSION_RESPONSE = 103 + + SSH_COM_AGENT2_FAILURE = 102 + + SSH_AGENT_REQUEST_RSA_IDENTITIES = 1 + SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2 + SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5 + SSH_AGENT_FAILURE = 5 + + # The underlying socket being used to communicate with the SSH agent. + attr_reader :socket + + # Instantiates a new agent object, connects to a running SSH agent, + # negotiates the agent protocol version, and returns the agent object. + def self.connect(logger=nil) + agent = new(logger) + agent.connect! + agent.negotiate! + agent + end + + # Creates a new Agent object, using the optional logger instance to + # report status. + def initialize(logger=nil) + self.logger = logger + end + + # Connect to the agent process using the socket factory and socket name + # given by the attribute writers. If the agent on the other end of the + # socket reports that it is an SSH2-compatible agent, this will fail + # (it only supports the ssh-agent distributed by OpenSSH). + def connect! + begin + debug { "connecting to ssh-agent" } + @socket = agent_socket_factory.open(ENV['SSH_AUTH_SOCK']) + rescue + error { "could not connect to ssh-agent" } + raise AgentNotAvailable, $!.message + end + end + + # Attempts to negotiate the SSH agent protocol version. Raises an error + # if the version could not be negotiated successfully. + def negotiate! + # determine what type of agent we're communicating with + type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION) + + if type == SSH2_AGENT_VERSION_RESPONSE + raise NotImplementedError, "SSH2 agents are not yet supported" + elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2 + raise AgentError, "unknown response from agent: #{type}, #{body.to_s.inspect}" + end + end + + # Return an array of all identities (public keys) known to the agent. + # Each key returned is augmented with a +comment+ property which is set + # to the comment returned by the agent for that key. + def identities + type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES) + raise AgentError, "could not get identity count" if agent_failed(type) + raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER + + identities = [] + body.read_long.times do + key = Buffer.new(body.read_string).read_key + key.extend(Comment) + key.comment = body.read_string + identities.push key + end + + return identities + end + + # Closes this socket. This agent reference is no longer able to + # query the agent. + def close + @socket.close + end + + # Using the agent and the given public key, sign the given data. The + # signature is returned in SSH2 format. + def sign(key, data) + type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0) + + if agent_failed(type) + raise AgentError, "agent could not sign data with requested identity" + elsif type != SSH2_AGENT_SIGN_RESPONSE + raise AgentError, "bad authentication response #{type}" + end + + return reply.read_string + end + + private + + # Returns the agent socket factory to use. + def agent_socket_factory + if File::ALT_SEPARATOR + Pageant::Socket + else + UNIXSocket + end + end + + # Send a new packet of the given type, with the associated data. + def send_packet(type, *args) + buffer = Buffer.from(*args) + data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*") + debug { "sending agent request #{type} len #{buffer.length}" } + @socket.send data, 0 + end + + # Read the next packet from the agent. This will return a two-part + # tuple consisting of the packet type, and the packet's body (which + # is returned as a Net::SSH::Buffer). + def read_packet + buffer = Net::SSH::Buffer.new(@socket.read(4)) + buffer.append(@socket.read(buffer.read_long)) + type = buffer.read_byte + debug { "received agent packet #{type} len #{buffer.length-4}" } + return type, buffer + end + + # Send the given packet and return the subsequent reply from the agent. + # (See #send_packet and #read_packet). + def send_and_wait(type, *args) + send_packet(type, *args) + read_packet + end + + # Returns +true+ if the parameter indicates a "failure" response from + # the agent, and +false+ otherwise. + def agent_failed(type) + type == SSH_AGENT_FAILURE || + type == SSH2_AGENT_FAILURE || + type == SSH_COM_AGENT2_FAILURE + end + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/constants.rb File: constants.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/constants.rb;tzinfo2 @@ -1,0 +1,18 @@ +module Net; module SSH; module Authentication + + # Describes the constants used by the Net::SSH::Authentication components + # of the Net::SSH library. Individual authentication method implemenations + # may define yet more constants that are specific to their implementation. + module Constants + USERAUTH_REQUEST = 50 + USERAUTH_FAILURE = 51 + USERAUTH_SUCCESS = 52 + USERAUTH_BANNER = 53 + + USERAUTH_PASSWD_CHANGEREQ = 60 + USERAUTH_PK_OK = 60 + + USERAUTH_METHOD_RANGE = 60..79 + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/key_manager.rb File: key_manager.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/key_manager.rb;tzinfo2 @@ -1,0 +1,193 @@ +require 'net/ssh/errors' +require 'net/ssh/key_factory' +require 'net/ssh/loggable' +require 'net/ssh/authentication/agent' + +module Net + module SSH + module Authentication + + # A trivial exception class used to report errors in the key manager. + class KeyManagerError < Net::SSH::Exception; end + + # This class encapsulates all operations done by clients on a user's + # private keys. In practice, the client should never need a reference + # to a private key; instead, they grab a list of "identities" (public + # keys) that are available from the KeyManager, and then use + # the KeyManager to do various private key operations using those + # identities. + # + # The KeyManager also uses the Agent class to encapsulate the + # ssh-agent. Thus, from a client's perspective it is completely + # hidden whether an identity comes from the ssh-agent or from a file + # on disk. + class KeyManager + include Loggable + + # The list of user key files that will be examined + attr_reader :key_files + + # The list of user key data that will be examined + attr_reader :key_data + + # The map of loaded identities + attr_reader :known_identities + + # The map of options that were passed to the key-manager + attr_reader :options + + # Create a new KeyManager. By default, the manager will + # use the ssh-agent (if it is running). + def initialize(logger, options={}) + self.logger = logger + @key_files = [] + @key_data = [] + @use_agent = true + @known_identities = {} + @agent = nil + @options = options + end + + # Clear all knowledge of any loaded user keys. This also clears the list + # of default identity files that are to be loaded, thus making it + # appropriate to use if a client wishes to NOT use the default identity + # files. + def clear! + key_files.clear + key_data.clear + known_identities.clear + self + end + + # Add the given key_file to the list of key files that will be used. + def add(key_file) + key_files.push(File.expand_path(key_file)).uniq! + self + end + + # Add the given key_file to the list of keys that will be used. + def add_key_data(key_data_) + key_data.push(key_data_).uniq! + self + end + + # This is used as a hint to the KeyManager indicating that the agent + # connection is no longer needed. Any other open resources may be closed + # at this time. + # + # Calling this does NOT indicate that the KeyManager will no longer + # be used. Identities may still be requested and operations done on + # loaded identities, in which case, the agent will be automatically + # reconnected. This method simply allows the client connection to be + # closed when it will not be used in the immediate future. + def finish + @agent.close if @agent + @agent = nil + end + + # Iterates over all available identities (public keys) known to this + # manager. As it finds one, it will then yield it to the caller. + # The origin of the identities may be from files on disk or from an + # ssh-agent. Note that identities from an ssh-agent are always listed + # first in the array, with other identities coming after. + def each_identity + if agent + agent.identities.each do |key| + known_identities[key] = { :from => :agent } + yield key + end + end + + key_files.each do |file| + public_key_file = file + ".pub" + if File.readable?(public_key_file) + begin + key = KeyFactory.load_public_key(public_key_file) + known_identities[key] = { :from => :file, :file => file } + yield key + rescue Exception => e + error { "could not load public key file `#{public_key_file}': #{e.class} (#{e.message})" } + end + elsif File.readable?(file) + begin + private_key = KeyFactory.load_private_key(file, options[:passphrase]) + key = private_key.send(:public_key) + known_identities[key] = { :from => :file, :file => file, :key => private_key } + yield key + rescue Exception => e + error { "could not load private key file `#{file}': #{e.class} (#{e.message})" } + end + end + end + + key_data.each do |data| + private_key = KeyFactory.load_data_private_key(data) + key = private_key.send(:public_key) + known_identities[key] = { :from => :key_data, :data => data, :key => private_key } + yield key + end + + self + end + + # Sign the given data, using the corresponding private key of the given + # identity. If the identity was originally obtained from an ssh-agent, + # then the ssh-agent will be used to sign the data, otherwise the + # private key for the identity will be loaded from disk (if it hasn't + # been loaded already) and will then be used to sign the data. + # + # Regardless of the identity's origin or who does the signing, this + # will always return the signature in an SSH2-specified "signature + # blob" format. + def sign(identity, data) + info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager" + + if info[:key].nil? && info[:from] == :file + begin + info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase]) + rescue Exception => e + raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})" + end + end + + if info[:key] + return Net::SSH::Buffer.from(:string, identity.ssh_type, + :string, info[:key].ssh_do_sign(data.to_s)).to_s + end + + if info[:from] == :agent + raise KeyManagerError, "the agent is no longer available" unless agent + return agent.sign(identity, data.to_s) + end + + raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})" + end + + # Identifies whether the ssh-agent will be used or not. + def use_agent? + @use_agent + end + + # Toggles whether the ssh-agent will be used or not. If true, an + # attempt will be made to use the ssh-agent. If false, any existing + # connection to an agent is closed and the agent will not be used. + def use_agent=(use_agent) + finish if !use_agent + @use_agent = use_agent + end + + # Returns an Agent instance to use for communicating with an SSH + # agent process. Returns nil if use of an SSH agent has been disabled, + # or if the agent is otherwise not available. + def agent + return unless use_agent? + @agent ||= Agent.connect(logger) + rescue AgentNotAvailable + @use_agent = false + nil + end + end + + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/pageant.rb File: pageant.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/pageant.rb;tzinfo2 @@ -1,0 +1,183 @@ +require 'dl/import' +require 'dl/struct' + +require 'net/ssh/errors' + +module Net; module SSH; module Authentication + + # This module encapsulates the implementation of a socket factory that + # uses the PuTTY "pageant" utility to obtain information about SSH + # identities. + # + # This code is a slightly modified version of the original implementation + # by Guillaume Marçais (guillaume.marcais@free.fr). It is used and + # relicensed by permission. + module Pageant + + # From Putty pageant.c + AGENT_MAX_MSGLEN = 8192 + AGENT_COPYDATA_ID = 0x804e50ba + + # The definition of the Windows methods and data structures used in + # communicating with the pageant process. + module Win + extend DL::Importable + + dlload 'user32' + dlload 'kernel32' + + typealias("LPCTSTR", "char *") # From winnt.h + typealias("LPVOID", "void *") # From winnt.h + typealias("LPCVOID", "const void *") # From windef.h + typealias("LRESULT", "long") # From windef.h + typealias("WPARAM", "unsigned int *") # From windef.h + typealias("LPARAM", "long *") # From windef.h + typealias("PDWORD_PTR", "long *") # From basetsd.h + + # From winbase.h, winnt.h + INVALID_HANDLE_VALUE = -1 + NULL = nil + PAGE_READWRITE = 0x0004 + FILE_MAP_WRITE = 2 + WM_COPYDATA = 74 + + SMTO_NORMAL = 0 # From winuser.h + + # args: lpClassName, lpWindowName + extern 'HWND FindWindow(LPCTSTR, LPCTSTR)' + + # args: none + extern 'DWORD GetCurrentThreadId()' + + # args: hFile, (ignored), flProtect, dwMaximumSizeHigh, + # dwMaximumSizeLow, lpName + extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, DWORD, ' + + 'DWORD, LPCTSTR)' + + # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, + # dwfileOffsetLow, dwNumberOfBytesToMap + extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)' + + # args: lpBaseAddress + extern 'BOOL UnmapViewOfFile(LPCVOID)' + + # args: hObject + extern 'BOOL CloseHandle(HANDLE)' + + # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult + extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' + + 'UINT, UINT, PDWORD_PTR)' + end + + # This is the pseudo-socket implementation that mimics the interface of + # a socket, translating each request into a Windows messaging call to + # the pageant daemon. This allows pageant support to be implemented + # simply by replacing the socket factory used by the Agent class. + class Socket + + private_class_method :new + + # The factory method for creating a new Socket instance. The location + # parameter is ignored, and is only needed for compatibility with + # the general Socket interface. + def self.open(location=nil) + new + end + + # Create a new instance that communicates with the running pageant + # instance. If no such instance is running, this will cause an error. + def initialize + @win = Win.findWindow("Pageant", "Pageant") + + if @win == 0 + raise Net::SSH::Exception, + "pageant process not running" + end + + @res = nil + @pos = 0 + end + + # Forwards the data to #send_query, ignoring any arguments after + # the first. Returns 0. + def send(data, *args) + @res = send_query(data) + @pos = 0 + end + + # Packages the given query string and sends it to the pageant + # process via the Windows messaging subsystem. The result is + # cached, to be returned piece-wise when #read is called. + def send_query(query) + res = nil + filemap = 0 + ptr = nil + id = DL::PtrData.malloc(DL.sizeof("L")) + + mapname = "PageantRequest%08x\000" % Win.getCurrentThreadId() + filemap = Win.createFileMapping(Win::INVALID_HANDLE_VALUE, + Win::NULL, + Win::PAGE_READWRITE, 0, + AGENT_MAX_MSGLEN, mapname) + if filemap == 0 + raise Net::SSH::Exception, + "Creation of file mapping failed" + end + + ptr = Win.mapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0, + AGENT_MAX_MSGLEN) + + if ptr.nil? || ptr.null? + raise Net::SSH::Exception, "Mapping of file failed" + end + + ptr[0] = query + + cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname]. + pack("LLp").to_ptr + succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL, + cds, Win::SMTO_NORMAL, 5000, id) + + if succ > 0 + retlen = 4 + ptr.to_s(4).unpack("N")[0] + res = ptr.to_s(retlen) + end + + return res + ensure + Win.unmapViewOfFile(ptr) unless ptr.nil? || ptr.null? + Win.closeHandle(filemap) if filemap != 0 + end + + # Conceptually close the socket. This doesn't really do anthing + # significant, but merely complies with the Socket interface. + def close + @res = nil + @pos = 0 + end + + # Conceptually asks if the socket is closed. As with #close, + # this doesn't really do anything significant, but merely + # complies with the Socket interface. + def closed? + @res.nil? && @pos.zero? + end + + # Reads +n+ bytes from the cached result of the last query. If +n+ + # is +nil+, returns all remaining data from the last query. + def read(n = nil) + return nil unless @res + if n.nil? + start, @pos = @pos, @res.size + return @res[start..-1] + else + start, @pos = @pos, @pos + n + return @res[start, n] + end + end + + end + + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/session.rb File: session.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/session.rb;tzinfo2 @@ -1,0 +1,134 @@ +require 'net/ssh/loggable' +require 'net/ssh/transport/constants' +require 'net/ssh/authentication/constants' +require 'net/ssh/authentication/key_manager' +require 'net/ssh/authentication/methods/publickey' +require 'net/ssh/authentication/methods/hostbased' +require 'net/ssh/authentication/methods/password' +require 'net/ssh/authentication/methods/keyboard_interactive' + +module Net; module SSH; module Authentication + + # Represents an authentication session. It manages the authentication of + # a user over an established connection (the "transport" object, see + # Net::SSH::Transport::Session). + # + # The use of an authentication session to manage user authentication is + # internal to Net::SSH (specifically Net::SSH.start). Consumers of the + # Net::SSH library will never need to access this class directly. + class Session + include Transport::Constants, Constants, Loggable + + # transport layer abstraction + attr_reader :transport + + # the list of authentication methods to try + attr_reader :auth_methods + + # the list of authentication methods that are allowed + attr_reader :allowed_auth_methods + + # a hash of options, given at construction time + attr_reader :options + + # Instantiates a new Authentication::Session object over the given + # transport layer abstraction. + def initialize(transport, options={}) + self.logger = transport.logger + @transport = transport + + @auth_methods = options[:auth_methods] || %w(publickey hostbased password keyboard-interactive) + @options = options + + @allowed_auth_methods = @auth_methods + end + + # Attempts to authenticate the given user, in preparation for the next + # service request. Returns true if an authentication method succeeds in + # authenticating the user, and false otherwise. + def authenticate(next_service, username, password=nil) + debug { "beginning authentication of `#{username}'" } + + transport.send_message(transport.service_request("ssh-userauth")) + message = expect_message(SERVICE_ACCEPT) + + key_manager = KeyManager.new(logger, options) + keys.each { |key| key_manager.add(key) } unless keys.empty? + key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty? + + attempted = [] + + @auth_methods.each do |name| + next unless @allowed_auth_methods.include?(name) + attempted << name + + debug { "trying #{name}" } + method = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join).new(self, :key_manager => key_manager) + + return true if method.authenticate(next_service, username, password) + end + + error { "all authorization methods failed (tried #{attempted.join(', ')})" } + return false + ensure + key_manager.finish if key_manager + end + + # Blocks until a packet is received. It silently handles USERAUTH_BANNER + # packets, and will raise an error if any packet is received that is not + # valid during user authentication. + def next_message + loop do + packet = transport.next_message + + case packet.type + when USERAUTH_BANNER + info { packet[:message] } + # TODO add a hook for people to retrieve the banner when it is sent + + when USERAUTH_FAILURE + @allowed_auth_methods = packet[:authentications].split(/,/) + debug { "allowed methods: #{packet[:authentications]}" } + return packet + + when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT + return packet + + when USERAUTH_SUCCESS + transport.hint :authenticated + return packet + + else + raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})" + end + end + end + + # Blocks until a packet is received, and returns it if it is of the given + # type. If it is not, an exception is raised. + def expect_message(type) + message = next_message + unless message.type == type + raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" + end + message + end + + private + + # Returns an array of paths to the key files that should be used when + # attempting any key-based authentication mechanism. + def keys + Array( + options[:keys] || + %w(~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa) + ) + end + + # Returns an array of the key data that should be used when + # attempting any key-based authentication mechanism. + def key_data + Array(options[:key_data]) + end + end +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/abstract.rb File: abstract.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/abstract.rb;tzinfo2 @@ -1,0 +1,60 @@ +require 'net/ssh/buffer' +require 'net/ssh/errors' +require 'net/ssh/loggable' +require 'net/ssh/authentication/constants' + +module Net; module SSH; module Authentication; module Methods + + # The base class of all user authentication methods. It provides a few + # bits of common functionality. + class Abstract + include Constants, Loggable + + # The authentication session object + attr_reader :session + + # The key manager object. Not all authentication methods will require + # this. + attr_reader :key_manager + + # Instantiates a new authentication method. + def initialize(session, options={}) + @session = session + @key_manager = options[:key_manager] + @options = options + self.logger = session.logger + end + + # Returns the session-id, as generated during the first key exchange of + # an SSH connection. + def session_id + session.transport.algorithms.session_id + end + + # Sends a message via the underlying transport layer abstraction. This + # will block until the message is completely sent. + def send_message(msg) + session.transport.send_message(msg) + end + + # Creates a new USERAUTH_REQUEST packet. The extra arguments on the end + # must be either boolean values or strings, and are tacked onto the end + # of the packet. The new packet is returned, ready for sending. + def userauth_request(username, next_service, auth_method, *others) + buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST, + :string, username, :string, next_service, :string, auth_method) + + others.each do |value| + case value + when true, false then buffer.write_bool(value) + when String then buffer.write_string(value) + else raise ArgumentError, "don't know how to write #{value.inspect}" + end + end + + buffer + end + + end + +end; end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/hostbased.rb File: hostbased.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/hostbased.rb;tzinfo2 @@ -1,0 +1,71 @@ +require 'net/ssh/authentication/methods/abstract' + +module Net + module SSH + module Authentication + module Methods + + # Implements the host-based SSH authentication method. + class Hostbased < Abstract + include Constants + + # Attempts to perform host-based authorization of the user by trying + # all known keys. + def authenticate(next_service, username, password=nil) + return false unless key_manager + + key_manager.each_identity do |identity| + return true if authenticate_with(identity, next_service, + username, key_manager) + end + + return false + end + + private + + # Returns the hostname as reported by the underlying socket. + def hostname + session.transport.socket.client_name + end + + # Attempts to perform host-based authentication of the user, using + # the given host identity (key). + def authenticate_with(identity, next_service, username, key_manager) + debug { "trying hostbased (#{identity.fingerprint})" } + client_username = ENV['USER'] || username + + req = build_request(identity, next_service, username, "#{hostname}.", client_username) + sig_data = Buffer.from(:string, session_id, :raw, req) + + sig = key_manager.sign(identity, sig_data.to_s) + + message = Buffer.from(:raw, req, :string, sig) + + send_message(message) + message = session.next_message + + case message.type + when USERAUTH_SUCCESS + info { "hostbased succeeded (#{identity.fingerprint})" } + return true + when USERAUTH_FAILURE + info { "hostbased failed (#{identity.fingerprint})" } + return false + else + raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" + end + end + + # Build the "core" hostbased request string. + def build_request(identity, next_service, username, hostname, client_username) + userauth_request(username, next_service, "hostbased", identity.ssh_type, + Buffer.from(:key, identity).to_s, hostname, client_username).to_s + end + + end + + end + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/keyboard_interactive.rb File: keyboard_interactive.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/keyboard_interactive.rb;tzinfo2 @@ -1,0 +1,66 @@ +require 'net/ssh/prompt' +require 'net/ssh/authentication/methods/abstract' + +module Net + module SSH + module Authentication + module Methods + + # Implements the "keyboard-interactive" SSH authentication method. + class KeyboardInteractive < Abstract + include Prompt + + USERAUTH_INFO_REQUEST = 60 + USERAUTH_INFO_RESPONSE = 61 + + # Attempt to authenticate the given user for the given service. + def authenticate(next_service, username, password=nil) + debug { "trying keyboard-interactive" } + send_message(userauth_request(username, next_service, "keyboard-interactive", "", "")) + + loop do + message = session.next_message + + case message.type + when USERAUTH_SUCCESS + debug { "keyboard-interactive succeeded" } + return true + when USERAUTH_FAILURE + debug { "keyboard-interactive failed" } + return false + when USERAUTH_INFO_REQUEST + name = message.read_string + instruction = message.read_string + debug { "keyboard-interactive info request" } + + unless password + puts(name) unless name.empty? + puts(instruction) unless instruction.empty? + end + + lang_tag = message.read_string + responses =[] + + message.read_long.times do + text = message.read_string + echo = message.read_bool + responses << (password || prompt(text, echo)) + end + + # if the password failed the first time around, don't try + # and use it on subsequent requests. + password = nil + + msg = Buffer.from(:byte, USERAUTH_INFO_RESPONSE, :long, responses.length, :string, responses) + send_message(msg) + else + raise Net::SSH::Exception, "unexpected reply in keyboard interactive: #{message.type} (#{message.inspect})" + end + end + end + end + + end + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/password.rb File: password.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/password.rb;tzinfo2 @@ -1,0 +1,39 @@ +require 'net/ssh/errors' +require 'net/ssh/authentication/methods/abstract' + +module Net + module SSH + module Authentication + module Methods + + # Implements the "password" SSH authentication method. + class Password < Abstract + # Attempt to authenticate the given user for the given service. If + # the password parameter is nil, this will never do anything except + # return false. + def authenticate(next_service, username, password=nil) + return false unless password + + send_message(userauth_request(username, next_service, "password", false, password)) + message = session.next_message + + case message.type + when USERAUTH_SUCCESS + debug { "password succeeded" } + return true + when USERAUTH_FAILURE + debug { "password failed" } + return false + when USERAUTH_PASSWD_CHANGEREQ + debug { "password change request received, failing" } + return false + else + raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" + end + end + end + + end + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/publickey.rb File: publickey.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/authentication/methods/publickey.rb;tzinfo2 @@ -1,0 +1,92 @@ +require 'net/ssh/buffer' +require 'net/ssh/errors' +require 'net/ssh/authentication/methods/abstract' + +module Net + module SSH + module Authentication + module Methods + + # Implements the "publickey" SSH authentication method. + class Publickey < Abstract + # Attempts to perform public-key authentication for the given + # username, trying each identity known to the key manager. If any of + # them succeed, returns +true+, otherwise returns +false+. This + # requires the presence of a key manager. + def authenticate(next_service, username, password=nil) + return false unless key_manager + + key_manager.each_identity do |identity| + return true if authenticate_with(identity, next_service, username) + end + + return false + end + + private + + # Builds a packet that contains the request formatted for sending + # a public-key request to the server. + def build_request(pub_key, username, next_service, has_sig) + blob = Net::SSH::Buffer.new + blob.write_key pub_key + + userauth_request(username, next_service, "publickey", has_sig, + pub_key.ssh_type, blob.to_s) + end + + # Builds and sends a request formatted for a public-key + # authentication request. + def send_request(pub_key, username, next_service, signature=nil) + msg = build_request(pub_key, username, next_service, !signature.nil?) + msg.write_string(signature) if signature + send_message(msg) + end + + # Attempts to perform public-key authentication for the given + # username, with the given identity (public key). Returns +true+ if + # successful, or +false+ otherwise. + def authenticate_with(identity, next_service, username) + debug { "trying publickey (#{identity.fingerprint})" } + send_request(identity, username, next_service) + + message = session.next_message + + case message.type + when USERAUTH_PK_OK + buffer = build_request(identity, username, next_service, true) + sig_data = Net::SSH::Buffer.new + sig_data.write_string(session_id) + sig_data.append(buffer.to_s) + + sig_blob = key_manager.sign(identity, sig_data) + + send_request(identity, username, next_service, sig_blob.to_s) + message = session.next_message + + case message.type + when USERAUTH_SUCCESS + debug { "publickey succeeded (#{identity.fingerprint})" } + return true + when USERAUTH_FAILURE + debug { "publickey failed (#{identity.fingerprint})" } + return false + else + raise Net::SSH::Exception, + "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" + end + + when USERAUTH_FAILURE + return false + + else + raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" + end + end + + end + + end + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/channel.rb File: channel.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/channel.rb;tzinfo2 @@ -1,0 +1,625 @@ +require 'net/ssh/loggable' +require 'net/ssh/connection/constants' +require 'net/ssh/connection/term' + +module Net; module SSH; module Connection + + # The channel abstraction. Multiple "channels" can be multiplexed onto a + # single SSH channel, each operating independently and seemingly in parallel. + # This class represents a single such channel. Most operations performed + # with the Net::SSH library will involve using one or more channels. + # + # Channels are intended to be used asynchronously. You request that one be + # opened (via Connection::Session#open_channel), and when it is opened, your + # callback is invoked. Then, you set various other callbacks on the newly + # opened channel, which are called in response to the corresponding events. + # Programming with Net::SSH works best if you think of your programs as + # state machines. Complex programs are best implemented as objects that + # wrap a channel. See Net::SCP and Net::SFTP for examples of how complex + # state machines can be built on top of the SSH protocol. + # + # ssh.open_channel do |channel| + # channel.exec("/invoke/some/command") do |ch, success| + # abort "could not execute command" unless success + # + # channel.on_data do |ch, data| + # puts "got stdout: #{data}" + # channel.send_data "something for stdin\n" + # end + # + # channel.on_extended_data do |ch, type, data| + # puts "got stderr: #{data}" + # end + # + # channel.on_close do |ch| + # puts "channel is closing!" + # end + # end + # end + # + # ssh.loop + # + # Channels also have a basic hash-like interface, that allows programs to + # store arbitrary state information on a channel object. This helps simplify + # the writing of state machines, especially when you may be juggling + # multiple open channels at the same time. + # + # Note that data sent across SSH channels are governed by maximum packet + # sizes and maximum window sizes. These details are managed internally + # by Net::SSH::Connection::Channel, so you may remain blissfully ignorant + # if you so desire, but you can always inspect the current maximums, as + # well as the remaining window size, using the reader attributes for those + # values. + class Channel + include Constants, Loggable + + # The local id for this channel, assigned by the Net::SSH::Connection::Session instance. + attr_reader :local_id + + # The remote id for this channel, assigned by the remote host. + attr_reader :remote_id + + # The type of this channel, usually "session". + attr_reader :type + + # The underlying Net::SSH::Connection::Session instance that supports this channel. + attr_reader :connection + + # The maximum packet size that the local host can receive. + attr_reader :local_maximum_packet_size + + # The maximum amount of data that the local end of this channel can + # receive. This is a total, not per-packet. + attr_reader :local_maximum_window_size + + # The maximum packet size that the remote host can receive. + attr_reader :remote_maximum_packet_size + + # The maximum amount of data that the remote end of this channel can + # receive. This is a total, not per-packet. + attr_reader :remote_maximum_window_size + + # This is the remaining window size on the local end of this channel. When + # this reaches zero, no more data can be received. + attr_reader :local_window_size + + # This is the remaining window size on the remote end of this channel. When + # this reaches zero, no more data can be sent. + attr_reader :remote_window_size + + # A hash of properties for this channel. These can be used to store state + # information about this channel. See also #[] and #[]=. + attr_reader :properties + + # The output buffer for this channel. Data written to the channel is + # enqueued here, to be written as CHANNEL_DATA packets during each pass of + # the event loop. See Connection::Session#process and #enqueue_pending_output. + attr_reader :output #:nodoc: + + # The list of pending requests. Each time a request is sent which requires + # a reply, the corresponding callback is pushed onto this queue. As responses + # arrive, they are shifted off the front and handled. + attr_reader :pending_requests #:nodoc: + + # Instantiates a new channel on the given connection, of the given type, + # and with the given id. If a block is given, it will be remembered until + # the channel is confirmed open by the server, and will be invoked at + # that time (see #do_open_confirmation). + # + # This also sets the default maximum packet size and maximum window size. + def initialize(connection, type, local_id, &on_confirm_open) + self.logger = connection.logger + + @connection = connection + @type = type + @local_id = local_id + + @local_maximum_packet_size = 0x10000 + @local_window_size = @local_maximum_window_size = 0x20000 + + @on_confirm_open = on_confirm_open + + @output = Buffer.new + + @properties = {} + + @pending_requests = [] + @on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil + @on_request = {} + @closing = @eof = false + end + + # A shortcut for accessing properties of the channel (see #properties). + def [](name) + @properties[name] + end + + # A shortcut for setting properties of the channel (see #properties). + def []=(name, value) + @properties[name] = value + end + + # Syntactic sugar for executing a command. Sends a channel request asking + # that the given command be invoked. If the block is given, it will be + # called when the server responds. The first parameter will be the + # channel, and the second will be true or false, indicating whether the + # request succeeded or not. In this case, success means that the command + # is being executed, not that it has completed, and failure means that the + # command altogether failed to be executed. + # + # channel.exec "ls -l /home" do |ch, success| + # if success + # puts "command has begun executing..." + # # this is a good place to hang callbacks like #on_data... + # else + # puts "alas! the command could not be invoked!" + # end + # end + def exec(command, &block) + send_channel_request("exec", :string, command, &block) + end + + # Syntactic sugar for requesting that a subsystem be started. Subsystems + # are a way for other protocols (like SFTP) to be run, using SSH as + # the transport. Generally, you'll never need to call this directly unless + # you are the implementor of something that consumes an SSH subsystem, like + # SFTP. + # + # channel.subsystem("sftp") do |ch, success| + # if success + # puts "subsystem successfully started" + # else + # puts "subsystem could not be started" + # end + # end + def subsystem(subsystem, &block) + send_channel_request("subsystem", :string, subsystem, &block) + end + + # Syntactic sugar for setting an environment variable in the remote + # process' environment. Note that for security reasons, the server may + # refuse to set certain environment variables, or all, at the server's + # discretion. If you are connecting to an OpenSSH server, you will + # need to update the AcceptEnv setting in the sshd_config to include the + # environment variables you want to send. + # + # channel.env "PATH", "/usr/local/bin" + def env(variable_name, variable_value, &block) + send_channel_request("env", :string, variable_name, :string, variable_value, &block) + end + + # A hash of the valid PTY options (see #request_pty). + VALID_PTY_OPTIONS = { :term => "xterm", + :chars_wide => 80, + :chars_high => 24, + :pixels_wide => 640, + :pixels_high => 480, + :modes => {} } + + # Requests that a pseudo-tty (or "pty") be made available for this channel. + # This is useful when you want to invoke and interact with some kind of + # screen-based program (e.g., vim, or some menuing system). + # + # Note, that without a pty some programs (e.g. sudo, or subversion) on + # some systems, will not be able to run interactively, and will error + # instead of prompt if they ever need some user interaction. + # + # Note, too, that when a pty is requested, user's shell configuration + # scripts (.bashrc and such) are not run by default, whereas they are + # run when a pty is not present. + # + # channel.request_pty do |ch, success| + # if success + # puts "pty successfully obtained" + # else + # puts "could not obtain pty" + # end + # end + def request_pty(opts={}, &block) + extra = opts.keys - VALID_PTY_OPTIONS.keys + raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any? + + opts = VALID_PTY_OPTIONS.merge(opts) + + modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)| + memo.write_byte(mode).write_long(data) + end + # mark the end of the mode opcode list with a 0 byte + modes.write_byte(0) + + send_channel_request("pty-req", :string, opts[:term], + :long, opts[:chars_wide], :long, opts[:chars_high], + :long, opts[:pixels_wide], :long, opts[:pixels_high], + :string, modes.to_s, &block) + end + + # Sends data to the channel's remote endpoint. This usually has the + # effect of sending the given string to the remote process' stdin stream. + # Note that it does not immediately send the data across the channel, + # but instead merely appends the given data to the channel's output buffer, + # preparatory to being packaged up and sent out the next time the connection + # is accepting data. (A connection might not be accepting data if, for + # instance, it has filled its data window and has not yet been resized by + # the remote end-point.) + # + # This will raise an exception if the channel has previously declared + # that no more data will be sent (see #eof!). + # + # channel.send_data("the password\n") + def send_data(data) + raise EOFError, "cannot send data if channel has declared eof" if eof? + output.append(data.to_s) + end + + # Returns true if the channel exists in the channel list of the session, + # and false otherwise. This can be used to determine whether a channel has + # been closed or not. + # + # ssh.loop { channel.active? } + def active? + connection.channels.key?(local_id) + end + + # Runs the SSH event loop until the channel is no longer active. This is + # handy for blocking while you wait for some channel to finish. + # + # channel.exec("grep ...") { ... } + # channel.wait + def wait + connection.loop { active? } + end + + # Returns true if the channel is currently closing, but not actually + # closed. A channel is closing when, for instance, #close has been + # invoked, but the server has not yet responded with a CHANNEL_CLOSE + # packet of its own. + def closing? + @closing + end + + # Requests that the channel be closed. If the channel is already closing, + # this does nothing, nor does it do anything if the channel has not yet + # been confirmed open (see #do_open_confirmation). Otherwise, it sends a + # CHANNEL_CLOSE message and marks the channel as closing. + def close + return if @closing + if remote_id + @closing = true + connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id)) + end + end + + # Returns true if the local end of the channel has declared that no more + # data is forthcoming (see #eof!). Trying to send data via #send_data when + # this is true will result in an exception being raised. + def eof? + @eof + end + + # Tells the remote end of the channel that no more data is forthcoming + # from this end of the channel. The remote end may still send data. + def eof! + return if eof? + @eof = true + connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id)) + end + + # If an #on_process handler has been set up, this will cause it to be + # invoked (passing the channel itself as an argument). It also causes all + # pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output). + def process + @on_process.call(self) if @on_process + enqueue_pending_output + end + + # Registers a callback to be invoked when data packets are received by the + # channel. The callback is called with the channel as the first argument, + # and the data as the second. + # + # channel.on_data do |ch, data| + # puts "got data: #{data.inspect}" + # end + # + # Data received this way is typically the data written by the remote + # process to its +stdout+ stream. + def on_data(&block) + old, @on_data = @on_data, block + old + end + + # Registers a callback to be invoked when extended data packets are received + # by the channel. The callback is called with the channel as the first + # argument, the data type (as an integer) as the second, and the data as + # the third. Extended data is almost exclusively used to send +stderr+ data + # (+type+ == 1). Other extended data types are not defined by the SSH + # protocol. + # + # channel.on_extended_data do |ch, type, data| + # puts "got stderr: #{data.inspect}" + # end + def on_extended_data(&block) + old, @on_extended_data = @on_extended_data, block + old + end + + # Registers a callback to be invoked for each pass of the event loop for + # this channel. There are no guarantees on timeliness in the event loop, + # but it will be called roughly once for each packet received by the + # connection (not the channel). This callback is invoked with the channel + # as the sole argument. + # + # Here's an example that accumulates the channel data into a variable on + # the channel itself, and displays individual lines in the input one + # at a time when the channel is processed: + # + # channel[:data] = "" + # + # channel.on_data do |ch, data| + # channel[:data] << data + # end + # + # channel.on_process do |ch| + # if channel[:data] =~ /^.*?\n/ + # puts $& + # channel[:data] = $' + # end + # end + def on_process(&block) + old, @on_process = @on_process, block + old + end + + # Registers a callback to be invoked when the server acknowledges that a + # channel is closed. This is invoked with the channel as the sole argument. + # + # channel.on_close do |ch| + # puts "remote end is closing!" + # end + def on_close(&block) + old, @on_close = @on_close, block + old + end + + # Registers a callback to be invoked when the server indicates that no more + # data will be sent to the channel (although the channel can still send + # data to the server). The channel is the sole argument to the callback. + # + # channel.on_eof do |ch| + # puts "remote end is done sending data" + # end + def on_eof(&block) + old, @on_eof = @on_eof, block + old + end + + # Registers a callback to be invoked when the server was unable to open + # the requested channel. The channel itself will be passed to the block, + # along with the integer "reason code" for the failure, and a textual + # description of the failure from the server. + # + # channel = session.open_channel do |ch| + # # .. + # end + # + # channel.on_open_failed { |ch, code, desc| ... } + def on_open_failed(&block) + old, @on_open_failed = @on_open_failed, block + old + end + + # Registers a callback to be invoked when a channel request of the given + # type is received. The callback will receive the channel as the first + # argument, and the associated (unparsed) data as the second. The data + # will be a Net::SSH::Buffer that you will need to parse, yourself, + # according to the kind of request you are watching. + # + # By default, if the request wants a reply, Net::SSH will send a + # CHANNEL_SUCCESS response for any request that was handled by a registered + # callback, and CHANNEL_FAILURE for any that wasn't, but if you want your + # registered callback to result in a CHANNEL_FAILURE response, just raise + # Net::SSH::ChannelRequestFailed. + # + # Some common channel requests that your programs might want to listen + # for are: + # + # * "exit-status" : the exit status of the remote process will be reported + # as a long integer in the data buffer, which you can grab via + # data.read_long. + # * "exit-signal" : if the remote process died as a result of a signal + # being sent to it, the signal will be reported as a string in the + # data, via data.read_string. (Not all SSH servers support this channel + # request type.) + # + # channel.on_request "exit-status" do |ch, data| + # puts "process terminated with exit status: #{data.read_long}" + # end + def on_request(type, &block) + old, @on_request[type] = @on_request[type], block + old + end + + # Sends a new channel request with the given name. The extra +data+ + # parameter must either be empty, or consist of an even number of + # arguments. See Net::SSH::Buffer.from for a description of their format. + # If a block is given, it is registered as a callback for a pending + # request, and the packet will be flagged so that the server knows a + # reply is required. If no block is given, the server will send no + # response to this request. Responses, where required, will cause the + # callback to be invoked with the channel as the first argument, and + # either true or false as the second, depending on whether the request + # succeeded or not. The meaning of "success" and "failure" in this context + # is dependent on the specific request that was sent. + # + # channel.send_channel_request "shell" do |ch, success| + # if success + # puts "user shell started successfully" + # else + # puts "could not start user shell" + # end + # end + # + # Most channel requests you'll want to send are already wrapped in more + # convenient helper methods (see #exec and #subsystem). + def send_channel_request(request_name, *data, &callback) + info { "sending channel request #{request_name.inspect}" } + msg = Buffer.from(:byte, CHANNEL_REQUEST, + :long, remote_id, :string, request_name, + :bool, !callback.nil?, *data) + connection.send_message(msg) + pending_requests << callback if callback + end + + public # these methods are public, but for Net::SSH internal use only + + # Enqueues pending output at the connection as CHANNEL_DATA packets. This + # does nothing if the channel has not yet been confirmed open (see + # #do_open_confirmation). This is called automatically by #process, which + # is called from the event loop (Connection::Session#process). You will + # generally not need to invoke it directly. + def enqueue_pending_output #:nodoc: + return unless remote_id + + while output.length > 0 + length = output.length + length = remote_window_size if length > remote_window_size + length = remote_maximum_packet_size if length > remote_maximum_packet_size + + if length > 0 + connection.send_message(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length))) + output.consume! + @remote_window_size -= length + else + break + end + end + end + + # Invoked when the server confirms that a channel has been opened. + # The remote_id is the id of the channel as assigned by the remote host, + # and max_window and max_packet are the maximum window and maximum + # packet sizes, respectively. If an open-confirmation callback was + # given when the channel was created, it is invoked at this time with + # the channel itself as the sole argument. + def do_open_confirmation(remote_id, max_window, max_packet) #:nodoc: + @remote_id = remote_id + @remote_window_size = @remote_maximum_window_size = max_window + @remote_maximum_packet_size = max_packet + connection.forward.agent(self) if connection.options[:forward_agent] && type == "session" + @on_confirm_open.call(self) if @on_confirm_open + end + + # Invoked when the server failed to open the channel. If an #on_open_failed + # callback was specified, it will be invoked with the channel, reason code, + # and description as arguments. Otherwise, a ChannelOpenFailed exception + # will be raised. + def do_open_failed(reason_code, description) + if @on_open_failed + @on_open_failed.call(self, reason_code, description) + else + raise ChannelOpenFailed.new(reason_code, description) + end + end + + # Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and + # causes the remote window size to be adjusted upwards by the given + # number of bytes. This has the effect of allowing more data to be sent + # from the local end to the remote end of the channel. + def do_window_adjust(bytes) #:nodoc: + @remote_maximum_window_size += bytes + @remote_window_size += bytes + end + + # Invoked when the server sends a channel request. If any #on_request + # callback has been registered for the specific type of this request, + # it is invoked. If +want_reply+ is true, a packet will be sent of + # either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback + # to handle the request, CHANNEL_FAILURE will be sent. Otherwise, + # CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The + # callback should accept the channel as the first argument, and the + # request-specific data as the second. + def do_request(request, want_reply, data) #:nodoc: + result = true + + begin + callback = @on_request[request] or raise ChannelRequestFailed + callback.call(self, data) + rescue ChannelRequestFailed + result = false + end + + if want_reply + msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id) + connection.send_message(msg) + end + end + + # Invokes the #on_data callback when the server sends data to the + # channel. This will reduce the available window size on the local end, + # but does not actually throttle requests that come in illegally when + # the window size is too small. The callback is invoked with the channel + # as the first argument, and the data as the second. + def do_data(data) #:nodoc: + update_local_window_size(data.length) + @on_data.call(self, data) if @on_data + end + + # Invokes the #on_extended_data callback when the server sends + # extended data to the channel. This will reduce the available window + # size on the local end. The callback is invoked with the channel, + # type, and data. + def do_extended_data(type, data) + update_local_window_size(data.length) + @on_extended_data.call(self, type, data) if @on_extended_data + end + + # Invokes the #on_eof callback when the server indicates that no + # further data is forthcoming. The callback is invoked with the channel + # as the argument. + def do_eof + @on_eof.call(self) if @on_eof + end + + # Invokes the #on_close callback when the server closes a channel. + # The channel is the only argument. + def do_close + @on_close.call(self) if @on_close + end + + # Invokes the next pending request callback with +false+ as the second + # argument. + def do_failure + if callback = pending_requests.shift + callback.call(self, false) + else + error { "channel failure recieved with no pending request to handle it (bug?)" } + end + end + + # Invokes the next pending request callback with +true+ as the second + # argument. + def do_success + if callback = pending_requests.shift + callback.call(self, true) + else + error { "channel success recieved with no pending request to handle it (bug?)" } + end + end + + private + + # Updates the local window size by the given amount. If the window + # size drops to less than half of the local maximum (an arbitrary + # threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the + # server telling it that the window size has grown. + def update_local_window_size(size) + @local_window_size -= size + if local_window_size < local_maximum_window_size/2 + connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST, + :long, remote_id, :long, 0x20000)) + @local_window_size += 0x20000 + @local_maximum_window_size += 0x20000 + end + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/constants.rb File: constants.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/constants.rb;tzinfo2 @@ -1,0 +1,33 @@ +module Net; module SSH; module Connection + + # Definitions of constants that are specific to the connection layer of the + # SSH protocol. + module Constants + + #-- + # Connection protocol generic messages + #++ + + GLOBAL_REQUEST = 80 + REQUEST_SUCCESS = 81 + REQUEST_FAILURE = 82 + + #-- + # Channel related messages + #++ + + CHANNEL_OPEN = 90 + CHANNEL_OPEN_CONFIRMATION = 91 + CHANNEL_OPEN_FAILURE = 92 + CHANNEL_WINDOW_ADJUST = 93 + CHANNEL_DATA = 94 + CHANNEL_EXTENDED_DATA = 95 + CHANNEL_EOF = 96 + CHANNEL_CLOSE = 97 + CHANNEL_REQUEST = 98 + CHANNEL_SUCCESS = 99 + CHANNEL_FAILURE = 100 + + end + +end; end end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/session.rb File: session.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/session.rb;tzinfo2 @@ -1,0 +1,596 @@ +require 'net/ssh/loggable' +require 'net/ssh/connection/channel' +require 'net/ssh/connection/constants' +require 'net/ssh/service/forward' + +module Net; module SSH; module Connection + + # A session class representing the connection service running on top of + # the SSH transport layer. It manages the creation of channels (see + # #open_channel), and the dispatching of messages to the various channels. + # It also encapsulates the SSH event loop (via #loop and #process), + # and serves as a central point-of-reference for all SSH-related services (e.g. + # port forwarding, SFTP, SCP, etc.). + # + # You will rarely (if ever) need to instantiate this class directly; rather, + # you'll almost always use Net::SSH.start to initialize a new network + # connection, authenticate a user, and return a new connection session, + # all in one call. + # + # Net::SSH.start("localhost", "user") do |ssh| + # # 'ssh' is an instance of Net::SSH::Connection::Session + # ssh.exec! "/etc/init.d/some_process start" + # end + class Session + include Constants, Loggable + + # The underlying transport layer abstraction (see Net::SSH::Transport::Session). + attr_reader :transport + + # The map of options that were used to initialize this instance. + attr_reader :options + + # The collection of custom properties for this instance. (See #[] and #[]=). + attr_reader :properties + + # The map of channels, each key being the local-id for the channel. + attr_reader :channels #:nodoc: + + # The map of listeners that the event loop knows about. See #listen_to. + attr_reader :listeners #:nodoc: + + # The map of specialized handlers for opening specific channel types. See + # #on_open_channel. + attr_reader :channel_open_handlers #:nodoc: + + # The list of callbacks for pending requests. See #send_global_request. + attr_reader :pending_requests #:nodoc: + + class NilChannel + def initialize(session) + @session = session + end + + def method_missing(sym, *args) + @session.lwarn { "ignoring request #{sym.inspect} for non-existent (closed?) channel; probably ssh server bug" } + end + end + + # Create a new connection service instance atop the given transport + # layer. Initializes the listeners to be only the underlying socket object. + def initialize(transport, options={}) + self.logger = transport.logger + + @transport = transport + @options = options + + @channel_id_counter = -1 + @channels = Hash.new(NilChannel.new(self)) + @listeners = { transport.socket => nil } + @pending_requests = [] + @channel_open_handlers = {} + @on_global_request = {} + @properties = (options[:properties] || {}).dup + end + + # Retrieves a custom property from this instance. This can be used to + # store additional state in applications that must manage multiple + # SSH connections. + def [](key) + @properties[key] + end + + # Sets a custom property for this instance. + def []=(key, value) + @properties[key] = value + end + + # Returns the name of the host that was given to the transport layer to + # connect to. + def host + transport.host + end + + # Returns true if the underlying transport has been closed. Note that + # this can be a little misleading, since if the remote server has + # closed the connection, the local end will still think it is open + # until the next operation on the socket. Nevertheless, this method can + # be useful if you just want to know if _you_ have closed the connection. + def closed? + transport.closed? + end + + # Closes the session gracefully, blocking until all channels have + # successfully closed, and then closes the underlying transport layer + # connection. + def close + info { "closing remaining channels (#{channels.length} open)" } + channels.each { |id, channel| channel.close } + loop { channels.any? } + transport.close + end + + # Performs a "hard" shutdown of the connection. In general, this should + # never be done, but it might be necessary (in a rescue clause, for instance, + # when the connection needs to close but you don't know the status of the + # underlying protocol's state). + def shutdown! + transport.shutdown! + end + + # preserve a reference to Kernel#loop + alias :loop_forever :loop + + # Returns +true+ if there are any channels currently active on this + # session. By default, this will not include "invisible" channels + # (such as those created by forwarding ports and such), but if you pass + # a +true+ value for +include_invisible+, then those will be counted. + # + # This can be useful for determining whether the event loop should continue + # to be run. + # + # ssh.loop { ssh.busy? } + def busy?(include_invisible=false) + if include_invisible + channels.any? + else + channels.any? { |id, ch| !ch[:invisible] } + end + end + + # The main event loop. Calls #process until #process returns false. If a + # block is given, it is passed to #process, otherwise a default proc is + # used that just returns true if there are any channels active (see #busy?). + # The # +wait+ parameter is also passed through to #process (where it is + # interpreted as the maximum number of seconds to wait for IO.select to return). + # + # # loop for as long as there are any channels active + # ssh.loop + # + # # loop for as long as there are any channels active, but make sure + # # the event loop runs at least once per 0.1 second + # ssh.loop(0.1) + # + # # loop until ctrl-C is pressed + # int_pressed = false + # trap("INT") { int_pressed = true } + # ssh.loop(0.1) { not int_pressed } + def loop(wait=nil, &block) + running = block || Proc.new { busy? } + loop_forever { break unless process(wait, &running) } + end + + # The core of the event loop. It processes a single iteration of the event + # loop. If a block is given, it should return false when the processing + # should abort, which causes #process to return false. Otherwise, + # #process returns true. The session itself is yielded to the block as its + # only argument. + # + # If +wait+ is nil (the default), this method will block until any of the + # monitored IO objects are ready to be read from or written to. If you want + # it to not block, you can pass 0, or you can pass any other numeric value + # to indicate that it should block for no more than that many seconds. + # Passing 0 is a good way to poll the connection, but if you do it too + # frequently it can make your CPU quite busy! + # + # This will also cause all active channels to be processed once each (see + # Net::SSH::Connection::Channel#on_process). + # + # # process multiple Net::SSH connections in parallel + # connections = [ + # Net::SSH.start("host1", ...), + # Net::SSH.start("host2", ...) + # ] + # + # connections.each do |ssh| + # ssh.exec "grep something /in/some/files" + # end + # + # condition = Proc.new { |s| s.busy? } + # + # loop do + # connections.delete_if { |ssh| !ssh.process(0.1, &condition) } + # break if connections.empty? + # end + def process(wait=nil, &block) + return false unless preprocess(&block) + + r = listeners.keys + w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? } + readers, writers, = IO.select(r, w, nil, wait) + + postprocess(readers, writers) + end + + # This is called internally as part of #process. It dispatches any + # available incoming packets, and then runs Net::SSH::Connection::Channel#process + # for any active channels. If a block is given, it is invoked at the + # start of the method and again at the end, and if the block ever returns + # false, this method returns false. Otherwise, it returns true. + def preprocess + return false if block_given? && !yield(self) + dispatch_incoming_packets + channels.each { |id, channel| channel.process unless channel.closing? } + return false if block_given? && !yield(self) + return true + end + + # This is called internally as part of #process. It loops over the given + # arrays of reader IO's and writer IO's, processing them as needed, and + # then calls Net::SSH::Transport::Session#rekey_as_needed to allow the + # transport layer to rekey. Then returns true. + def postprocess(readers, writers) + Array(readers).each do |reader| + if listeners[reader] + listeners[reader].call(reader) + else + if reader.fill.zero? + reader.close + stop_listening_to(reader) + end + end + end + + Array(writers).each do |writer| + writer.send_pending + end + + transport.rekey_as_needed + + return true + end + + # Send a global request of the given type. The +extra+ parameters must + # be even in number, and conform to the same format as described for + # Net::SSH::Buffer.from. If a callback is not specified, the request will + # not require a response from the server, otherwise the server is required + # to respond and indicate whether the request was successful or not. This + # success or failure is indicated by the callback being invoked, with the + # first parameter being true or false (success, or failure), and the second + # being the packet itself. + # + # Generally, Net::SSH will manage global requests that need to be sent + # (e.g. port forward requests and such are handled in the Net::SSH::Service::Forward + # class, for instance). However, there may be times when you need to + # send a global request that isn't explicitly handled by Net::SSH, and so + # this method is available to you. + # + # ssh.send_global_request("keep-alive@openssh.com") + def send_global_request(type, *extra, &callback) + info { "sending global request #{type}" } + msg = Buffer.from(:byte, GLOBAL_REQUEST, :string, type.to_s, :bool, !callback.nil?, *extra) + send_message(msg) + pending_requests << callback if callback + self + end + + # Requests that a new channel be opened. By default, the channel will be + # of type "session", but if you know what you're doing you can select any + # of the channel types supported by the SSH protocol. The +extra+ parameters + # must be even in number and conform to the same format as described for + # Net::SSH::Buffer.from. If a callback is given, it will be invoked when + # the server confirms that the channel opened successfully. The sole parameter + # for the callback is the channel object itself. + # + # In general, you'll use #open_channel without any arguments; the only + # time you'd want to set the channel type or pass additional initialization + # data is if you were implementing an SSH extension. + # + # channel = ssh.open_channel do |ch| + # ch.exec "grep something /some/files" do |ch, success| + # ... + # end + # end + # + # channel.wait + def open_channel(type="session", *extra, &on_confirm) + local_id = get_next_channel_id + channel = Channel.new(self, type, local_id, &on_confirm) + + msg = Buffer.from(:byte, CHANNEL_OPEN, :string, type, :long, local_id, + :long, channel.local_maximum_window_size, + :long, channel.local_maximum_packet_size, *extra) + send_message(msg) + + channels[local_id] = channel + end + + # A convenience method for executing a command and interacting with it. If + # no block is given, all output is printed via $stdout and $stderr. Otherwise, + # the block is called for each data and extended data packet, with three + # arguments: the channel object, a symbol indicating the data type + # (:stdout or :stderr), and the data (as a string). + # + # Note that this method returns immediately, and requires an event loop + # (see Session#loop) in order for the command to actually execute. + # + # This is effectively identical to calling #open_channel, and then + # Net::SSH::Connection::Channel#exec, and then setting up the channel + # callbacks. However, for most uses, this will be sufficient. + # + # ssh.exec "grep something /some/files" do |ch, stream, data| + # if stream == :stderr + # puts "ERROR: #{data}" + # else + # puts data + # end + # end + def exec(command, &block) + open_channel do |channel| + channel.exec(command) do |ch, success| + raise "could not execute command: #{command.inspect}" unless success + + channel.on_data do |ch2, data| + if block + block.call(ch2, :stdout, data) + else + $stdout.print(data) + end + end + + channel.on_extended_data do |ch2, type, data| + if block + block.call(ch2, :stderr, data) + else + $stderr.print(data) + end + end + end + end + end + + # Same as #exec, except this will block until the command finishes. Also, + # if a block is not given, this will return all output (stdout and stderr) + # as a single string. + # + # matches = ssh.exec!("grep something /some/files") + def exec!(command, &block) + block ||= Proc.new do |ch, type, data| + ch[:result] ||= "" + ch[:result] << data + end + + channel = exec(command, &block) + channel.wait + + return channel[:result] + end + + # Enqueues a message to be sent to the server as soon as the socket is + # available for writing. Most programs will never need to call this, but + # if you are implementing an extension to the SSH protocol, or if you + # need to send a packet that Net::SSH does not directly support, you can + # use this to send it. + # + # ssh.send_message(Buffer.from(:byte, REQUEST_SUCCESS).to_s) + def send_message(message) + transport.enqueue_message(message) + end + + # Adds an IO object for the event loop to listen to. If a callback + # is given, it will be invoked when the io is ready to be read, otherwise, + # the io will merely have its #fill method invoked. + # + # Any +io+ value passed to this method _must_ have mixed into it the + # Net::SSH::BufferedIo functionality, typically by calling #extend on the + # object. + # + # The following example executes a process on the remote server, opens + # a socket to somewhere, and then pipes data from that socket to the + # remote process' stdin stream: + # + # channel = ssh.open_channel do |ch| + # ch.exec "/some/process/that/wants/input" do |ch, success| + # abort "can't execute!" unless success + # + # io = TCPSocket.new(somewhere, port) + # io.extend(Net::SSH::BufferedIo) + # ssh.listen_to(io) + # + # ch.on_process do + # if io.available > 0 + # ch.send_data(io.read_available) + # end + # end + # + # ch.on_close do + # ssh.stop_listening_to(io) + # io.close + # end + # end + # end + # + # channel.wait + def listen_to(io, &callback) + listeners[io] = callback + end + + # Removes the given io object from the listeners collection, so that the + # event loop will no longer monitor it. + def stop_listening_to(io) + listeners.delete(io) + end + + # Returns a reference to the Net::SSH::Service::Forward service, which can + # be used for forwarding ports over SSH. + def forward + @forward ||= Service::Forward.new(self) + end + + # Registers a handler to be invoked when the server wants to open a + # channel on the client. The callback receives the connection object, + # the new channel object, and the packet itself as arguments, and should + # raise ChannelOpenFailed if it is unable to open the channel for some + # reason. Otherwise, the channel will be opened and a confirmation message + # sent to the server. + # + # This is used by the Net::SSH::Service::Forward service to open a channel + # when a remote forwarded port receives a connection. However, you are + # welcome to register handlers for other channel types, as needed. + def on_open_channel(type, &block) + channel_open_handlers[type] = block + end + + # Registers a handler to be invoked when the server sends a global request + # of the given type. The callback receives the request data as the first + # parameter, and true/false as the second (indicating whether a response + # is required). If the callback sends the response, it should return + # :sent. Otherwise, if it returns true, REQUEST_SUCCESS will be sent, and + # if it returns false, REQUEST_FAILURE will be sent. + def on_global_request(type, &block) + old, @on_global_request[type] = @on_global_request[type], block + old + end + + private + + # Read all pending packets from the connection and dispatch them as + # appropriate. Returns as soon as there are no more pending packets. + def dispatch_incoming_packets + while packet = transport.poll_message + unless MAP.key?(packet.type) + raise Net::SSH::Exception, "unexpected response #{packet.type} (#{packet.inspect})" + end + + send(MAP[packet.type], packet) + end + end + + # Returns the next available channel id to be assigned, and increments + # the counter. + def get_next_channel_id + @channel_id_counter += 1 + end + + # Invoked when a global request is received. The registered global + # request callback will be invoked, if one exists, and the necessary + # reply returned. + def global_request(packet) + info { "global request received: #{packet[:request_type]} #{packet[:want_reply]}" } + callback = @on_global_request[packet[:request_type]] + result = callback ? callback.call(packet[:request_data], packet[:want_reply]) : false + + if result != :sent && result != true && result != false + raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}" + end + + if packet[:want_reply] && result != :sent + msg = Buffer.from(:byte, result ? REQUEST_SUCCESS : REQUEST_FAILURE) + send_message(msg) + end + end + + # Invokes the next pending request callback with +true+. + def request_success(packet) + info { "global request success" } + callback = pending_requests.shift + callback.call(true, packet) if callback + end + + # Invokes the next pending request callback with +false+. + def request_failure(packet) + info { "global request failure" } + callback = pending_requests.shift + callback.call(false, packet) if callback + end + + # Called when the server wants to open a channel. If no registered + # channel handler exists for the given channel type, CHANNEL_OPEN_FAILURE + # is returned, otherwise the callback is invoked and everything proceeds + # accordingly. + def channel_open(packet) + info { "channel open #{packet[:channel_type]}" } + + local_id = get_next_channel_id + channel = Channel.new(self, packet[:channel_type], local_id) + channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size]) + + callback = channel_open_handlers[packet[:channel_type]] + + if callback + begin + callback[self, channel, packet] + rescue ChannelOpenFailed => err + failure = [err.code, err.reason] + else + channels[local_id] = channel + msg = Buffer.from(:byte, CHANNEL_OPEN_CONFIRMATION, :long, channel.remote_id, :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size) + end + else + failure = [3, "unknown channel type #{channel.type}"] + end + + if failure + error { failure.inspect } + msg = Buffer.from(:byte, CHANNEL_OPEN_FAILURE, :long, channel.remote_id, :long, failure[0], :string, failure[1], :string, "") + end + + send_message(msg) + end + + def channel_open_confirmation(packet) + info { "channel_open_confirmation: #{packet[:local_id]} #{packet[:remote_id]} #{packet[:window_size]} #{packet[:packet_size]}" } + channel = channels[packet[:local_id]] + channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size]) + end + + def channel_open_failure(packet) + error { "channel_open_failed: #{packet[:local_id]} #{packet[:reason_code]} #{packet[:description]}" } + channel = channels.delete(packet[:local_id]) + channel.do_open_failed(packet[:reason_code], packet[:description]) + end + + def channel_window_adjust(packet) + info { "channel_window_adjust: #{packet[:local_id]} +#{packet[:extra_bytes]}" } + channels[packet[:local_id]].do_window_adjust(packet[:extra_bytes]) + end + + def channel_request(packet) + info { "channel_request: #{packet[:local_id]} #{packet[:request]} #{packet[:want_reply]}" } + channels[packet[:local_id]].do_request(packet[:request], packet[:want_reply], packet[:request_data]) + end + + def channel_data(packet) + info { "channel_data: #{packet[:local_id]} #{packet[:data].length}b" } + channels[packet[:local_id]].do_data(packet[:data]) + end + + def channel_extended_data(packet) + info { "channel_extended_data: #{packet[:local_id]} #{packet[:data_type]} #{packet[:data].length}b" } + channels[packet[:local_id]].do_extended_data(packet[:data_type], packet[:data]) + end + + def channel_eof(packet) + info { "channel_eof: #{packet[:local_id]}" } + channels[packet[:local_id]].do_eof + end + + def channel_close(packet) + info { "channel_close: #{packet[:local_id]}" } + + channel = channels[packet[:local_id]] + channel.close + + channels.delete(packet[:local_id]) + channel.do_close + end + + def channel_success(packet) + info { "channel_success: #{packet[:local_id]}" } + channels[packet[:local_id]].do_success + end + + def channel_failure(packet) + info { "channel_failure: #{packet[:local_id]}" } + channels[packet[:local_id]].do_failure + end + + MAP = Constants.constants.inject({}) do |memo, name| + value = const_get(name) + next unless Integer === value + memo[value] = name.downcase.to_sym + memo + end + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/term.rb File: term.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/connection/term.rb;tzinfo2 @@ -1,0 +1,178 @@ +module Net; module SSH; module Connection + + # These constants are used when requesting a pseudo-terminal (via + # Net::SSH::Connection::Channel#request_pty). The descriptions for each are + # taken directly from RFC 4254 ("The Secure Shell (SSH) Connection Protocol"), + # http://tools.ietf.org/html/rfc4254. + module Term + # Interrupt character; 255 if none. Similarly for the other characters. + # Not all of these characters are supported on all systems. + VINTR = 1 + + # The quit character (sends SIGQUIT signal on POSIX systems). + VQUIT = 2 + + # Erase the character to left of the cursor. + VERASE = 3 + + # Kill the current input line. + VKILL = 4 + + # End-of-file character (sends EOF from the terminal). + VEOF = 5 + + # End-of-line character in addition to carriage return and/or linefeed. + VEOL = 6 + + # Additional end-of-line character. + VEOL2 = 7 + + # Continues paused output (normally control-Q). + VSTART = 8 + + # Pauses output (normally control-S). + VSTOP = 9 + + # Suspends the current program. + VSUSP = 10 + + # Another suspend character. + VDSUSP = 11 + + # Reprints the current input line. + VREPRINT = 12 + + # Erases a word left of cursor. + VWERASE = 13 + + # Enter the next character typed literally, even if it is a special + # character. + VLNEXT = 14 + + # Character to flush output. + VFLUSH = 15 + + # Switch to a different shell layer. + VSWITCH = 16 + + # Prints system status line (load, command, pid, etc). + VSTATUS = 17 + + # Toggles the flushing of terminal output. + VDISCARD = 18 + + # The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, + # and 1 if it is TRUE. + IGNPAR = 30 + + # Mark parity and framing errors. + PARMRK = 31 + + # Enable checking of parity errors. + INPCK = 32 + + # Strip 8th bit off characters. + ISTRIP = 33 + + # Map NL into CR on input. + INCLR = 34 + + # Ignore CR on input. + IGNCR = 35 + + # Map CR to NL on input. + ICRNL = 36 + + # Translate uppercase characters to lowercase. + IUCLC = 37 + + # Enable output flow control. + IXON = 38 + + # Any char will restart after stop. + IXANY = 39 + + # Enable input flow control. + IXOFF = 40 + + # Ring bell on input queue full. + IMAXBEL = 41 + + # Enable signals INTR, QUIT, [D]SUSP. + ISIG = 50 + + # Canonicalize input lines. + ICANON = 51 + + # Enable input and output of uppercase characters by preceding their + # lowercase equivalents with "\". + XCASE = 52 + + # Enable echoing. + ECHO = 53 + + # Visually erase chars. + ECHOE = 54 + + # Kill character discards current line. + ECHOK = 55 + + # Echo NL even if ECHO is off. + ECHONL = 56 + + # Don't flush after interrupt. + NOFLSH = 57 + + # Stop background jobs from output. + TOSTOP= 58 + + # Enable extensions. + IEXTEN = 59 + + # Echo control characters as ^(Char). + ECHOCTL = 60 + + # Visual erase for line kill. + ECHOKE = 61 + + # Retype pending input. + PENDIN = 62 + + # Enable output processing. + OPOST = 70 + + # Convert lowercase to uppercase. + OLCUC = 71 + + # Map NL to CR-NL. + ONLCR = 72 + + # Translate carriage return to newline (output). + OCRNL = 73 + + # Translate newline to carriage return-newline (output). + ONOCR = 74 + + # Newline performs a carriage return (output). + ONLRET = 75 + + # 7 bit mode. + CS7 = 90 + + # 8 bit mode. + CS8 = 91 + + # Parity enable. + PARENB = 92 + + # Odd parity, else even. + PARODD = 93 + + # Specifies the input baud rate in bits per second. + TTY_OP_ISPEED = 128 + + # Specifies the output baud rate in bits per second. + TTY_OP_OSPEED = 129 + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/errors.rb File: errors.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/errors.rb;tzinfo2 @@ -1,0 +1,14 @@ +require 'net/ssh/errors' + +module Net; module SSH; module Proxy + + # A general exception class for all Proxy errors. + class Error < Net::SSH::Exception; end + + # Used for reporting proxy connection errors. + class ConnectError < Error; end + + # Used when the server doesn't recognize the user's credentials. + class UnauthorizedError < Error; end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/http.rb File: http.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/http.rb;tzinfo2 @@ -1,0 +1,94 @@ +require 'socket' +require 'net/ssh/proxy/errors' + +module Net; module SSH; module Proxy + + # An implementation of an HTTP proxy. To use it, instantiate it, then + # pass the instantiated object via the :proxy key to Net::SSH.start: + # + # require 'net/ssh/proxy/http' + # + # proxy = Net::SSH::Proxy::HTTP.new('proxy.host', proxy_port) + # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| + # ... + # end + # + # If the proxy requires authentication, you can pass :user and :password + # to the proxy's constructor: + # + # proxy = Net::SSH::Proxy::HTTP.new('proxy.host', proxy_port, + # :user => "user", :password => "password") + # + # Note that HTTP digest authentication is not supported; Basic only at + # this point. + class HTTP + + # The hostname or IP address of the HTTP proxy. + attr_reader :proxy_host + + # The port number of the proxy. + attr_reader :proxy_port + + # The map of additional options that were given to the object at + # initialization. + attr_reader :options + + # Create a new socket factory that tunnels via the given host and + # port. The +options+ parameter is a hash of additional settings that + # can be used to tweak this proxy connection. Specifically, the following + # options are supported: + # + # * :user => the user name to use when authenticating to the proxy + # * :password => the password to use when authenticating + def initialize(proxy_host, proxy_port=80, options={}) + @proxy_host = proxy_host + @proxy_port = proxy_port + @options = options + end + + # Return a new socket connected to the given host and port via the + # proxy that was requested when the socket factory was instantiated. + def open(host, port) + socket = TCPSocket.new(proxy_host, proxy_port) + socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n" + + if options[:user] + credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "") + socket.write "Proxy-Authorization: Basic #{credentials}\r\n" + end + + socket.write "\r\n" + + resp = parse_response(socket) + + return socket if resp[:code] == 200 + + socket.close + raise ConnectError, resp.inspect + end + + private + + def parse_response(socket) + version, code, reason = socket.gets.chomp.split(/ /, 3) + headers = {} + + while (line = socket.gets.chomp) != "" + name, value = line.split(/:/, 2) + headers[name.strip] = value.strip + end + + if headers["Content-Length"] + body = socket.read(headers["Content-Length"].to_i) + end + + return { :version => version, + :code => code.to_i, + :reason => reason, + :headers => headers, + :body => body } + end + + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/socks4.rb File: socks4.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/socks4.rb;tzinfo2 @@ -1,0 +1,70 @@ +require 'socket' +require 'resolv' +require 'ipaddr' +require 'net/ssh/proxy/errors' + +module Net + module SSH + module Proxy + + # An implementation of a SOCKS4 proxy. To use it, instantiate it, then + # pass the instantiated object via the :proxy key to Net::SSH.start: + # + # require 'net/ssh/proxy/socks4' + # + # proxy = Net::SSH::Proxy::SOCKS4.new('proxy.host', proxy_port, :user => 'user') + # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| + # ... + # end + class SOCKS4 + + # The SOCKS protocol version used by this class + VERSION = 4 + + # The packet type for connection requests + CONNECT = 1 + + # The status code for a successful connection + GRANTED = 90 + + # The proxy's host name or IP address, as given to the constructor. + attr_reader :proxy_host + + # The proxy's port number. + attr_reader :proxy_port + + # The additional options that were given to the proxy's constructor. + attr_reader :options + + # Create a new proxy connection to the given proxy host and port. + # Optionally, a :user key may be given to identify the username + # with which to authenticate. + def initialize(proxy_host, proxy_port=1080, options={}) + @proxy_host = proxy_host + @proxy_port = proxy_port + @options = options + end + + # Return a new socket connected to the given host and port via the + # proxy that was requested when the socket factory was instantiated. + def open(host, port) + socket = TCPSocket.new(proxy_host, proxy_port) + ip_addr = IPAddr.new(Resolv.getaddress(host)) + + packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*") + socket.send packet, 0 + + version, status, port, ip = socket.recv(8).unpack("CCnN") + if status != GRANTED + socket.close + raise ConnectError, "error connecting to proxy (#{status})" + end + + return socket + end + + end + + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/socks5.rb File: socks5.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/proxy/socks5.rb;tzinfo2 @@ -1,0 +1,142 @@ +require 'socket' +require 'net/ssh/ruby_compat' +require 'net/ssh/proxy/errors' + +module Net + module SSH + module Proxy + + # An implementation of a SOCKS5 proxy. To use it, instantiate it, then + # pass the instantiated object via the :proxy key to Net::SSH.start: + # + # require 'net/ssh/proxy/socks5' + # + # proxy = Net::SSH::Proxy::SOCKS5.new('proxy.host', proxy_port, + # :user => 'user', :password => "password") + # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| + # ... + # end + class SOCKS5 + # The SOCKS protocol version used by this class + VERSION = 5 + + # The SOCKS authentication type for requests without authentication + METHOD_NO_AUTH = 0 + + # The SOCKS authentication type for requests via username/password + METHOD_PASSWD = 2 + + # The SOCKS authentication type for when there are no supported + # authentication methods. + METHOD_NONE = 0xFF + + # The SOCKS packet type for requesting a proxy connection. + CMD_CONNECT = 1 + + # The SOCKS address type for connections via IP address. + ATYP_IPV4 = 1 + + # The SOCKS address type for connections via domain name. + ATYP_DOMAIN = 3 + + # The SOCKS response code for a successful operation. + SUCCESS = 0 + + # The proxy's host name or IP address + attr_reader :proxy_host + + # The proxy's port number + attr_reader :proxy_port + + # The map of options given at initialization + attr_reader :options + + # Create a new proxy connection to the given proxy host and port. + # Optionally, :user and :password options may be given to + # identify the username and password with which to authenticate. + def initialize(proxy_host, proxy_port=1080, options={}) + @proxy_host = proxy_host + @proxy_port = proxy_port + @options = options + end + + # Return a new socket connected to the given host and port via the + # proxy that was requested when the socket factory was instantiated. + def open(host, port) + socket = TCPSocket.new(proxy_host, proxy_port) + + methods = [METHOD_NO_AUTH] + methods << METHOD_PASSWD if options[:user] + + packet = [VERSION, methods.size, *methods].pack("C*") + socket.send packet, 0 + + version, method = socket.recv(2).unpack("CC") + if version != VERSION + socket.close + raise Net::SSH::Proxy::Error, "invalid SOCKS version (#{version})" + end + + if method == METHOD_NONE + socket.close + raise Net::SSH::Proxy::Error, "no supported authorization methods" + end + + negotiate_password(socket) if method == METHOD_PASSWD + + packet = [VERSION, CMD_CONNECT, 0].pack("C*") + + if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ + packet << [ATYP_IPV4, $1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*") + else + packet << [ATYP_DOMAIN, host.length, host].pack("CCA*") + end + + packet << [port].pack("n") + socket.send packet, 0 + + version, reply, = socket.recv(2).unpack("C*") + socket.recv(1) + address_type = socket.recv(1).getbyte(0) + case address_type + when 1 + socket.recv(4) # get four bytes for IPv4 address + when 3 + len = socket.recv(1).getbyte(0) + hostname = socket.recv(len) + when 4 + ipv6addr hostname = socket.recv(16) + else + socket.close + raise ConnectionError, "Illegal response type" + end + portnum = socket.recv(2) + + unless reply == SUCCESS + socket.close + raise ConnectError, "#{reply}" + end + + return socket + end + + private + + # Simple username/password negotiation with the SOCKS5 server. + def negotiate_password(socket) + packet = [0x01, options[:user].length, options[:user], + options[:password].length, options[:password]].pack("CCA*CA*") + socket.send packet, 0 + + version, status = socket.recv(2).unpack("CC") + + if status != SUCCESS + socket.close + raise UnauthorizedError, "could not authorize user" + end + end + end + + end + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/service/forward.rb File: forward.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/service/forward.rb;tzinfo2 @@ -1,0 +1,267 @@ +require 'net/ssh/loggable' + +module Net; module SSH; module Service + + # This class implements various port forwarding services for use by + # Net::SSH clients. The Forward class should never need to be instantiated + # directly; instead, it should be accessed via the singleton instance + # returned by Connection::Session#forward: + # + # ssh.forward.local(1234, "www.capify.org", 80) + class Forward + include Loggable + + # The underlying connection service instance that the port-forwarding + # services employ. + attr_reader :session + + # A simple class for representing a requested remote forwarded port. + Remote = Struct.new(:host, :port) #:nodoc: + + # Instantiates a new Forward service instance atop the given connection + # service session. This will register new channel open handlers to handle + # the specialized channels that the SSH port forwarding protocols employ. + def initialize(session) + @session = session + self.logger = session.logger + @remote_forwarded_ports = {} + @local_forwarded_ports = {} + @agent_forwarded = false + + session.on_open_channel('forwarded-tcpip', &method(:forwarded_tcpip)) + session.on_open_channel('auth-agent', &method(:auth_agent_channel)) + session.on_open_channel('auth-agent@openssh.com', &method(:auth_agent_channel)) + end + + # Starts listening for connections on the local host, and forwards them + # to the specified remote host/port via the SSH connection. This method + # accepts either three or four arguments. When four arguments are given, + # they are: + # + # * the local address to bind to + # * the local port to listen on + # * the remote host to forward connections to + # * the port on the remote host to connect to + # + # If three arguments are given, it is as if the local bind address is + # "127.0.0.1", and the rest are applied as above. + # + # ssh.forward.local(1234, "www.capify.org", 80) + # ssh.forward.local("0.0.0.0", 1234, "www.capify.org", 80) + def local(*args) + if args.length < 3 || args.length > 4 + raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}" + end + + bind_address = "127.0.0.1" + bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/ + + local_port = args.shift.to_i + remote_host = args.shift + remote_port = args.shift.to_i + + socket = TCPServer.new(bind_address, local_port) + + @local_forwarded_ports[[local_port, bind_address]] = socket + + session.listen_to(socket) do |server| + client = server.accept + debug { "received connection on #{bind_address}:#{local_port}" } + + channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, :long, local_port) do |achannel| + achannel.info { "direct channel established" } + end + + prepare_client(client, channel, :local) + + channel.on_open_failed do |ch, code, description| + channel.error { "could not establish direct channel: #{description} (#{code})" } + channel[:socket].close + end + end + end + + # Terminates an active local forwarded port. If no such forwarded port + # exists, this will raise an exception. Otherwise, the forwarded connection + # is terminated. + # + # ssh.forward.cancel_local(1234) + # ssh.forward.cancel_local(1234, "0.0.0.0") + def cancel_local(port, bind_address="127.0.0.1") + socket = @local_forwarded_ports.delete([port, bind_address]) + socket.shutdown rescue nil + socket.close rescue nil + session.stop_listening_to(socket) + end + + # Returns a list of all active locally forwarded ports. The returned value + # is an array of arrays, where each element is a two-element tuple + # consisting of the local port and bind address corresponding to the + # forwarding port. + def active_locals + @local_forwarded_ports.keys + end + + # Requests that all connections on the given remote-port be forwarded via + # the local host to the given port/host. The last argument describes the + # bind address on the remote host, and defaults to 127.0.0.1. + # + # This method will return immediately, but the port will not actually be + # forwarded immediately. If the remote server is not able to begin the + # listener for this request, an exception will be raised asynchronously. + # + # If you want to know when the connection is active, it will show up in the + # #active_remotes list. If you want to block until the port is active, you + # could do something like this: + # + # ssh.forward.remote(80, "www.google.com", 1234, "0.0.0.0") + # ssh.loop { !ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) } + def remote(port, host, remote_port, remote_host="127.0.0.1") + session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response| + if success + debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" } + @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port) + else + error { "remote forwarding request failed" } + raise Net::SSH::Exception, "remote forwarding request failed" + end + end + end + + # an alias, for token backwards compatibility with the 1.x API + alias :remote_to :remote + + # Requests that a remote forwarded port be cancelled. The remote forwarded + # port on the remote host, bound to the given address on the remote host, + # will be terminated, but not immediately. This method returns immediately + # after queueing the request to be sent to the server. If for some reason + # the port cannot be cancelled, an exception will be raised (asynchronously). + # + # If you want to know when the connection has been cancelled, it will no + # longer be present in the #active_remotes list. If you want to block until + # the port is no longer active, you could do something like this: + # + # ssh.forward.cancel_remote(1234, "0.0.0.0") + # ssh.loop { ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) } + def cancel_remote(port, host="127.0.0.1") + session.send_global_request("cancel-tcpip-forward", :string, host, :long, port) do |success, response| + if success + @remote_forwarded_ports.delete([port, host]) + else + raise Net::SSH::Exception, "could not cancel remote forward request on #{host}:#{port}" + end + end + end + + # Returns all active forwarded remote ports. The returned value is an + # array of two-element tuples, where the first element is the port on the + # remote host and the second is the bind address. + def active_remotes + @remote_forwarded_ports.keys + end + + # Enables SSH agent forwarding on the given channel. The forwarded agent + # will remain active even after the channel closes--the channel is only + # used as the transport for enabling the forwarded connection. You should + # never need to call this directly--it is called automatically the first + # time a session channel is opened, when the connection was created with + # :forward_agent set to true: + # + # Net::SSH.start("remote.host", "me", :forwrd_agent => true) do |ssh| + # ssh.open_channel do |ch| + # # agent will be automatically forwarded by this point + # end + # ssh.loop + # end + def agent(channel) + return if @agent_forwarded + @agent_forwarded = true + + channel.send_channel_request("auth-agent-req@openssh.com") do |achannel, success| + if success + debug { "authentication agent forwarding is active" } + else + achannel.send_channel_request("auth-agent-req") do |a2channel, success2| + if success2 + debug { "authentication agent forwarding is active" } + else + error { "could not establish forwarding of authentication agent" } + end + end + end + end + end + + private + + # Perform setup operations that are common to all forwarded channels. + # +client+ is a socket, +channel+ is the channel that was just created, + # and +type+ is an arbitrary string describing the type of the channel. + def prepare_client(client, channel, type) + client.extend(Net::SSH::BufferedIo) + client.logger = logger + + session.listen_to(client) + channel[:socket] = client + + channel.on_data do |ch, data| + ch[:socket].enqueue(data) + end + + channel.on_close do |ch| + debug { "closing #{type} forwarded channel" } + ch[:socket].close if !client.closed? + session.stop_listening_to(ch[:socket]) + end + + channel.on_process do |ch| + if ch[:socket].closed? + ch.info { "#{type} forwarded connection closed" } + ch.close + elsif ch[:socket].available > 0 + data = ch[:socket].read_available(8192) + ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" } + ch.send_data(data) + end + end + end + + # The callback used when a new "forwarded-tcpip" channel is requested + # by the server. This will open a new socket to the host/port specified + # when the forwarded connection was first requested. + def forwarded_tcpip(session, channel, packet) + connected_address = packet.read_string + connected_port = packet.read_long + originator_address = packet.read_string + originator_port = packet.read_long + + remote = @remote_forwarded_ports[[connected_port, connected_address]] + + if remote.nil? + raise Net::SSH::ChannelOpenFailed.new(1, "unknown request from remote forwarded connection on #{connected_address}:#{connected_port}") + end + + client = TCPSocket.new(remote.host, remote.port) + info { "connected #{connected_address}:#{connected_port} originator #{originator_address}:#{originator_port}" } + + prepare_client(client, channel, :remote) + rescue SocketError => err + raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to remote host (#{remote.host}:#{remote.port}): #{err.message}") + end + + # The callback used when an auth-agent channel is requested by the server. + def auth_agent_channel(session, channel, packet) + info { "opening auth-agent channel" } + channel[:invisible] = true + + begin + agent = Authentication::Agent.connect(logger) + prepare_client(agent.socket, channel, :agent) + rescue Exception => e + error { "attempted to connect to agent but failed: #{e.class.name} (#{e.message})" } + raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to authentication agent") + end + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/channel.rb File: channel.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/channel.rb;tzinfo2 @@ -1,0 +1,129 @@ +module Net; module SSH; module Test + + # A mock channel, used for scripting actions in tests. It wraps a + # Net::SSH::Test::Script instance, and delegates to it for the most part. + # This class has little real functionality on its own, but rather acts as + # a convenience for scripting channel-related activity for later comparison + # in a unit test. + # + # story do |session| + # channel = session.opens_channel + # channel.sends_exec "ls" + # channel.gets_data "result of ls" + # channel.gets_close + # channel.sends_close + # end + class Channel + # The Net::SSH::Test::Script instance employed by this mock channel. + attr_reader :script + + # Sets the local-id of this channel object (the id assigned by the client). + attr_writer :local_id + + # Sets the remote-id of this channel object (the id assigned by the mock-server). + attr_writer :remote_id + + # Creates a new Test::Channel instance on top of the given +script+ (which + # must be a Net::SSH::Test::Script instance). + def initialize(script) + @script = script + @local_id = @remote_id = nil + end + + # Returns the local (client-assigned) id for this channel, or a Proc object + # that will return the local-id later if the local id has not yet been set. + # (See Net::SSH::Test::Packet#instantiate!.) + def local_id + @local_id || Proc.new { @local_id or raise "local-id has not been set yet!" } + end + + # Returns the remote (server-assigned) id for this channel, or a Proc object + # that will return the remote-id later if the remote id has not yet been set. + # (See Net::SSH::Test::Packet#instantiate!.) + def remote_id + @remote_id || Proc.new { @remote_id or raise "remote-id has not been set yet!" } + end + + # Because adjacent calls to #gets_data will sometimes cause the data packets + # to be concatenated (causing expectations in tests to fail), you may + # need to separate those calls with calls to #inject_remote_delay! (which + # essentially just mimics receiving an empty data packet): + # + # channel.gets_data "abcdefg" + # channel.inject_remote_delay! + # channel.gets_data "hijklmn" + def inject_remote_delay! + gets_data("") + end + + # Scripts the sending of an "exec" channel request packet to the mock + # server. If +reply+ is true, then the server is expected to reply to the + # request, otherwise no response to this request will be sent. If +success+ + # is +true+, then the request will be successful, otherwise a failure will + # be scripted. + # + # channel.sends_exec "ls -l" + def sends_exec(command, reply=true, success=true) + script.sends_channel_request(self, "exec", reply, command, success) + end + + # Scripts the sending of a "subsystem" channel request packet to the mock + # server. See #sends_exec for a discussion of the meaning of the +reply+ + # and +success+ arguments. + # + # channel.sends_subsystem "sftp" + def sends_subsystem(subsystem, reply=true, success=true) + script.sends_channel_request(self, "subsystem", reply, subsystem, success) + end + + # Scripts the sending of a data packet across the channel. + # + # channel.sends_data "foo" + def sends_data(data) + script.sends_channel_data(self, data) + end + + # Scripts the sending of an EOF packet across the channel. + # + # channel.sends_eof + def sends_eof + script.sends_channel_eof(self) + end + + # Scripts the sending of a "channel close" packet across the channel. + # + # channel.sends_close + def sends_close + script.sends_channel_close(self) + end + + # Scripts the reception of a channel data packet from the remote end. + # + # channel.gets_data "bar" + def gets_data(data) + script.gets_channel_data(self, data) + end + + # Scripts the reception of an "exit-status" channel request packet. + # + # channel.gets_exit_status(127) + def gets_exit_status(status=0) + script.gets_channel_request(self, "exit-status", false, status) + end + + # Scripts the reception of an EOF packet from the remote end. + # + # channel.gets_eof + def gets_eof + script.gets_channel_eof(self) + end + + # Scripts the reception of a "channel close" packet from the remote end. + # + # channel.gets_close + def gets_close + script.gets_channel_close(self) + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/extensions.rb File: extensions.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/extensions.rb;tzinfo2 @@ -1,0 +1,152 @@ +require 'net/ssh/buffer' +require 'net/ssh/packet' +require 'net/ssh/buffered_io' +require 'net/ssh/connection/channel' +require 'net/ssh/connection/constants' +require 'net/ssh/transport/constants' +require 'net/ssh/transport/packet_stream' + +module Net; module SSH; module Test + + # A collection of modules used to extend/override the default behavior of + # Net::SSH internals for ease of testing. As a consumer of Net::SSH, you'll + # never need to use this directly--they're all used under the covers by + # the Net::SSH::Test system. + module Extensions + + # An extension to Net::SSH::BufferedIo (assumes that the underlying IO + # is actually a StringIO). Facilitates unit testing. + module BufferedIo + # Returns +true+ if the position in the stream is less than the total + # length of the stream. + def select_for_read? + pos < size + end + + # Set this to +true+ if you want the IO to pretend to be available for writing + attr_accessor :select_for_write + + # Set this to +true+ if you want the IO to pretend to be in an error state + attr_accessor :select_for_error + + alias select_for_write? select_for_write + alias select_for_error? select_for_error + end + + # An extension to Net::SSH::Transport::PacketStream (assumes that the + # underlying IO is actually a StringIO). Facilitates unit testing. + module PacketStream + include BufferedIo # make sure we get the extensions here, too + + def self.included(base) #:nodoc: + base.send :alias_method, :real_available_for_read?, :available_for_read? + base.send :alias_method, :available_for_read?, :test_available_for_read? + + base.send :alias_method, :real_enqueue_packet, :enqueue_packet + base.send :alias_method, :enqueue_packet, :test_enqueue_packet + + base.send :alias_method, :real_poll_next_packet, :poll_next_packet + base.send :alias_method, :poll_next_packet, :test_poll_next_packet + end + + # Called when another packet should be inspected from the current + # script. If the next packet is a remote packet, it pops it off the + # script and shoves it onto this IO object, making it available to + # be read. + def idle! + return false unless script.next(:first) + + if script.next(:first).remote? + self.string << script.next.to_s + self.pos = pos + end + + return true + end + + # The testing version of Net::SSH::Transport::PacketStream#available_for_read?. + # Returns true if there is data pending to be read. Otherwise calls #idle!. + def test_available_for_read? + return true if select_for_read? + idle! + false + end + + # The testing version of Net::SSH::Transport::PacketStream#enqueued_packet. + # Simply calls Net::SSH::Test::Script#process on the packet. + def test_enqueue_packet(payload) + packet = Net::SSH::Buffer.new(payload.to_s) + script.process(packet) + end + + # The testing version of Net::SSH::Transport::PacketStream#poll_next_packet. + # Reads the next available packet from the IO object and returns it. + def test_poll_next_packet + return nil if available <= 0 + packet = Net::SSH::Buffer.new(read_available(4)) + length = packet.read_long + Net::SSH::Packet.new(read_available(length)) + end + end + + # An extension to Net::SSH::Connection::Channel. Facilitates unit testing. + module Channel + def self.included(base) #:nodoc: + base.send :alias_method, :send_data_for_real, :send_data + base.send :alias_method, :send_data, :send_data_for_test + end + + # The testing version of Net::SSH::Connection::Channel#send_data. Calls + # the original implementation, and then immediately enqueues the data for + # output so that scripted sends are properly interpreted as discrete + # (rather than concatenated) data packets. + def send_data_for_test(data) + send_data_for_real(data) + enqueue_pending_output + end + end + + # An extension to the built-in ::IO class. Simply redefines IO.select + # so that it can be scripted in Net::SSH unit tests. + module IO + def self.included(base) #:nodoc: + base.extend(ClassMethods) + end + + module ClassMethods + def self.extended(obj) #:nodoc: + class < "abc-xyz", + :server_key => OpenSSL::PKey::RSA.new(32), + :shared_secret => OpenSSL::BN.new("1234567890", 10), + :hashing_algorithm => OpenSSL::Digest::SHA1 } + end + end + +end; end; end + +Net::SSH::Transport::Algorithms::ALGORITHMS[:kex] << "test" +Net::SSH::Transport::Kex::MAP["test"] = Net::SSH::Test::Kex =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/local_packet.rb File: local_packet.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/local_packet.rb;tzinfo2 @@ -1,0 +1,51 @@ +require 'net/ssh/packet' +require 'net/ssh/test/packet' + +module Net; module SSH; module Test + + # This is a specialization of Net::SSH::Test::Packet for representing mock + # packets that are sent from the local (client) host. These are created + # automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any + # of the sends_* methods. + class LocalPacket < Packet + attr_reader :init + + # Extend the default Net::SSH::Test::Packet constructor to also accept an + # optional block, which is used to finalize the initialization of the + # packet when #process is first called. + def initialize(type, *args, &block) + super(type, *args) + @init = block + end + + # Returns +true+; this is a local packet. + def local? + true + end + + # Called by Net::SSH::Test::Extensions::PacketStream#test_enqueue_packet + # to mimic remote processing of a locally-sent packet. It compares the + # packet it was given with the contents of this LocalPacket's data, to see + # if what was sent matches what was scripted. If it differs in any way, + # an exception is raised. + def process(packet) + @init.call(Net::SSH::Packet.new(packet.to_s)) if @init + type = packet.read_byte + raise "expected #{@type}, but got #{type}" if @type != type + + @data.zip(types).each do |expected, type| + type ||= case expected + when nil then break + when Numeric then :long + when String then :string + when TrueClass, FalseClass then :bool + end + + actual = packet.send("read_#{type}") + next if expected.nil? + raise "expected #{type} #{expected.inspect} but got #{actual.inspect}" unless expected == actual + end + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/packet.rb File: packet.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/packet.rb;tzinfo2 @@ -1,0 +1,81 @@ +require 'net/ssh/connection/constants' +require 'net/ssh/transport/constants' + +module Net; module SSH; module Test + + # This is an abstract class, not to be instantiated directly, subclassed by + # Net::SSH::Test::LocalPacket and Net::SSH::Test::RemotePacket. It implements + # functionality common to those subclasses. + # + # These packets are not true packets, in that they don't represent what was + # actually sent between the hosst; rather, they represent what was expected + # to be sent, as dictated by the script (Net::SSH::Test::Script). Thus, + # though they are defined with data elements, these data elements are used + # to either validate data that was sent by the local host (Net::SSH::Test::LocalPacket) + # or to mimic the sending of data by the remote host (Net::SSH::Test::RemotePacket). + class Packet + include Net::SSH::Transport::Constants + include Net::SSH::Connection::Constants + + # Ceate a new packet of the given +type+, and with +args+ being a list of + # data elements in the order expected for packets of the given +type+ + # (see #types). + def initialize(type, *args) + @type = self.class.const_get(type.to_s.upcase) + @data = args + end + + # The default for +remote?+ is false. Subclasses should override as necessary. + def remote? + false + end + + # The default for +local?+ is false. Subclasses should override as necessary. + def local? + false + end + + # Instantiates the packets data elements. When the packet was first defined, + # some elements may not have been fully realized, and were described as + # Proc objects rather than atomic types. This invokes those Proc objects + # and replaces them with their returned values. This allows for values + # like Net::SSH::Test::Channel#remote_id to be used in scripts before + # the remote_id is known (since it is only known after a channel has been + # confirmed open). + def instantiate! + @data.map! { |i| i.respond_to?(:call) ? i.call : i } + end + + # Returns an array of symbols describing the data elements for packets of + # the same type as this packet. These types are used to either validate + # sent packets (Net::SSH::Test::LocalPacket) or build received packets + # (Net::SSH::Test::RemotePacket). + # + # Not all packet types are defined here. As new packet types are required + # (e.g., a unit test needs to test that the remote host sent a packet that + # is not implemented here), the description of that packet should be + # added. Unsupported packet types will otherwise raise an exception. + def types + @types ||= case @type + when KEXINIT then + [:long, :long, :long, :long, + :string, :string, :string, :string, :string, :string, :string, :string, :string, :string, + :bool] + when NEWKEYS then [] + when CHANNEL_OPEN then [:string, :long, :long, :long] + when CHANNEL_OPEN_CONFIRMATION then [:long, :long, :long, :long] + when CHANNEL_DATA then [:long, :string] + when CHANNEL_EOF, CHANNEL_CLOSE, CHANNEL_SUCCESS, CHANNEL_FAILURE then [:long] + when CHANNEL_REQUEST + parts = [:long, :string, :bool] + case @data[1] + when "exec", "subsystem" then parts << :string + when "exit-status" then parts << :long + else raise "don't know what to do about #{@data[1]} channel request" + end + else raise "don't know how to parse packet type #{@type}" + end + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/remote_packet.rb File: remote_packet.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/remote_packet.rb;tzinfo2 @@ -1,0 +1,38 @@ +require 'net/ssh/buffer' +require 'net/ssh/test/packet' + +module Net; module SSH; module Test + + # This is a specialization of Net::SSH::Test::Packet for representing mock + # packets that are received by the local (client) host. These are created + # automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any + # of the gets_* methods. + class RemotePacket < Packet + # Returns +true+; this is a remote packet. + def remote? + true + end + + # The #process method should only be called on Net::SSH::Test::LocalPacket + # packets; if it is attempted on a remote packet, then it is an expectation + # mismatch (a remote packet was received when a local packet was expected + # to be sent). This will happen when either your test script + # (Net::SSH::Test::Script) or your program are wrong. + def process(packet) + raise "received packet type #{packet.read_byte} and was not expecting any packet" + end + + # Returns this remote packet as a string, suitable for parsing by + # Net::SSH::Transport::PacketStream and friends. When a remote packet is + # received, this method is called and the result concatenated onto the + # input buffer for the packet stream. + def to_s + @to_s ||= begin + instantiate! + string = Net::SSH::Buffer.from(:byte, @type, *types.zip(@data).flatten).to_s + [string.length, string].pack("NA*") + end + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/script.rb File: script.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/script.rb;tzinfo2 @@ -1,0 +1,157 @@ +require 'net/ssh/test/channel' +require 'net/ssh/test/local_packet' +require 'net/ssh/test/remote_packet' + +module Net; module SSH; module Test + + # Represents a sequence of scripted events that identify the behavior that + # a test expects. Methods named "sends_*" create events for packets being + # sent from the local to the remote host, and methods named "gets_*" create + # events for packets being received by the local from the remote host. + # + # A reference to a script. is generally obtained in a unit test via the + # Net::SSH::Test#story helper method: + # + # story do |script| + # channel = script.opens_channel + # ... + # end + class Script + # The list of scripted events. These will be Net::SSH::Test::LocalPacket + # and Net::SSH::Test::RemotePacket instances. + attr_reader :events + + # Create a new, empty script. + def initialize + @events = [] + end + + # Scripts the opening of a channel by adding a local packet sending the + # channel open request, and if +confirm+ is true (the default), also + # adding a remote packet confirming the new channel. + # + # A new Net::SSH::Test::Channel instance is returned, which can be used + # to script additional channel operations. + def opens_channel(confirm=true) + channel = Channel.new(self) + channel.remote_id = 5555 + + events << LocalPacket.new(:channel_open) { |p| channel.local_id = p[:remote_id] } + + if confirm + events << RemotePacket.new(:channel_open_confirmation, channel.local_id, channel.remote_id, 0x20000, 0x10000) + end + + channel + end + + # A convenience method for adding an arbitrary local packet to the events + # list. + def sends(type, *args, &block) + events << LocalPacket.new(type, *args, &block) + end + + # A convenience method for adding an arbitrary remote packet to the events + # list. + def gets(type, *args) + events << RemotePacket.new(type, *args) + end + + # Scripts the sending of a new channel request packet to the remote host. + # +channel+ should be an instance of Net::SSH::Test::Channel. +request+ + # is a string naming the request type to send, +reply+ is a boolean + # indicating whether a response to this packet is required , and +data+ + # is any additional request-specific data that this packet should send. + # +success+ indicates whether the response (if one is required) should be + # success or failure. + # + # If a reply is desired, a remote packet will also be queued, :channel_success + # if +success+ is true, or :channel_failure if +success+ is false. + # + # This will typically be called via Net::SSH::Test::Channel#sends_exec or + # Net::SSH::Test::Channel#sends_subsystem. + def sends_channel_request(channel, request, reply, data, success=true) + events << LocalPacket.new(:channel_request, channel.remote_id, request, reply, data) + if reply + if success + events << RemotePacket.new(:channel_success, channel.local_id) + else + events << RemotePacket.new(:channel_failure, channel.local_id) + end + end + end + + # Scripts the sending of a channel data packet. +channel+ must be a + # Net::SSH::Test::Channel object, and +data+ is the (string) data to + # expect will be sent. + # + # This will typically be called via Net::SSH::Test::Channel#sends_data. + def sends_channel_data(channel, data) + events << LocalPacket.new(:channel_data, channel.remote_id, data) + end + + # Scripts the sending of a channel EOF packet from the given + # Net::SSH::Test::Channel +channel+. This will typically be called via + # Net::SSH::Test::Channel#sends_eof. + def sends_channel_eof(channel) + events << LocalPacket.new(:channel_eof, channel.remote_id) + end + + # Scripts the sending of a channel close packet from the given + # Net::SSH::Test::Channel +channel+. This will typically be called via + # Net::SSH::Test::Channel#sends_close. + def sends_channel_close(channel) + events << LocalPacket.new(:channel_close, channel.remote_id) + end + + # Scripts the reception of a channel data packet from the remote host by + # the given Net::SSH::Test::Channel +channel+. This will typically be + # called via Net::SSH::Test::Channel#gets_data. + def gets_channel_data(channel, data) + events << RemotePacket.new(:channel_data, channel.local_id, data) + end + + # Scripts the reception of a channel request packet from the remote host by + # the given Net::SSH::Test::Channel +channel+. This will typically be + # called via Net::SSH::Test::Channel#gets_exit_status. + def gets_channel_request(channel, request, reply, data) + events << RemotePacket.new(:channel_request, channel.local_id, request, reply, data) + end + + # Scripts the reception of a channel EOF packet from the remote host by + # the given Net::SSH::Test::Channel +channel+. This will typically be + # called via Net::SSH::Test::Channel#gets_eof. + def gets_channel_eof(channel) + events << RemotePacket.new(:channel_eof, channel.local_id) + end + + # Scripts the reception of a channel close packet from the remote host by + # the given Net::SSH::Test::Channel +channel+. This will typically be + # called via Net::SSH::Test::Channel#gets_close. + def gets_channel_close(channel) + events << RemotePacket.new(:channel_close, channel.local_id) + end + + # By default, removes the next event in the list and returns it. However, + # this can also be used to non-destructively peek at the next event in the + # list, by passing :first as the argument. + # + # # remove the next event and return it + # event = script.next + # + # # peek at the next event + # event = script.next(:first) + def next(mode=:shift) + events.send(mode) + end + + # Compare the given packet against the next event in the list. If there is + # no next event, an exception will be raised. This is called by + # Net::SSH::Test::Extensions::PacketStream#test_enqueue_packet. + def process(packet) + event = events.shift or raise "end of script reached, but got a packet type #{packet.read_byte}" + event.process(packet) + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/socket.rb File: socket.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/test/socket.rb;tzinfo2 @@ -1,0 +1,59 @@ +require 'socket' +require 'stringio' +require 'net/ssh/test/extensions' +require 'net/ssh/test/script' + +module Net; module SSH; module Test + + # A mock socket implementation for use in testing. It implements the minimum + # necessary interface for interacting with the rest of the Net::SSH::Test + # system. + class Socket < StringIO + attr_reader :host, :port + + # The Net::SSH::Test::Script object in use by this socket. This is the + # canonical script instance that should be used for any test depending on + # this socket instance. + attr_reader :script + + # Create a new test socket. This will also instantiate a new Net::SSH::Test::Script + # and seed it with the necessary events to power the initialization of the + # connection. + def initialize + extend(Net::SSH::Transport::PacketStream) + super "SSH-2.0-Test\r\n" + + @script = Script.new + + script.gets(:kexinit, 1, 2, 3, 4, "test", "ssh-rsa", "none", "none", "none", "none", "none", "none", "", "", false) + script.sends(:kexinit) + script.sends(:newkeys) + script.gets(:newkeys) + end + + # This doesn't actually do anything, since we don't really care what gets + # written. + def write(data) + # black hole, because we don't actually care about what gets written + end + + # Allows the socket to also mimic a socket factory, simply returning + # +self+. + def open(host, port) + @host, @port = host, port + self + end + + # Returns a sockaddr struct for the port and host that were used when the + # socket was instantiated. + def getpeername + ::Socket.sockaddr_in(port, host) + end + + # Alias to #read, but never returns nil (returns an empty string instead). + def recv(n) + read(n) || "" + end + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/algorithms.rb File: algorithms.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/algorithms.rb;tzinfo2 @@ -1,0 +1,384 @@ +require 'net/ssh/buffer' +require 'net/ssh/known_hosts' +require 'net/ssh/loggable' +require 'net/ssh/transport/cipher_factory' +require 'net/ssh/transport/constants' +require 'net/ssh/transport/hmac' +require 'net/ssh/transport/kex' +require 'net/ssh/transport/server_version' + +module Net; module SSH; module Transport + + # Implements the higher-level logic behind an SSH key-exchange. It handles + # both the initial exchange, as well as subsequent re-exchanges (as needed). + # It also encapsulates the negotiation of the algorithms, and provides a + # single point of access to the negotiated algorithms. + # + # You will never instantiate or reference this directly. It is used + # internally by the transport layer. + class Algorithms + include Constants, Loggable + + # Define the default algorithms, in order of preference, supported by + # Net::SSH. + ALGORITHMS = { + :host_key => %w(ssh-rsa ssh-dss), + :kex => %w(diffie-hellman-group-exchange-sha1 + diffie-hellman-group1-sha1), + :encryption => %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc + aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se + idea-cbc none arcfour128 arcfour256), + :hmac => %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 none), + :compression => %w(none zlib@openssh.com zlib), + :language => %w() + } + + # The underlying transport layer session that supports this object + attr_reader :session + + # The hash of options used to initialize this object + attr_reader :options + + # The kex algorithm to use settled on between the client and server. + attr_reader :kex + + # The type of host key that will be used for this session. + attr_reader :host_key + + # The type of the cipher to use to encrypt packets sent from the client to + # the server. + attr_reader :encryption_client + + # The type of the cipher to use to decrypt packets arriving from the server. + attr_reader :encryption_server + + # The type of HMAC to use to sign packets sent by the client. + attr_reader :hmac_client + + # The type of HMAC to use to validate packets arriving from the server. + attr_reader :hmac_server + + # The type of compression to use to compress packets being sent by the client. + attr_reader :compression_client + + # The type of compression to use to decompress packets arriving from the server. + attr_reader :compression_server + + # The language that will be used in messages sent by the client. + attr_reader :language_client + + # The language that will be used in messages sent from the server. + attr_reader :language_server + + # The hash of algorithms preferred by the client, which will be told to + # the server during algorithm negotiation. + attr_reader :algorithms + + # The session-id for this session, as decided during the initial key exchange. + attr_reader :session_id + + # Returns true if the given packet can be processed during a key-exchange. + def self.allowed_packet?(packet) + ( 1.. 4).include?(packet.type) || + ( 6..19).include?(packet.type) || + (21..49).include?(packet.type) + end + + # Instantiates a new Algorithms object, and prepares the hash of preferred + # algorithms based on the options parameter and the ALGORITHMS constant. + def initialize(session, options={}) + @session = session + @logger = session.logger + @options = options + @algorithms = {} + @pending = @initialized = false + @client_packet = @server_packet = nil + prepare_preferred_algorithms! + end + + # Request a rekey operation. This will return immediately, and does not + # actually perform the rekey operation. It does cause the session to change + # state, however--until the key exchange finishes, no new packets will be + # processed. + def rekey! + @client_packet = @server_packet = nil + @initialized = false + send_kexinit + end + + # Called by the transport layer when a KEXINIT packet is recieved, indicating + # that the server wants to exchange keys. This can be spontaneous, or it + # can be in response to a client-initiated rekey request (see #rekey!). Either + # way, this will block until the key exchange completes. + def accept_kexinit(packet) + info { "got KEXINIT from server" } + @server_data = parse_server_algorithm_packet(packet) + @server_packet = @server_data[:raw] + if !pending? + send_kexinit + else + proceed! + end + end + + # A convenience method for accessing the list of preferred types for a + # specific algorithm (see #algorithms). + def [](key) + algorithms[key] + end + + # Returns +true+ if a key-exchange is pending. This will be true from the + # moment either the client or server requests the key exchange, until the + # exchange completes. While an exchange is pending, only a limited number + # of packets are allowed, so event processing essentially stops during this + # period. + def pending? + @pending + end + + # Returns true if no exchange is pending, and otherwise returns true or + # false depending on whether the given packet is of a type that is allowed + # during a key exchange. + def allow?(packet) + !pending? || Algorithms.allowed_packet?(packet) + end + + # Returns true if the algorithms have been negotiated at all. + def initialized? + @initialized + end + + private + + # Sends a KEXINIT packet to the server. If a server KEXINIT has already + # been received, this will then invoke #proceed! to proceed with the key + # exchange, otherwise it returns immediately (but sets the object to the + # pending state). + def send_kexinit + info { "sending KEXINIT" } + @pending = true + packet = build_client_algorithm_packet + @client_packet = packet.to_s + session.send_message(packet) + proceed! if @server_packet + end + + # After both client and server have sent their KEXINIT packets, this + # will do the algorithm negotiation and key exchange. Once both finish, + # the object leaves the pending state and the method returns. + def proceed! + info { "negotiating algorithms" } + negotiate_algorithms + exchange_keys + @pending = false + end + + # Prepares the list of preferred algorithms, based on the options hash + # that was given when the object was constructed, and the ALGORITHMS + # constant. Also, when determining the host_key type to use, the known + # hosts files are examined to see if the host has ever sent a host_key + # before, and if so, that key type is used as the preferred type for + # communicating with this server. + def prepare_preferred_algorithms! + options[:compression] = %w(zlib@openssh.com zlib) if options[:compression] == true + + ALGORITHMS.each do |algorithm, list| + algorithms[algorithm] = list.dup + + # apply the preferred algorithm order, if any + if options[algorithm] + algorithms[algorithm] = Array(options[algorithm]).compact.uniq + invalid = algorithms[algorithm].detect { |name| !ALGORITHMS[algorithm].include?(name) } + raise NotImplementedError, "unsupported #{algorithm} algorithm: `#{invalid}'" if invalid + + # make sure all of our supported algorithms are tacked onto the + # end, so that if the user tries to give a list of which none are + # supported, we can still proceed. + list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) } + end + end + + # for convention, make sure our list has the same keys as the server + # list + + algorithms[:encryption_client ] = algorithms[:encryption_server ] = algorithms[:encryption] + algorithms[:hmac_client ] = algorithms[:hmac_server ] = algorithms[:hmac] + algorithms[:compression_client] = algorithms[:compression_server] = algorithms[:compression] + algorithms[:language_client ] = algorithms[:language_server ] = algorithms[:language] + + if !options.key?(:host_key) + # make sure the host keys are specified in preference order, where any + # existing known key for the host has preference. + + existing_keys = KnownHosts.search_for(options[:host_key_alias] || session.host_as_string, options) + host_keys = existing_keys.map { |key| key.ssh_type }.uniq + algorithms[:host_key].each do |name| + host_keys << name unless host_keys.include?(name) + end + algorithms[:host_key] = host_keys + end + end + + # Parses a KEXINIT packet from the server. + def parse_server_algorithm_packet(packet) + data = { :raw => packet.content } + + packet.read(16) # skip the cookie value + + data[:kex] = packet.read_string.split(/,/) + data[:host_key] = packet.read_string.split(/,/) + data[:encryption_client] = packet.read_string.split(/,/) + data[:encryption_server] = packet.read_string.split(/,/) + data[:hmac_client] = packet.read_string.split(/,/) + data[:hmac_server] = packet.read_string.split(/,/) + data[:compression_client] = packet.read_string.split(/,/) + data[:compression_server] = packet.read_string.split(/,/) + data[:language_client] = packet.read_string.split(/,/) + data[:language_server] = packet.read_string.split(/,/) + + # TODO: if first_kex_packet_follows, we need to try to skip the + # actual kexinit stuff and try to guess what the server is doing... + # need to read more about this scenario. + first_kex_packet_follows = packet.read_bool + + return data + end + + # Given the #algorithms map of preferred algorithm types, this constructs + # a KEXINIT packet to send to the server. It does not actually send it, + # it simply builds the packet and returns it. + def build_client_algorithm_packet + kex = algorithms[:kex ].join(",") + host_key = algorithms[:host_key ].join(",") + encryption = algorithms[:encryption ].join(",") + hmac = algorithms[:hmac ].join(",") + compression = algorithms[:compression].join(",") + language = algorithms[:language ].join(",") + + Net::SSH::Buffer.from(:byte, KEXINIT, + :long, [rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF), rand(0xFFFFFFFF)], + :string, [kex, host_key, encryption, encryption, hmac, hmac], + :string, [compression, compression, language, language], + :bool, false, :long, 0) + end + + # Given the parsed server KEX packet, and the client's preferred algorithm + # lists in #algorithms, determine which preferred algorithms each has + # in common and set those as the selected algorithms. If, for any algorithm, + # no type can be settled on, an exception is raised. + def negotiate_algorithms + @kex = negotiate(:kex) + @host_key = negotiate(:host_key) + @encryption_client = negotiate(:encryption_client) + @encryption_server = negotiate(:encryption_server) + @hmac_client = negotiate(:hmac_client) + @hmac_server = negotiate(:hmac_server) + @compression_client = negotiate(:compression_client) + @compression_server = negotiate(:compression_server) + @language_client = negotiate(:language_client) rescue "" + @language_server = negotiate(:language_server) rescue "" + + debug do + "negotiated:\n" + + [:kex, :host_key, :encryption_server, :encryption_client, :hmac_client, :hmac_server, :compression_client, :compression_server, :language_client, :language_server].map do |key| + "* #{key}: #{instance_variable_get("@#{key}")}" + end.join("\n") + end + end + + # Negotiates a single algorithm based on the preferences reported by the + # server and those set by the client. This is called by + # #negotiate_algorithms. + def negotiate(algorithm) + match = self[algorithm].find { |item| @server_data[algorithm].include?(item) } + + if match.nil? + raise Net::SSH::Exception, "could not settle on #{algorithm} algorithm" + end + + return match + end + + # Considers the sizes of the keys and block-sizes for the selected ciphers, + # and the lengths of the hmacs, and returns the largest as the byte requirement + # for the key-exchange algorithm. + def kex_byte_requirement + sizes = [8] # require at least 8 bytes + + sizes.concat(CipherFactory.get_lengths(encryption_client)) + sizes.concat(CipherFactory.get_lengths(encryption_server)) + + sizes << HMAC.key_length(hmac_client) + sizes << HMAC.key_length(hmac_server) + + sizes.max + end + + # Instantiates one of the Transport::Kex classes (based on the negotiated + # kex algorithm), and uses it to exchange keys. Then, the ciphers and + # HMACs are initialized and fed to the transport layer, to be used in + # further communication with the server. + def exchange_keys + debug { "exchanging keys" } + + algorithm = Kex::MAP[kex].new(self, session, + :client_version_string => Net::SSH::Transport::ServerVersion::PROTO_VERSION, + :server_version_string => session.server_version.version, + :server_algorithm_packet => @server_packet, + :client_algorithm_packet => @client_packet, + :need_bytes => kex_byte_requirement, + :logger => logger) + result = algorithm.exchange_keys + + secret = result[:shared_secret].to_ssh + hash = result[:session_id] + digester = result[:hashing_algorithm] + + @session_id ||= hash + + key = Proc.new { |salt| digester.digest(secret + hash + salt + @session_id) } + + iv_client = key["A"] + iv_server = key["B"] + key_client = key["C"] + key_server = key["D"] + mac_key_client = key["E"] + mac_key_server = key["F"] + + parameters = { :iv => iv_client, :key => key_client, :shared => secret, + :hash => hash, :digester => digester } + + cipher_client = CipherFactory.get(encryption_client, parameters.merge(:encrypt => true)) + cipher_server = CipherFactory.get(encryption_server, parameters.merge(:iv => iv_server, :key => key_server, :decrypt => true)) + + mac_client = HMAC.get(hmac_client, mac_key_client) + mac_server = HMAC.get(hmac_server, mac_key_server) + + session.configure_client :cipher => cipher_client, :hmac => mac_client, + :compression => normalize_compression_name(compression_client), + :compression_level => options[:compression_level], + :rekey_limit => options[:rekey_limit], + :max_packets => options[:rekey_packet_limit], + :max_blocks => options[:rekey_blocks_limit] + + session.configure_server :cipher => cipher_server, :hmac => mac_server, + :compression => normalize_compression_name(compression_server), + :rekey_limit => options[:rekey_limit], + :max_packets => options[:rekey_packet_limit], + :max_blocks => options[:rekey_blocks_limit] + + @initialized = true + end + + # Given the SSH name for some compression algorithm, return a normalized + # name as a symbol. + def normalize_compression_name(name) + case name + when "none" then false + when "zlib" then :standard + when "zlib@openssh.com" then :delayed + else raise ArgumentError, "unknown compression type `#{name}'" + end + end + end +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/cipher_factory.rb File: cipher_factory.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/cipher_factory.rb;tzinfo2 @@ -1,0 +1,97 @@ +require 'openssl' +require 'net/ssh/transport/identity_cipher' + +module Net; module SSH; module Transport + + # Implements a factory of OpenSSL cipher algorithms. + class CipherFactory + # Maps the SSH name of a cipher to it's corresponding OpenSSL name + SSH_TO_OSSL = { + "3des-cbc" => "des-ede3-cbc", + "blowfish-cbc" => "bf-cbc", + "aes256-cbc" => "aes-256-cbc", + "aes192-cbc" => "aes-192-cbc", + "aes128-cbc" => "aes-128-cbc", + "idea-cbc" => "idea-cbc", + "cast128-cbc" => "cast-cbc", + "rijndael-cbc@lysator.liu.se" => "aes-256-cbc", + "arcfour128" => "rc4", + "arcfour256" => "rc4", + "arcfour512" => "rc4", + "none" => "none" + } + + # Ruby's OpenSSL bindings always return a key length of 16 for RC4 ciphers + # resulting in the error: OpenSSL::CipherError: key length too short. + # The following ciphers will override this key length. + KEY_LEN_OVERRIDE = { + "arcfour256" => 32, + "arcfour512" => 64 + } + + # Returns true if the underlying OpenSSL library supports the given cipher, + # and false otherwise. + def self.supported?(name) + ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'" + return true if ossl_name == "none" + return OpenSSL::Cipher.ciphers.include?(ossl_name) + end + + # Retrieves a new instance of the named algorithm. The new instance + # will be initialized using an iv and key generated from the given + # iv, key, shared, hash and digester values. Additionally, the + # cipher will be put into encryption or decryption mode, based on the + # value of the +encrypt+ parameter. + def self.get(name, options={}) + ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'" + return IdentityCipher if ossl_name == "none" + + cipher = OpenSSL::Cipher::Cipher.new(ossl_name) + cipher.send(options[:encrypt] ? :encrypt : :decrypt) + + cipher.padding = 0 + cipher.iv = make_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4" + key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len + cipher.key_len = key_len + cipher.key = make_key(key_len, options[:key], options) + cipher.update(" " * 1536) if ossl_name == "rc4" + + return cipher + end + + # Returns a two-element array containing the [ key-length, + # block-size ] for the named cipher algorithm. If the cipher + # algorithm is unknown, or is "none", 0 is returned for both elements + # of the tuple. + def self.get_lengths(name) + ossl_name = SSH_TO_OSSL[name] + return [0, 0] if ossl_name.nil? || ossl_name == "none" + + cipher = OpenSSL::Cipher::Cipher.new(ossl_name) + key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len + cipher.key_len = key_len + + return [key_len, ossl_name=="rc4" ? 8 : cipher.block_size] + end + + private + + # Generate a key value in accordance with the SSH2 specification. + def self.make_key(bytes, start, options={}) + k = start[0, bytes] + + digester = options[:digester] or raise 'No digester supplied' + shared = options[:shared] or raise 'No shared secret supplied' + hash = options[:hash] or raise 'No hash supplied' + + while k.length < bytes + step = digester.digest(shared + hash + k) + bytes_needed = bytes - k.length + k << step[0, bytes_needed] + end + + return k + end + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/constants.rb File: constants.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/constants.rb;tzinfo2 @@ -1,0 +1,30 @@ +module Net; module SSH; module Transport + module Constants + + #-- + # Transport layer generic messages + #++ + + DISCONNECT = 1 + IGNORE = 2 + UNIMPLEMENTED = 3 + DEBUG = 4 + SERVICE_REQUEST = 5 + SERVICE_ACCEPT = 6 + + #-- + # Algorithm negotiation messages + #++ + + KEXINIT = 20 + NEWKEYS = 21 + + #-- + # Key exchange method specific messages + #++ + + KEXDH_INIT = 30 + KEXDH_REPLY = 31 + + end +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/hmac add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/hmac.rb File: hmac.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/hmac.rb;tzinfo2 @@ -1,0 +1,31 @@ +require 'net/ssh/transport/hmac/md5' +require 'net/ssh/transport/hmac/md5_96' +require 'net/ssh/transport/hmac/sha1' +require 'net/ssh/transport/hmac/sha1_96' +require 'net/ssh/transport/hmac/none' + +# Implements a simple factory interface for fetching hmac implementations, or +# for finding the key lengths for hmac implementations.s +module Net::SSH::Transport::HMAC + # The mapping of SSH hmac algorithms to their implementations + MAP = { + 'hmac-md5' => MD5, + 'hmac-md5-96' => MD5_96, + 'hmac-sha1' => SHA1, + 'hmac-sha1-96' => SHA1_96, + 'none' => None + } + + # Retrieves a new hmac instance of the given SSH type (+name+). If +key+ is + # given, the new instance will be initialized with that key. + def self.get(name, key="") + impl = MAP[name] or raise ArgumentError, "hmac not found: #{name.inspect}" + impl.new(key) + end + + # Retrieves the key length for the hmac of the given SSH type (+name+). + def self.key_length(name) + impl = MAP[name] or raise ArgumentError, "hmac not found: #{name.inspect}" + impl.key_length + end +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/identity_cipher.rb File: identity_cipher.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/identity_cipher.rb;tzinfo2 @@ -1,0 +1,55 @@ +module Net; module SSH; module Transport + + # A cipher that does nothing but pass the data through, unchanged. This + # keeps things in the code nice and clean when a cipher has not yet been + # determined (i.e., during key exchange). + class IdentityCipher + class < DiffieHellmanGroupExchangeSHA1, + 'diffie-hellman-group1-sha1' => DiffieHellmanGroup1SHA1 + } + end +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/openssl.rb File: openssl.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/openssl.rb;tzinfo2 @@ -1,0 +1,128 @@ +require 'openssl' +require 'net/ssh/buffer' + +module OpenSSL + + # This class is originally defined in the OpenSSL module. As needed, methods + # have been added to it by the Net::SSH module for convenience in dealing with + # SSH functionality. + class BN + + # Converts a BN object to a string. The format used is that which is + # required by the SSH2 protocol. + def to_ssh + if zero? + return [0].pack("N") + else + buf = to_s(2) + if buf.getbyte(0)[7] == 1 + return [buf.length+1, 0, buf].pack("NCA*") + else + return [buf.length, buf].pack("NA*") + end + end + end + + end + + module PKey + + class PKey + def fingerprint + @fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":") + end + end + + # This class is originally defined in the OpenSSL module. As needed, methods + # have been added to it by the Net::SSH module for convenience in dealing + # with SSH functionality. + class DH + + # Determines whether the pub_key for this key is valid. (This algorithm + # lifted more-or-less directly from OpenSSH, dh.c, dh_pub_is_valid.) + def valid? + return false if pub_key.nil? || pub_key < 0 + bits_set = 0 + pub_key.num_bits.times { |i| bits_set += 1 if pub_key.bit_set?(i) } + return ( bits_set > 1 && pub_key < p ) + end + + end + + # This class is originally defined in the OpenSSL module. As needed, methods + # have been added to it by the Net::SSH module for convenience in dealing + # with SSH functionality. + class RSA + + # Returns "ssh-rsa", which is the description of this key type used by the + # SSH2 protocol. + def ssh_type + "ssh-rsa" + end + + # Converts the key to a blob, according to the SSH2 protocol. + def to_blob + @blob ||= Net::SSH::Buffer.from(:string, ssh_type, :bignum, e, :bignum, n).to_s + end + + # Verifies the given signature matches the given data. + def ssh_do_verify(sig, data) + verify(OpenSSL::Digest::SHA1.new, sig, data) + end + + # Returns the signature for the given data. + def ssh_do_sign(data) + sign(OpenSSL::Digest::SHA1.new, data) + end + end + + # This class is originally defined in the OpenSSL module. As needed, methods + # have been added to it by the Net::SSH module for convenience in dealing + # with SSH functionality. + class DSA + + # Returns "ssh-dss", which is the description of this key type used by the + # SSH2 protocol. + def ssh_type + "ssh-dss" + end + + # Converts the key to a blob, according to the SSH2 protocol. + def to_blob + @blob ||= Net::SSH::Buffer.from(:string, ssh_type, + :bignum, p, :bignum, q, :bignum, g, :bignum, pub_key).to_s + end + + # Verifies the given signature matches the given data. + def ssh_do_verify(sig, data) + sig_r = sig[0,20].unpack("H*")[0].to_i(16) + sig_s = sig[20,20].unpack("H*")[0].to_i(16) + a1sig = OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer(sig_r), + OpenSSL::ASN1::Integer(sig_s) + ]) + return verify(OpenSSL::Digest::DSS1.new, a1sig.to_der, data) + end + + # Signs the given data. + def ssh_do_sign(data) + sig = sign( OpenSSL::Digest::DSS1.new, data) + a1sig = OpenSSL::ASN1.decode( sig ) + + sig_r = a1sig.value[0].value.to_s(2) + sig_s = a1sig.value[1].value.to_s(2) + + if sig_r.length > 20 || sig_s.length > 20 + raise OpenSSL::PKey::DSAError, "bad sig size" + end + + sig_r = "\0" * ( 20 - sig_r.length ) + sig_r if sig_r.length < 20 + sig_s = "\0" * ( 20 - sig_s.length ) + sig_s if sig_s.length < 20 + + return sig_r + sig_s + end + end + + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/packet_stream.rb File: packet_stream.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/packet_stream.rb;tzinfo2 @@ -1,0 +1,230 @@ +require 'net/ssh/buffered_io' +require 'net/ssh/errors' +require 'net/ssh/packet' +require 'net/ssh/transport/cipher_factory' +require 'net/ssh/transport/hmac' +require 'net/ssh/transport/state' + +module Net; module SSH; module Transport + + # A module that builds additional functionality onto the Net::SSH::BufferedIo + # module. It adds SSH encryption, compression, and packet validation, as + # per the SSH2 protocol. It also adds an abstraction for polling packets, + # to allow for both blocking and non-blocking reads. + module PacketStream + include BufferedIo + + def self.extended(object) + object.__send__(:initialize_ssh) + end + + # The map of "hints" that can be used to modify the behavior of the packet + # stream. For instance, when authentication succeeds, an "authenticated" + # hint is set, which is used to determine whether or not to compress the + # data when using the "delayed" compression algorithm. + attr_reader :hints + + # The server state object, which encapsulates the algorithms used to interpret + # packets coming from the server. + attr_reader :server + + # The client state object, which encapsulates the algorithms used to build + # packets to send to the server. + attr_reader :client + + # The name of the client (local) end of the socket, as reported by the + # socket. + def client_name + @client_name ||= begin + sockaddr = getsockname + begin + Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first + rescue + begin + Socket.getnameinfo(sockaddr).first + rescue + begin + Socket.gethostbyname(Socket.gethostname).first + rescue + lwarn { "the client ipaddr/name could not be determined" } + "unknown" + end + end + end + end + end + + # The IP address of the peer (remote) end of the socket, as reported by + # the socket. + def peer_ip + @peer_ip ||= begin + addr = getpeername + Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first + end + end + + # Returns true if the IO is available for reading, and false otherwise. + def available_for_read? + result = IO.select([self], nil, nil, 0) + result && result.first.any? + end + + # Returns the next full packet. If the mode parameter is :nonblock (the + # default), then this will return immediately, whether a packet is + # available or not, and will return nil if there is no packet ready to be + # returned. If the mode parameter is :block, then this method will block + # until a packet is available. + def next_packet(mode=:nonblock) + case mode + when :nonblock then + fill if available_for_read? + poll_next_packet + + when :block then + loop do + packet = poll_next_packet + return packet if packet + + loop do + result = IO.select([self]) or next + break if result.first.any? + end + + if fill <= 0 + raise Net::SSH::Disconnect, "connection closed by remote host" + end + end + + else + raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}" + end + end + + # Enqueues a packet to be sent, and blocks until the entire packet is + # sent. + def send_packet(payload) + enqueue_packet(payload) + wait_for_pending_sends + end + + # Enqueues a packet to be sent, but does not immediately send the packet. + # The given payload is pre-processed according to the algorithms specified + # in the client state (compression, cipher, and hmac). + def enqueue_packet(payload) + # try to compress the packet + payload = client.compress(payload) + + # the length of the packet, minus the padding + actual_length = 4 + payload.length + 1 + + # compute the padding length + padding_length = client.block_size - (actual_length % client.block_size) + padding_length += client.block_size if padding_length < 4 + + # compute the packet length (sans the length field itself) + packet_length = payload.length + padding_length + 1 + + if packet_length < 16 + padding_length += client.block_size + packet_length = payload.length + padding_length + 1 + end + + padding = Array.new(padding_length) { rand(256) }.pack("C*") + + unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*") + mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*")) + + encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher + message = encrypted_data + mac + + debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" } + enqueue(message) + + client.increment(packet_length) + + self + end + + # Performs any pending cleanup necessary on the IO and its associated + # state objects. (See State#cleanup). + def cleanup + client.cleanup + server.cleanup + end + + # If the IO object requires a rekey operation (as indicated by either its + # client or server state objects, see State#needs_rekey?), this will + # yield. Otherwise, this does nothing. + def if_needs_rekey? + if client.needs_rekey? || server.needs_rekey? + yield + client.reset! if client.needs_rekey? + server.reset! if server.needs_rekey? + end + end + + protected + + # Called when this module is used to extend an object. It initializes + # the states and generally prepares the object for use as a packet stream. + def initialize_ssh + @hints = {} + @server = State.new(self, :server) + @client = State.new(self, :client) + @packet = nil + initialize_buffered_io + end + + # Tries to read the next packet. If there is insufficient data to read + # an entire packet, this returns immediately, otherwise the packet is + # read, post-processed according to the cipher, hmac, and compression + # algorithms specified in the server state object, and returned as a + # new Packet object. + def poll_next_packet + if @packet.nil? + minimum = server.block_size < 4 ? 4 : server.block_size + return nil if available < minimum + data = read_available(minimum) + + # decipher it + @packet = Net::SSH::Buffer.new(server.update_cipher(data)) + @packet_length = @packet.read_long + end + + need = @packet_length + 4 - server.block_size + raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0 + + return nil if available < need + server.hmac.mac_length + + if need > 0 + # read the remainder of the packet and decrypt it. + data = read_available(need) + @packet.append(server.update_cipher(data)) + end + + # get the hmac from the tail of the packet (if one exists), and + # then validate it. + real_hmac = read_available(server.hmac.mac_length) || "" + + @packet.append(server.final_cipher) + padding_length = @packet.read_byte + + payload = @packet.read(@packet_length - padding_length - 1) + padding = @packet.read(padding_length) if padding_length > 0 + + my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*")) + raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac + + # try to decompress the payload, in case compression is active + payload = server.decompress(payload) + + debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" } + + server.increment(@packet_length) + @packet = nil + + return Packet.new(payload) + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/server_version.rb File: server_version.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/server_version.rb;tzinfo2 @@ -1,0 +1,70 @@ +require 'net/ssh/errors' +require 'net/ssh/loggable' +require 'net/ssh/version' + +module Net; module SSH; module Transport + + # Negotiates the SSH protocol version and trades information about server + # and client. This is never used directly--it is always called by the + # transport layer as part of the initialization process of the transport + # layer. + # + # Note that this class also encapsulates the negotiated version, and acts as + # the authoritative reference for any queries regarding the version in effect. + class ServerVersion + include Loggable + + # The SSH version string as reported by Net::SSH + PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}" + + # Any header text sent by the server prior to sending the version. + attr_reader :header + + # The version string reported by the server. + attr_reader :version + + # Instantiates a new ServerVersion and immediately (and synchronously) + # negotiates the SSH protocol in effect, using the given socket. + def initialize(socket, logger) + @header = "" + @version = nil + @logger = logger + negotiate!(socket) + end + + private + + # Negotiates the SSH protocol to use, via the given socket. If the server + # reports an incompatible SSH version (e.g., SSH1), this will raise an + # exception. + def negotiate!(socket) + info { "negotiating protocol version" } + + loop do + @version = "" + loop do + b = socket.recv(1) + + if b.nil? + raise Net::SSH::Disconnect, "connection closed by remote host" + end + @version << b + break if b == "\n" + end + break if @version.match(/^SSH-/) + @header << @version + end + + @version.chomp! + debug { "remote is `#{@version}'" } + + unless @version.match(/^SSH-(1\.99|2\.0)-/) + raise Net::SSH::Exception, "incompatible SSH version `#{@version}'" + end + + debug { "local is `#{PROTO_VERSION}'" } + socket.write "#{PROTO_VERSION}\r\n" + socket.flush + end + end +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/session.rb File: session.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/session.rb;tzinfo2 @@ -1,0 +1,276 @@ +require 'socket' +require 'timeout' + +require 'net/ssh/errors' +require 'net/ssh/loggable' +require 'net/ssh/version' +require 'net/ssh/transport/algorithms' +require 'net/ssh/transport/constants' +require 'net/ssh/transport/packet_stream' +require 'net/ssh/transport/server_version' +require 'net/ssh/verifiers/null' +require 'net/ssh/verifiers/strict' +require 'net/ssh/verifiers/lenient' + +module Net; module SSH; module Transport + + # The transport layer represents the lowest level of the SSH protocol, and + # implements basic message exchanging and protocol initialization. It will + # never be instantiated directly (unless you really know what you're about), + # but will instead be created for you automatically when you create a new + # SSH session via Net::SSH.start. + class Session + include Constants, Loggable + + # The standard port for the SSH protocol. + DEFAULT_PORT = 22 + + # The host to connect to, as given to the constructor. + attr_reader :host + + # The port number to connect to, as given in the options to the constructor. + # If no port number was given, this will default to DEFAULT_PORT. + attr_reader :port + + # The underlying socket object being used to communicate with the remote + # host. + attr_reader :socket + + # The ServerVersion instance that encapsulates the negotiated protocol + # version. + attr_reader :server_version + + # The Algorithms instance used to perform key exchanges. + attr_reader :algorithms + + # The host-key verifier object used to verify host keys, to ensure that + # the connection is not being spoofed. + attr_reader :host_key_verifier + + # The hash of options that were given to the object at initialization. + attr_reader :options + + # Instantiates a new transport layer abstraction. This will block until + # the initial key exchange completes, leaving you with a ready-to-use + # transport session. + def initialize(host, options={}) + self.logger = options[:logger] + + @host = host + @port = options[:port] || DEFAULT_PORT + @options = options + + debug { "establishing connection to #{@host}:#{@port}" } + factory = options[:proxy] || TCPSocket + @socket = timeout(options[:timeout] || 0) { factory.open(@host, @port) } + @socket.extend(PacketStream) + @socket.logger = @logger + + debug { "connection established" } + + @queue = [] + + @host_key_verifier = select_host_key_verifier(options[:paranoid]) + + @server_version = ServerVersion.new(socket, logger) + + @algorithms = Algorithms.new(self, options) + wait { algorithms.initialized? } + end + + # Returns the host (and possibly IP address) in a format compatible with + # SSH known-host files. + def host_as_string + @host_as_string ||= begin + string = "#{host}" + string = "[#{string}]:#{port}" if port != DEFAULT_PORT + if socket.peer_ip != host + string2 = socket.peer_ip + string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT + string << "," << string2 + end + string + end + end + + # Returns true if the underlying socket has been closed. + def closed? + socket.closed? + end + + # Cleans up (see PacketStream#cleanup) and closes the underlying socket. + def close + socket.cleanup + socket.close + end + + # Performs a "hard" shutdown of the connection. In general, this should + # never be done, but it might be necessary (in a rescue clause, for instance, + # when the connection needs to close but you don't know the status of the + # underlying protocol's state). + def shutdown! + error { "forcing connection closed" } + socket.close + end + + # Returns a new service_request packet for the given service name, ready + # for sending to the server. + def service_request(service) + Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service) + end + + # Requests a rekey operation, and blocks until the operation completes. + # If a rekey is already pending, this returns immediately, having no + # effect. + def rekey! + if !algorithms.pending? + algorithms.rekey! + wait { algorithms.initialized? } + end + end + + # Returns immediately if a rekey is already in process. Otherwise, if a + # rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?) + # one is performed, causing this method to block until it completes. + def rekey_as_needed + return if algorithms.pending? + socket.if_needs_rekey? { rekey! } + end + + # Returns a hash of information about the peer (remote) side of the socket, + # including :ip, :port, :host, and :canonized (see #host_as_string). + def peer + @peer ||= { :ip => socket.peer_ip, :port => @port.to_i, :host => @host, :canonized => host_as_string } + end + + # Blocks until a new packet is available to be read, and returns that + # packet. See #poll_message. + def next_message + poll_message(:block) + end + + # Tries to read the next packet from the socket. If mode is :nonblock (the + # default), this will not block and will return nil if there are no packets + # waiting to be read. Otherwise, this will block until a packet is + # available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED, + # DEBUG, and KEXINIT) are handled silently by this method, and will never + # be returned. + # + # If a key-exchange is in process and a disallowed packet type is + # received, it will be enqueued and otherwise ignored. When a key-exchange + # is not in process, and consume_queue is true, packets will be first + # read from the queue before the socket is queried. + def poll_message(mode=:nonblock, consume_queue=true) + loop do + if consume_queue && @queue.any? && algorithms.allow?(@queue.first) + return @queue.shift + end + + packet = socket.next_packet(mode) + return nil if packet.nil? + + case packet.type + when DISCONNECT + raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})" + + when IGNORE + debug { "IGNORE packet recieved: #{packet[:data].inspect}" } + + when UNIMPLEMENTED + lwarn { "UNIMPLEMENTED: #{packet[:number]}" } + + when DEBUG + send(packet[:always_display] ? :fatal : :debug) { packet[:message] } + + when KEXINIT + algorithms.accept_kexinit(packet) + + else + return packet if algorithms.allow?(packet) + push(packet) + end + end + end + + # Waits (blocks) until the given block returns true. If no block is given, + # this just waits long enough to see if there are any pending packets. Any + # packets read are enqueued (see #push). + def wait + loop do + break if block_given? && yield + message = poll_message(:nonblock, false) + push(message) if message + break if !block_given? + end + end + + # Adds the given packet to the packet queue. If the queue is non-empty, + # #poll_message will return packets from the queue in the order they + # were received. + def push(packet) + @queue.push(packet) + end + + # Sends the given message via the packet stream, blocking until the + # entire message has been sent. + def send_message(message) + socket.send_packet(message) + end + + # Enqueues the given message, such that it will be sent at the earliest + # opportunity. This does not block, but returns immediately. + def enqueue_message(message) + socket.enqueue_packet(message) + end + + # Configure's the packet stream's client state with the given set of + # options. This is typically used to define the cipher, compression, and + # hmac algorithms to use when sending packets to the server. + def configure_client(options={}) + socket.client.set(options) + end + + # Configure's the packet stream's server state with the given set of + # options. This is typically used to define the cipher, compression, and + # hmac algorithms to use when reading packets from the server. + def configure_server(options={}) + socket.server.set(options) + end + + # Sets a new hint for the packet stream, which the packet stream may use + # to change its behavior. (See PacketStream#hints). + def hint(which, value=true) + socket.hints[which] = value + end + + public + + # this method is primarily for use in tests + attr_reader :queue #:nodoc: + + private + + # Instantiates a new host-key verification class, based on the value of + # the parameter. When true or nil, the default Lenient verifier is + # returned. If it is false, the Null verifier is returned, and if it is + # :very, the Strict verifier is returned. If the argument happens to + # respond to :verify, it is returned directly. Otherwise, an exception + # is raised. + def select_host_key_verifier(paranoid) + case paranoid + when true, nil then + Net::SSH::Verifiers::Lenient.new + when false then + Net::SSH::Verifiers::Null.new + when :very then + Net::SSH::Verifiers::Strict.new + else + if paranoid.respond_to?(:verify) + paranoid + else + raise ArgumentError, "argument to :paranoid is not valid: #{paranoid.inspect}" + end + end + end + end +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/state.rb File: state.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/state.rb;tzinfo2 @@ -1,0 +1,206 @@ +require 'zlib' +require 'net/ssh/transport/cipher_factory' +require 'net/ssh/transport/hmac' + +module Net; module SSH; module Transport + + # Encapsulates state information about one end of an SSH connection. Such + # state includes the packet sequence number, the algorithms in use, how + # many packets and blocks have been processed since the last reset, and so + # forth. This class will never be instantiated directly, but is used as + # part of the internal state of the PacketStream module. + class State + # The socket object that owns this state object. + attr_reader :socket + + # The next packet sequence number for this socket endpoint. + attr_reader :sequence_number + + # The hmac algorithm in use for this endpoint. + attr_reader :hmac + + # The compression algorithm in use for this endpoint. + attr_reader :compression + + # The compression level to use when compressing data (or nil, for the default). + attr_reader :compression_level + + # The number of packets processed since the last call to #reset! + attr_reader :packets + + # The number of data blocks processed since the last call to #reset! + attr_reader :blocks + + # The cipher algorithm in use for this socket endpoint. + attr_reader :cipher + + # The block size for the cipher + attr_reader :block_size + + # The role that this state plays (either :client or :server) + attr_reader :role + + # The maximum number of packets that this endpoint wants to process before + # needing a rekey. + attr_accessor :max_packets + + # The maximum number of blocks that this endpoint wants to process before + # needing a rekey. + attr_accessor :max_blocks + + # The user-specified maximum number of bytes that this endpoint ought to + # process before needing a rekey. + attr_accessor :rekey_limit + + # Creates a new state object, belonging to the given socket. Initializes + # the algorithms to "none". + def initialize(socket, role) + @socket = socket + @role = role + @sequence_number = @packets = @blocks = 0 + @cipher = CipherFactory.get("none") + @block_size = 8 + @hmac = HMAC.get("none") + @compression = nil + @compressor = @decompressor = nil + @next_iv = "" + end + + # A convenience method for quickly setting multiple values in a single + # command. + def set(values) + values.each do |key, value| + instance_variable_set("@#{key}", value) + end + reset! + end + + def update_cipher(data) + result = cipher.update(data) + update_next_iv(role == :client ? result : data) + return result + end + + def final_cipher + result = cipher.final + update_next_iv(role == :client ? result : "", true) + return result + end + + # Increments the counters. The sequence number is incremented (and remapped + # so it always fits in a 32-bit integer). The number of packets and blocks + # are also incremented. + def increment(packet_length) + @sequence_number = (@sequence_number + 1) & 0xFFFFFFFF + @packets += 1 + @blocks += (packet_length + 4) / @block_size + end + + # The compressor object to use when compressing data. This takes into account + # the desired compression level. + def compressor + @compressor ||= Zlib::Deflate.new(compression_level || Zlib::DEFAULT_COMPRESSION) + end + + # The decompressor object to use when decompressing data. + def decompressor + @decompressor ||= Zlib::Inflate.new(nil) + end + + # Returns true if data compression/decompression is enabled. This will + # return true if :standard compression is selected, or if :delayed + # compression is selected and the :authenticated hint has been received + # by the socket. + def compression? + compression == :standard || (compression == :delayed && socket.hints[:authenticated]) + end + + # Compresses the data. If no compression is in effect, this will just return + # the data unmodified, otherwise it uses #compressor to compress the data. + def compress(data) + data = data.to_s + return data unless compression? + compressor.deflate(data, Zlib::SYNC_FLUSH) + end + + # Deompresses the data. If no compression is in effect, this will just return + # the data unmodified, otherwise it uses #decompressor to decompress the data. + def decompress(data) + data = data.to_s + return data unless compression? + decompressor.inflate(data) + end + + # Resets the counters on the state object, but leaves the sequence_number + # unchanged. It also sets defaults for and recomputes the max_packets and + # max_blocks values. + def reset! + @packets = @blocks = 0 + + @max_packets ||= 1 << 31 + + @block_size = cipher.name == "RC4" ? 8 : cipher.block_size + + if max_blocks.nil? + # cargo-culted from openssh. the idea is that "the 2^(blocksize*2) + # limit is too expensive for 3DES, blowfish, etc., so enforce a 1GB + # limit for small blocksizes." + if @block_size >= 16 + @max_blocks = 1 << (@block_size * 2) + else + @max_blocks = (1 << 30) / @block_size + end + + # if a limit on the # of bytes has been given, convert that into a + # minimum number of blocks processed. + + if rekey_limit + @max_blocks = [@max_blocks, rekey_limit / @block_size].min + end + end + + cleanup + end + + # Closes any the compressor and/or decompressor objects that have been + # instantiated. + def cleanup + if @compressor + @compressor.finish if !@compressor.finished? + @compressor.close + end + + if @decompressor + # we call reset here so that we don't get warnings when we try to + # close the decompressor + @decompressor.reset + @decompressor.close + end + + @compressor = @decompressor = nil + end + + # Returns true if the number of packets processed exceeds the maximum + # number of packets, or if the number of blocks processed exceeds the + # maximum number of blocks. + def needs_rekey? + max_packets && packets > max_packets || + max_blocks && blocks > max_blocks + end + + private + + def update_next_iv(data, reset=false) + @next_iv << data + @next_iv = @next_iv[-cipher.iv_len..-1] + + if reset + cipher.reset + cipher.iv = @next_iv + end + + return data + end + end + +end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/hmac/abstract.rb File: abstract.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/hmac/abstract.rb;tzinfo2 @@ -1,0 +1,78 @@ +require 'openssl' + +module Net; module SSH; module Transport; module HMAC + + # The base class of all OpenSSL-based HMAC algorithm wrappers. + class Abstract + + class < MAXIMUM_BITS + need_bits = MAXIMUM_BITS + end + + data[:need_bits ] = need_bits + data[:need_bytes] = need_bits / 8 + end + + # Returns the DH key parameters for the given session. + def get_parameters + compute_need_bits + + # request the DH key parameters for the given number of bits. + buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, MINIMUM_BITS, + :long, data[:need_bits], :long, MAXIMUM_BITS) + connection.send_message(buffer) + + buffer = connection.next_message + unless buffer.type == KEXDH_GEX_GROUP + raise Net::SSH::Exception, "expected KEXDH_GEX_GROUP, got #{buffer.type}" + end + + p = buffer.read_bignum + g = buffer.read_bignum + + [p, g] + end + + # Returns the INIT/REPLY constants used by this algorithm. + def get_message_types + [KEXDH_GEX_INIT, KEXDH_GEX_REPLY] + end + + # Build the signature buffer to use when verifying a signature from + # the server. + def build_signature_buffer(result) + response = Net::SSH::Buffer.new + response.write_string data[:client_version_string], + data[:server_version_string], + data[:client_algorithm_packet], + data[:server_algorithm_packet], + result[:key_blob] + response.write_long MINIMUM_BITS, + data[:need_bits], + MAXIMUM_BITS + response.write_bignum dh.p, dh.g, dh.pub_key, + result[:server_dh_pubkey], + result[:shared_secret] + response + end + end + +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb File: diffie_hellman_group1_sha1.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb;tzinfo2 @@ -1,0 +1,208 @@ +require 'net/ssh/buffer' +require 'net/ssh/errors' +require 'net/ssh/loggable' +require 'net/ssh/transport/openssl' +require 'net/ssh/transport/constants' + +module Net; module SSH; module Transport; module Kex + + # A key-exchange service implementing the "diffie-hellman-group1-sha1" + # key-exchange algorithm. + class DiffieHellmanGroup1SHA1 + include Constants, Loggable + + # The value of 'P', as a string, in hexadecimal + P_s = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" + + "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" + + "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" + + "4FE1356D" "6D51C245" "E485B576" "625E7EC6" + + "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" + + "49286651" "ECE65381" "FFFFFFFF" "FFFFFFFF" + + # The radix in which P_s represents the value of P + P_r = 16 + + # The group constant + G = 2 + + attr_reader :p + attr_reader :g + attr_reader :digester + attr_reader :algorithms + attr_reader :connection + attr_reader :data + attr_reader :dh + + # Create a new instance of the DiffieHellmanGroup1SHA1 algorithm. + # The data is a Hash of symbols representing information + # required by this algorithm, which was acquired during earlier + # processing. + def initialize(algorithms, connection, data) + @p = OpenSSL::BN.new(P_s, P_r) + @g = G + + @digester = OpenSSL::Digest::SHA1 + @algorithms = algorithms + @connection = connection + + @data = data.dup + @dh = generate_key + @logger = @data.delete(:logger) + end + + # Perform the key-exchange for the given session, with the given + # data. This method will return a hash consisting of the + # following keys: + # + # * :session_id + # * :server_key + # * :shared_secret + # * :hashing_algorithm + # + # The caller is expected to be able to understand how to use these + # deliverables. + def exchange_keys + result = send_kexinit + verify_server_key(result[:server_key]) + session_id = verify_signature(result) + confirm_newkeys + + return { :session_id => session_id, + :server_key => result[:server_key], + :shared_secret => result[:shared_secret], + :hashing_algorithm => digester } + end + + private + + # Returns the DH key parameters for the current connection. + def get_parameters + [p, g] + end + + # Returns the INIT/REPLY constants used by this algorithm. + def get_message_types + [KEXDH_INIT, KEXDH_REPLY] + end + + # Build the signature buffer to use when verifying a signature from + # the server. + def build_signature_buffer(result) + response = Net::SSH::Buffer.new + response.write_string data[:client_version_string], + data[:server_version_string], + data[:client_algorithm_packet], + data[:server_algorithm_packet], + result[:key_blob] + response.write_bignum dh.pub_key, + result[:server_dh_pubkey], + result[:shared_secret] + response + end + + # Generate a DH key with a private key consisting of the given + # number of bytes. + def generate_key #:nodoc: + dh = OpenSSL::PKey::DH.new + + dh.p, dh.g = get_parameters + dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8) + + dh.generate_key! until dh.valid? + + dh + end + + # Send the KEXDH_INIT message, and expect the KEXDH_REPLY. Return the + # resulting buffer. + # + # Parse the buffer from a KEXDH_REPLY message, returning a hash of + # the extracted values. + def send_kexinit #:nodoc: + init, reply = get_message_types + + # send the KEXDH_INIT message + buffer = Net::SSH::Buffer.from(:byte, init, :bignum, dh.pub_key) + connection.send_message(buffer) + + # expect the KEXDH_REPLY message + buffer = connection.next_message + raise Net::SSH::Exception, "expected REPLY" unless buffer.type == reply + + result = Hash.new + + result[:key_blob] = buffer.read_string + result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key + result[:server_dh_pubkey] = buffer.read_bignum + result[:shared_secret] = OpenSSL::BN.new(dh.compute_key(result[:server_dh_pubkey]), 2) + + sig_buffer = Net::SSH::Buffer.new(buffer.read_string) + sig_type = sig_buffer.read_string + if sig_type != algorithms.host_key + raise Net::SSH::Exception, + "host key algorithm mismatch for signature " + + "'#{sig_type}' != '#{algorithms.host_key}'" + end + result[:server_sig] = sig_buffer.read_string + + return result + end + + # Verify that the given key is of the expected type, and that it + # really is the key for the session's host. Raise Net::SSH::Exception + # if it is not. + def verify_server_key(key) #:nodoc: + if key.ssh_type != algorithms.host_key + raise Net::SSH::Exception, + "host key algorithm mismatch " + + "'#{key.ssh_type}' != '#{algorithms.host_key}'" + end + + blob, fingerprint = generate_key_fingerprint(key) + + unless connection.host_key_verifier.verify(:key => key, :key_blob => blob, :fingerprint => fingerprint, :session => connection) + raise Net::SSH::Exception, "host key verification failed" + end + end + + def generate_key_fingerprint(key) + blob = Net::SSH::Buffer.from(:key, key).to_s + fingerprint = OpenSSL::Digest::MD5.hexdigest(blob).scan(/../).join(":") + + [blob, fingerprint] + rescue ::Exception => e + [nil, "(could not generate fingerprint: #{e.message})"] + end + + # Verify the signature that was received. Raise Net::SSH::Exception + # if the signature could not be verified. Otherwise, return the new + # session-id. + def verify_signature(result) #:nodoc: + response = build_signature_buffer(result) + + hash = @digester.digest(response.to_s) + + unless result[:server_key].ssh_do_verify(result[:server_sig], hash) + raise Net::SSH::Exception, "could not verify server signature" + end + + return hash + end + + # Send the NEWKEYS message, and expect the NEWKEYS message in + # reply. + def confirm_newkeys #:nodoc: + # send own NEWKEYS message first (the wodSSHServer won't send first) + response = Net::SSH::Buffer.new + response.write_byte(NEWKEYS) + connection.send_message(response) + + # wait for the server's NEWKEYS message + buffer = connection.next_message + raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS + end + end + +end; end; end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/verifiers/lenient.rb File: lenient.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/verifiers/lenient.rb;tzinfo2 @@ -1,0 +1,30 @@ +require 'net/ssh/verifiers/strict' + +module Net; module SSH; module Verifiers + + # Basically the same as the Strict verifier, but does not try to actually + # verify a connection if the server is the localhost and the port is a + # nonstandard port number. Those two conditions will typically mean the + # connection is being tunnelled through a forwarded port, so the known-hosts + # file will not be helpful (in general). + class Lenient < Strict + # Tries to determine if the connection is being tunnelled, and if so, + # returns true. Otherwise, performs the standard strict verification. + def verify(arguments) + return true if tunnelled?(arguments) + super + end + + private + + # A connection is potentially being tunnelled if the port is not 22, + # and the ip refers to the localhost. + def tunnelled?(args) + return false if args[:session].port == Net::SSH::Transport::Session::DEFAULT_PORT + + ip = args[:session].peer[:ip] + return ip == "127.0.0.1" || ip == "::1" + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/verifiers/null.rb File: null.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/verifiers/null.rb;tzinfo2 @@ -1,0 +1,12 @@ +module Net; module SSH; module Verifiers + + # The Null host key verifier simply allows every key it sees, without + # bothering to verify. This is simple, but is not particularly secure. + class Null + # Returns true. + def verify(arguments) + true + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/verifiers/strict.rb File: strict.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/lib/net/ssh/verifiers/strict.rb;tzinfo2 @@ -1,0 +1,53 @@ +require 'net/ssh/errors' +require 'net/ssh/known_hosts' + +module Net; module SSH; module Verifiers + + # Does a strict host verification, looking the server up in the known + # host files to see if a key has already been seen for this server. If this + # server does not appear in any host file, this will silently add the + # server. If the server does appear at least once, but the key given does + # not match any known for the server, an exception will be raised (HostKeyMismatch). + # Otherwise, this returns true. + class Strict + def verify(arguments) + options = arguments[:session].options + host = options[:host_key_alias] || arguments[:session].host_as_string + matches = Net::SSH::KnownHosts.search_for(host, arguments[:session].options) + + # we've never seen this host before, so just automatically add the key. + # not the most secure option (since the first hit might be the one that + # is hacked), but since almost nobody actually compares the key + # fingerprint, this is a reasonable compromise between usability and + # security. + if matches.empty? + ip = arguments[:session].peer[:ip] + Net::SSH::KnownHosts.add(host, arguments[:key], arguments[:session].options) + return true + end + + # If we found any matches, check to see that the key type and + # blob also match. + found = matches.any? do |key| + key.ssh_type == arguments[:key].ssh_type && + key.to_blob == arguments[:key].to_blob + end + + # If a match was found, return true. Otherwise, raise an exception + # indicating that the key was not recognized. + found || process_cache_miss(host, arguments) + end + + private + + def process_cache_miss(host, args) + exception = HostKeyMismatch.new("fingerprint #{args[:fingerprint]} does not match for #{host.inspect}") + exception.data = args + exception.callback = Proc.new do + Net::SSH::KnownHosts.add(host, args[:key], args[:session].options) + end + raise exception + end + end + +end; end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/support/arcfour_check.rb File: arcfour_check.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/support/arcfour_check.rb;tzinfo2 @@ -1,0 +1,20 @@ + +require 'net/ssh' + +# ARCFOUR CHECK +# +# Usage: +# $ ruby support/arcfour_check.rb +# +# Expected Output: +# arcfour128: [16, 8] OpenSSL::Cipher::Cipher +# arcfour256: [32, 8] OpenSSL::Cipher::Cipher +# arcfour512: [64, 8] OpenSSL::Cipher::Cipher + +[['arcfour128', 16], ['arcfour256', 32], ['arcfour512', 64]].each do |cipher| + print "#{cipher[0]}: " + a = Net::SSH::Transport::CipherFactory.get_lengths(cipher[0]) + b = Net::SSH::Transport::CipherFactory.get(cipher[0], :key => ([].fill('x', 0, cipher[1]).join)) + puts "#{a} #{b.class}" +end + =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/common.rb File: common.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/common.rb;tzinfo2 @@ -1,0 +1,107 @@ +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +gem "test-unit" # http://rubyforge.org/pipermail/test-unit-tracker/2009-July/000075.html +require 'test/unit' +require 'mocha' +require 'net/ssh/buffer' +require 'net/ssh/config' +require 'net/ssh/loggable' +require 'net/ssh/packet' +require 'net/ssh/transport/session' +require 'ostruct' + +# clear the default files out so that tests don't get confused by existing +# SSH config files. +$original_config_default_files = Net::SSH::Config.default_files.dup +Net::SSH::Config.default_files.clear + +def P(*args) + Net::SSH::Packet.new(Net::SSH::Buffer.from(*args)) +end + +class MockTransport < Net::SSH::Transport::Session + class BlockVerifier + def initialize(block) + @block = block + end + + def verify(data) + @block.call(data) + end + end + + attr_reader :host_key_verifier + attr_accessor :host_as_string + attr_accessor :server_version + + attr_reader :client_options + attr_reader :server_options + attr_reader :hints, :queue + + attr_accessor :mock_enqueue + + def initialize(options={}) + self.logger = options[:logger] + self.host_as_string = "net.ssh.test,127.0.0.1" + self.server_version = OpenStruct.new(:version => "SSH-2.0-Ruby/Net::SSH::Test") + @expectation = nil + @queue = [] + @hints = {} + @socket = options[:socket] + @algorithms = OpenStruct.new(:session_id => "abcxyz123") + verifier { |data| true } + end + + def send_message(message) + buffer = Net::SSH::Buffer.new(message.to_s) + if @expectation.nil? + raise "got #{message.to_s.inspect} but was not expecting anything" + else + block, @expectation = @expectation, nil + block.call(self, Net::SSH::Packet.new(buffer)) + end + end + + def enqueue_message(message) + if mock_enqueue + send_message(message) + else + super + end + end + + def poll_message + @queue.shift + end + + def next_message + @queue.shift or raise "expected a message from the server but nothing was ready to send" + end + + def return(type, *args) + @queue << P(:byte, type, *args) + end + + def expect(&block) + @expectation = block + end + + def expect! + expect {} + end + + def verifier(&block) + @host_key_verifier = BlockVerifier.new(block) + end + + def configure_client(options) + @client_options = options + end + + def configure_server(options) + @server_options = options + end + + def hint(name, value=true) + @hints[name] = value + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/connection add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_all.rb File: test_all.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_all.rb;tzinfo2 @@ -1,0 +1,8 @@ +# $ ruby -Ilib -Itest -rrubygems test/test_all.rb +# $ ruby -Ilib -Itest -rrubygems test/transport/test_server_version.rb +Dir.chdir(File.dirname(__FILE__)) do + test_files = Dir['**/test_*.rb'] + test_files = test_files.select { |f| f =~ Regexp.new(ENV['ONLY']) } if ENV['ONLY'] + test_files = test_files.reject { |f| f =~ Regexp.new(ENV['EXCEPT']) } if ENV['EXCEPT'] + test_files.each { |file| require(file) } +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_buffer.rb File: test_buffer.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_buffer.rb;tzinfo2 @@ -1,0 +1,336 @@ +require 'common' +require 'net/ssh/buffer' + +class TestBuffer < Test::Unit::TestCase + def test_constructor_should_initialize_buffer_to_empty_by_default + buffer = new + assert buffer.empty? + assert_equal 0, buffer.position + end + + def test_constructor_with_string_should_initialize_buffer_to_the_string + buffer = new("hello") + assert !buffer.empty? + assert_equal "hello", buffer.to_s + assert_equal 0, buffer.position + end + + def test_from_should_require_an_even_number_of_arguments + assert_raises(ArgumentError) { Net::SSH::Buffer.from("this") } + end + + def test_from_should_build_new_buffer_from_definition + buffer = Net::SSH::Buffer.from(:byte, 1, :long, 2, :int64, 3, :string, "4", :bool, true, :bool, false, :bignum, OpenSSL::BN.new("1234567890", 10), :raw, "something") + assert_equal "\1\0\0\0\2\0\0\0\0\0\0\0\3\0\0\0\0014\1\0\000\000\000\004I\226\002\322something", buffer.to_s + end + + def test_from_with_array_argument_should_write_multiple_of_the_given_type + buffer = Net::SSH::Buffer.from(:byte, [1,2,3,4,5]) + assert_equal "\1\2\3\4\5", buffer.to_s + end + + def test_read_without_argument_should_read_to_end + buffer = new("hello world") + assert_equal "hello world", buffer.read + assert buffer.eof? + assert_equal 11, buffer.position + end + + def test_read_with_argument_that_is_less_than_length_should_read_that_many_bytes + buffer = new "hello world" + assert_equal "hello", buffer.read(5) + assert_equal 5, buffer.position + end + + def test_read_with_argument_that_is_more_than_length_should_read_no_more_than_length + buffer = new "hello world" + assert_equal "hello world", buffer.read(500) + assert_equal 11, buffer.position + end + + def test_read_at_eof_should_return_empty_string + buffer = new "hello" + buffer.position = 5 + assert_equal "", buffer.read + end + + def test_consume_without_argument_should_resize_buffer_to_start_at_position + buffer = new "hello world" + buffer.read(5) + assert_equal 5, buffer.position + assert_equal 11, buffer.length + buffer.consume! + assert_equal 0, buffer.position + assert_equal 6, buffer.length + assert_equal " world", buffer.to_s + end + + def test_consume_with_argument_should_resize_buffer_starting_at_n + buffer = new "hello world" + assert_equal 0, buffer.position + buffer.consume!(5) + assert_equal 0, buffer.position + assert_equal 6, buffer.length + assert_equal " world", buffer.to_s + end + + def test_read_bang_should_read_and_consume_and_return_read_portion + buffer = new "hello world" + assert_equal "hello", buffer.read!(5) + assert_equal 0, buffer.position + assert_equal 6, buffer.length + assert_equal " world", buffer.to_s + end + + def test_available_should_return_length_after_position_to_end_of_string + buffer = new "hello world" + buffer.read(5) + assert_equal 6, buffer.available + end + + def test_clear_bang_should_reset_buffer_contents_and_counters + buffer = new "hello world" + buffer.read(5) + buffer.clear! + assert_equal 0, buffer.length + assert_equal 0, buffer.position + assert_equal "", buffer.to_s + end + + def test_append_should_append_argument_without_changing_position_and_should_return_self + buffer = new "hello world" + buffer.read(5) + buffer.append(" again") + assert_equal 5, buffer.position + assert_equal 12, buffer.available + assert_equal 17, buffer.length + assert_equal "hello world again", buffer.to_s + end + + def test_remainder_as_buffer_should_return_a_new_buffer_filled_with_the_text_after_the_current_position + buffer = new "hello world" + buffer.read(6) + b2 = buffer.remainder_as_buffer + assert_equal 6, buffer.position + assert_equal 0, b2.position + assert_equal "world", b2.to_s + end + + def test_read_int64_should_return_8_byte_integer + buffer = new "\xff\xee\xdd\xcc\xbb\xaa\x99\x88" + assert_equal 0xffeeddccbbaa9988, buffer.read_int64 + assert_equal 8, buffer.position + end + + def test_read_int64_should_return_nil_on_partial_read + buffer = new "\0\0\0\0\0\0\0" + assert_nil buffer.read_int64 + assert buffer.eof? + end + + def test_read_long_should_return_4_byte_integer + buffer = new "\xff\xee\xdd\xcc\xbb\xaa\x99\x88" + assert_equal 0xffeeddcc, buffer.read_long + assert_equal 4, buffer.position + end + + def test_read_long_should_return_nil_on_partial_read + buffer = new "\0\0\0" + assert_nil buffer.read_long + assert buffer.eof? + end + + def test_read_byte_should_return_single_byte_integer + buffer = new "\xfe\xdc" + assert_equal 0xfe, buffer.read_byte + assert_equal 1, buffer.position + end + + def test_read_byte_should_return_nil_at_eof + assert_nil new.read_byte + end + + def test_read_string_should_read_length_and_data_from_buffer + buffer = new "\0\0\0\x0bhello world" + assert_equal "hello world", buffer.read_string + end + + def test_read_string_should_return_nil_if_4_byte_length_cannot_be_read + assert_nil new("\0\1").read_string + end + + def test_read_bool_should_return_true_if_non_zero_byte_is_read + buffer = new "\1\2\3\4\5\6" + 6.times { assert_equal true, buffer.read_bool } + end + + def test_read_bool_should_return_false_if_zero_byte_is_read + buffer = new "\0" + assert_equal false, buffer.read_bool + end + + def test_read_bool_should_return_nil_at_eof + assert_nil new.read_bool + end + + def test_read_bignum_should_read_openssl_formatted_bignum + buffer = new("\000\000\000\004I\226\002\322") + assert_equal OpenSSL::BN.new("1234567890", 10), buffer.read_bignum + end + + def test_read_bignum_should_return_nil_if_length_cannot_be_read + assert_nil new("\0\1\2").read_bignum + end + + def test_read_key_blob_should_read_dsa_keys + random_dss { |buffer| buffer.read_keyblob("ssh-dss") } + end + + def test_read_key_blob_should_read_rsa_keys + random_rsa { |buffer| buffer.read_keyblob("ssh-rsa") } + end + + def test_read_key_should_read_dsa_key_type_and_keyblob + random_dss do |buffer| + b2 = Net::SSH::Buffer.from(:string, "ssh-dss", :raw, buffer) + b2.read_key + end + end + + def test_read_key_should_read_rsa_key_type_and_keyblob + random_rsa do |buffer| + b2 = Net::SSH::Buffer.from(:string, "ssh-rsa", :raw, buffer) + b2.read_key + end + end + + def test_read_buffer_should_read_a_string_and_return_it_wrapped_in_a_buffer + buffer = new("\0\0\0\x0bhello world") + b2 = buffer.read_buffer + assert_equal 0, b2.position + assert_equal 11, b2.length + assert_equal "hello world", b2.read + end + + def test_read_to_should_return_nil_if_pattern_does_not_exist_in_buffer + buffer = new("one two three") + assert_nil buffer.read_to("\n") + end + + def test_read_to_should_grok_string_patterns + buffer = new("one two three") + assert_equal "one tw", buffer.read_to("tw") + assert_equal 6, buffer.position + end + + def test_read_to_should_grok_regex_patterns + buffer = new("one two three") + assert_equal "one tw", buffer.read_to(/tw/) + assert_equal 6, buffer.position + end + + def test_read_to_should_grok_fixnum_patterns + buffer = new("one two three") + assert_equal "one tw", buffer.read_to(?w) + assert_equal 6, buffer.position + end + + def test_reset_bang_should_reset_position_to_0 + buffer = new("hello world") + buffer.read(5) + assert_equal 5, buffer.position + buffer.reset! + assert_equal 0, buffer.position + end + + def test_write_should_write_arguments_directly_to_end_buffer + buffer = new("start") + buffer.write "hello", " ", "world" + assert_equal "starthello world", buffer.to_s + assert_equal 0, buffer.position + end + + def test_write_int64_should_write_arguments_as_8_byte_integers_to_end_of_buffer + buffer = new("start") + buffer.write_int64 0xffeeddccbbaa9988, 0x7766554433221100 + assert_equal "start\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00", buffer.to_s + end + + def test_write_long_should_write_arguments_as_4_byte_integers_to_end_of_buffer + buffer = new("start") + buffer.write_long 0xffeeddcc, 0xbbaa9988 + assert_equal "start\xff\xee\xdd\xcc\xbb\xaa\x99\x88", buffer.to_s + end + + def test_write_byte_should_write_arguments_as_1_byte_integers_to_end_of_buffer + buffer = new("start") + buffer.write_byte 1, 2, 3, 4, 5 + assert_equal "start\1\2\3\4\5", buffer.to_s + end + + def test_write_bool_should_write_arguments_as_1_byte_boolean_values_to_end_of_buffer + buffer = new("start") + buffer.write_bool nil, false, true, 1, Object.new + assert_equal "start\0\0\1\1\1", buffer.to_s + end + + def test_write_bignum_should_write_arguments_as_ssh_formatted_bignum_values_to_end_of_buffer + buffer = new("start") + buffer.write_bignum OpenSSL::BN.new('1234567890', 10) + assert_equal "start\000\000\000\004I\226\002\322", buffer.to_s + end + + def test_write_dss_key_should_write_argument_to_end_of_buffer + buffer = new("start") + + key = OpenSSL::PKey::DSA.new + key.p = 0xffeeddccbbaa9988 + key.q = 0x7766554433221100 + key.g = 0xffddbb9977553311 + key.pub_key = 0xeeccaa8866442200 + + buffer.write_key(key) + assert_equal "start\0\0\0\7ssh-dss\0\0\0\011\0\xff\xee\xdd\xcc\xbb\xaa\x99\x88\0\0\0\010\x77\x66\x55\x44\x33\x22\x11\x00\0\0\0\011\0\xff\xdd\xbb\x99\x77\x55\x33\x11\0\0\0\011\0\xee\xcc\xaa\x88\x66\x44\x22\x00", buffer.to_s + end + + def test_write_rsa_key_should_write_argument_to_end_of_buffer + buffer = new("start") + + key = OpenSSL::PKey::RSA.new + key.e = 0xffeeddccbbaa9988 + key.n = 0x7766554433221100 + + buffer.write_key(key) + assert_equal "start\0\0\0\7ssh-rsa\0\0\0\011\0\xff\xee\xdd\xcc\xbb\xaa\x99\x88\0\0\0\010\x77\x66\x55\x44\x33\x22\x11\x00", buffer.to_s + end + + private + + def random_rsa + n1 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) + n2 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) + buffer = Net::SSH::Buffer.from(:bignum, [n1, n2]) + key = yield(buffer) + assert_equal "ssh-rsa", key.ssh_type + assert_equal n1, key.e + assert_equal n2, key.n + end + + def random_dss + n1 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) + n2 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) + n3 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) + n4 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) + buffer = Net::SSH::Buffer.from(:bignum, [n1, n2, n3, n4]) + key = yield(buffer) + assert_equal "ssh-dss", key.ssh_type + assert_equal n1, key.p + assert_equal n2, key.q + assert_equal n3, key.g + assert_equal n4, key.pub_key + end + + def new(*args) + Net::SSH::Buffer.new(*args) + end +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_buffered_io.rb File: test_buffered_io.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_buffered_io.rb;tzinfo2 @@ -1,0 +1,63 @@ +require 'common' +require 'net/ssh/buffered_io' + +class TestBufferedIo < Test::Unit::TestCase + def test_fill_should_pull_from_underlying_io + io.expects(:recv).with(8192).returns("here is some data") + assert_equal 17, io.fill + assert_equal 17, io.available + assert_equal "here is some data", io.read_available(20) + end + + def test_enqueue_should_not_write_to_underlying_io + assert !io.pending_write? + io.expects(:send).never + io.enqueue("here is some data") + assert io.pending_write? + end + + def test_send_pending_should_not_fail_when_no_writes_are_pending + assert !io.pending_write? + io.expects(:send).never + assert_nothing_raised { io.send_pending } + end + + def test_send_pending_with_pending_writes_should_write_to_underlying_io + io.enqueue("here is some data") + io.expects(:send).with("here is some data", 0).returns(17) + assert io.pending_write? + assert_nothing_raised { io.send_pending } + assert !io.pending_write? + end + + def test_wait_for_pending_sends_should_write_only_once_if_all_can_be_written_at_once + io.enqueue("here is some data") + io.expects(:send).with("here is some data", 0).returns(17) + assert io.pending_write? + assert_nothing_raised { io.wait_for_pending_sends } + assert !io.pending_write? + end + + def test_wait_for_pending_sends_should_write_multiple_times_if_first_write_was_partial + io.enqueue("here is some data") + + io.expects(:send).with("here is some data", 0).returns(10) + io.expects(:send).with("me data", 0).returns(4) + io.expects(:send).with("ata", 0).returns(3) + + IO.expects(:select).times(2).with(nil, [io]).returns([[], [io]]) + + assert_nothing_raised { io.wait_for_pending_sends } + assert !io.pending_write? + end + + private + + def io + @io ||= begin + io = mock("io") + io.extend(Net::SSH::BufferedIo) + io + end + end +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_config.rb File: test_config.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_config.rb;tzinfo2 @@ -1,0 +1,99 @@ +require 'common' +require 'net/ssh/config' + +class TestConfig < Test::Unit::TestCase + def test_load_for_non_existant_file_should_return_empty_hash + File.expects(:readable?).with("/bogus/file").returns(false) + assert_equal({}, Net::SSH::Config.load("/bogus/file", "host.name")) + end + + def test_load_should_expand_path + expected = File.expand_path("~/.ssh/config") + File.expects(:readable?).with(expected).returns(false) + Net::SSH::Config.load("~/.ssh/config", "host.name") + end + + def test_load_with_exact_host_match_should_load_that_section + config = Net::SSH::Config.load(config(:exact_match), "test.host") + assert config['compression'] + assert config['forwardagent'] + assert_equal 1234, config['port'] + end + + def test_load_with_wild_card_matches_should_load_all_matches_with_first_match_taking_precedence + config = Net::SSH::Config.load(config(:wild_cards), "test.host") + assert_equal 1234, config['port'] + assert !config['compression'] + assert config['forwardagent'] + assert_equal %w(~/.ssh/id_dsa), config['identityfile'] + assert !config.key?('rekeylimit') + end + + def test_for_should_load_all_files_and_translate_to_net_ssh_options + config = Net::SSH::Config.for("test.host", [config(:exact_match), config(:wild_cards)]) + assert_equal 1234, config[:port] + assert config[:compression] + assert config[:forward_agent] + assert_equal %w(~/.ssh/id_dsa), config[:keys] + assert !config.key?(:rekey_limit) + end + + def test_load_with_multiple_hosts + config = Net::SSH::Config.load(config(:multihost), "test.host") + assert config['compression'] + assert_equal '2G', config['rekeylimit'] + assert_equal 1980, config['port'] + end + + def test_load_with_multiple_hosts_and_config_should_match_for_both + aconfig = Net::SSH::Config.load(config(:multihost), "test.host") + bconfig = Net::SSH::Config.load(config(:multihost), "other.host") + assert_equal aconfig['port'], bconfig['port'] + assert_equal aconfig['compression'], bconfig['compression'] + assert_equal aconfig['rekeylimit'], bconfig['rekeylimit'] + end + + def test_load_should_parse_equal_sign_delimiters + config = Net::SSH::Config.load(config(:eqsign), "test.test") + assert config['compression'] + assert_equal 1234, config['port'] + end + + def test_translate_should_correctly_translate_from_openssh_to_net_ssh_names + open_ssh = { + 'ciphers' => "a,b,c", + 'compression' => true, + 'compressionlevel' => 6, + 'connecttimeout' => 100, + 'forwardagent' => true, + 'hostbasedauthentication' => true, + 'hostkeyalgorithms' => "d,e,f", + 'identityfile' => %w(g h i), + 'macs' => "j,k,l", + 'passwordauthentication' => true, + 'port' => 1234, + 'pubkeyauthentication' => true, + 'rekeylimit' => 1024 + } + + net_ssh = Net::SSH::Config.translate(open_ssh) + + assert_equal %w(a b c), net_ssh[:encryption] + assert_equal true, net_ssh[:compression] + assert_equal 6, net_ssh[:compression_level] + assert_equal 100, net_ssh[:timeout] + assert_equal true, net_ssh[:forward_agent] + assert_equal %w(hostbased password publickey), net_ssh[:auth_methods].sort + assert_equal %w(d e f), net_ssh[:host_key] + assert_equal %w(g h i), net_ssh[:keys] + assert_equal %w(j k l), net_ssh[:hmac] + assert_equal 1234, net_ssh[:port] + assert_equal 1024, net_ssh[:rekey_limit] + end + + private + + def config(name) + "test/configs/#{name}" + end +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_key_factory.rb File: test_key_factory.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/test_key_factory.rb;tzinfo2 @@ -1,0 +1,67 @@ +require 'common' +require 'net/ssh/key_factory' + +class TestKeyFactory < Test::Unit::TestCase + def test_load_unencrypted_private_RSA_key_should_return_key + File.expects(:read).with("/key-file").returns(rsa_key.export) + assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + end + + def test_load_unencrypted_private_DSA_key_should_return_key + File.expects(:read).with("/key-file").returns(dsa_key.export) + assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + end + + def test_load_encrypted_private_RSA_key_should_prompt_for_password_and_return_key + File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password")) + Net::SSH::KeyFactory.expects(:prompt).with("Enter passphrase for /key-file:", false).returns("password") + assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + end + + def test_load_encrypted_private_RSA_key_with_password_should_not_prompt_and_return_key + File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password")) + assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file", "password").to_der + end + + def test_load_encrypted_private_DSA_key_should_prompt_for_password_and_return_key + File.expects(:read).with("/key-file").returns(encrypted(dsa_key, "password")) + Net::SSH::KeyFactory.expects(:prompt).with("Enter passphrase for /key-file:", false).returns("password") + assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der + end + + def test_load_encrypted_private_DSA_key_with_password_should_not_prompt_and_return_key + File.expects(:read).with("/key-file").returns(encrypted(dsa_key, "password")) + assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file", "password").to_der + end + + def test_load_encrypted_private_key_should_give_three_tries_for_the_password_and_then_raise_exception + File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password")) + Net::SSH::KeyFactory.expects(:prompt).times(3).with("Enter passphrase for /key-file:", false).returns("passwod","passphrase","passwd") + assert_raises(OpenSSL::PKey::RSAError) { Net::SSH::KeyFactory.load_private_key("/key-file") } + end + + def test_load_public_rsa_key_should_return_key + File.expects(:read).with("/key-file").returns(public(rsa_key)) + assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key("/key-file").to_blob + end + + private + + def rsa_key + @rsa_key ||= OpenSSL::PKey::RSA.new("0@\002\001\000\002\t\000\300\030\317\2132\340 \267\002\003\001\000\001\002\t\000\236~\232\025\350Y=\341\002\005\000\352D\217\a\002\005\000\321\352\304\321\002\005\000\242\350\206%\002\005\000\270\021\217\361\002\004~\253\214j") + end + + def dsa_key + @dsa_key ||= OpenSSL::PKey::DSA.new("0\201\367\002\001\000\002A\000\203\316/\037u\272&J\265\003l3\315d\324h\372{\t8\252#\331_\026\006\035\270\266\255\343\353Z\302\276\335\336\306\220\375\202L\244\244J\206>\346\b\315\211\302L\246x\247u\a\376\366\345\302\016#\002\025\000\244\274\302\221Og\275/\302+\356\346\360\024\373wI\2573\361\002@\027\215\270r*\f\213\350C\245\021:\350 \006\\\376\345\022`\210b\262\3643\023XLKS\320\370\002\276\347A\nU\204\276\324\256`=\026\240\330\306J\316V\213\024\e\030\215\355\006\037q\337\356ln\002@\017\257\034\f\260\333'S\271#\237\230E\321\312\027\021\226\331\251Vj\220\305\316\036\v\266+\000\230\270\177B\003?t\a\305]e\344\261\334\023\253\323\251\223M\2175)a(\004\"lI8\312\303\307\a\002\024_\aznW\345\343\203V\326\246ua\203\376\201o\350\302\002") + end + + def encrypted(key, password) + key.export(OpenSSL::Cipher::Cipher.new("des-ede3-cbc"), password) + end + + def public(key) + result = "#{key.ssh_type} " + result << [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").strip.tr("\n\r\t ", "") + result << " joe@host.test" + end +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/test_agent.rb File: test_agent.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/test_agent.rb;tzinfo2 @@ -1,0 +1,205 @@ +require 'common' +require 'net/ssh/authentication/agent' + +module Authentication + + class TestAgent < Test::Unit::TestCase + + SSH2_AGENT_REQUEST_VERSION = 1 + SSH2_AGENT_REQUEST_IDENTITIES = 11 + SSH2_AGENT_IDENTITIES_ANSWER = 12 + SSH2_AGENT_SIGN_REQUEST = 13 + SSH2_AGENT_SIGN_RESPONSE = 14 + SSH2_AGENT_FAILURE = 30 + SSH2_AGENT_VERSION_RESPONSE = 103 + + SSH_COM_AGENT2_FAILURE = 102 + + SSH_AGENT_REQUEST_RSA_IDENTITIES = 1 + SSH_AGENT_RSA_IDENTITIES_ANSWER = 2 + SSH_AGENT_FAILURE = 5 + + def setup + @original, ENV['SSH_AUTH_SOCK'] = ENV['SSH_AUTH_SOCK'], "/path/to/ssh.agent.sock" + end + + def teardown + ENV['SSH_AUTH_SOCK'] = @original + end + + def test_connect_should_use_agent_factory_to_determine_connection_type + factory.expects(:open).with("/path/to/ssh.agent.sock").returns(socket) + agent(false).connect! + end + + def test_connect_should_raise_error_if_connection_could_not_be_established + factory.expects(:open).raises(SocketError) + assert_raises(Net::SSH::Authentication::AgentNotAvailable) { agent(false).connect! } + end + + def test_negotiate_should_raise_error_if_ssh2_agent_response_recieved + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_VERSION, type + assert_equal Net::SSH::Transport::ServerVersion::PROTO_VERSION, buffer.read_string + s.return(SSH2_AGENT_VERSION_RESPONSE) + end + assert_raises(NotImplementedError) { agent.negotiate! } + end + + def test_negotiate_should_raise_error_if_response_was_unexpected + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_VERSION, type + s.return(255) + end + assert_raises(Net::SSH::Authentication::AgentError) { agent.negotiate! } + end + + def test_negotiate_should_be_successful_with_expected_response + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_VERSION, type + s.return(SSH_AGENT_RSA_IDENTITIES_ANSWER) + end + assert_nothing_raised { agent(:connect).negotiate! } + end + + def test_identities_should_fail_if_SSH_AGENT_FAILURE_recieved + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type + s.return(SSH_AGENT_FAILURE) + end + assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } + end + + def test_identities_should_fail_if_SSH2_AGENT_FAILURE_recieved + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type + s.return(SSH2_AGENT_FAILURE) + end + assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } + end + + def test_identities_should_fail_if_SSH_COM_AGENT2_FAILURE_recieved + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type + s.return(SSH_COM_AGENT2_FAILURE) + end + assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } + end + + def test_identities_should_fail_if_response_is_not_SSH2_AGENT_IDENTITIES_ANSWER + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type + s.return(255) + end + assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } + end + + def test_identities_should_augment_identities_with_comment_field + key1 = key + key2 = OpenSSL::PKey::DSA.new(32) + + socket.expect do |s, type, buffer| + assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type + s.return(SSH2_AGENT_IDENTITIES_ANSWER, :long, 2, :string, Net::SSH::Buffer.from(:key, key1), :string, "My favorite key", :string, Net::SSH::Buffer.from(:key, key2), :string, "Okay, but not the best") + end + + result = agent.identities + assert_equal key1.to_blob, result.first.to_blob + assert_equal key2.to_blob, result.last.to_blob + assert_equal "My favorite key", result.first.comment + assert_equal "Okay, but not the best", result.last.comment + end + + def test_close_should_close_socket + socket.expects(:close) + agent.close + end + + def test_sign_should_fail_if_response_is_SSH_AGENT_FAILURE + socket.expect { |s,| s.return(SSH_AGENT_FAILURE) } + assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } + end + + def test_sign_should_fail_if_response_is_SSH2_AGENT_FAILURE + socket.expect { |s,| s.return(SSH2_AGENT_FAILURE) } + assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } + end + + def test_sign_should_fail_if_response_is_SSH_COM_AGENT2_FAILURE + socket.expect { |s,| s.return(SSH_COM_AGENT2_FAILURE) } + assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } + end + + def test_sign_should_fail_if_response_is_not_SSH2_AGENT_SIGN_RESPONSE + socket.expect { |s,| s.return(255) } + assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } + end + + def test_sign_should_return_signed_data_from_agent + socket.expect do |s,type,buffer| + assert_equal SSH2_AGENT_SIGN_REQUEST, type + assert_equal key.to_blob, Net::SSH::Buffer.new(buffer.read_string).read_key.to_blob + assert_equal "hello world", buffer.read_string + assert_equal 0, buffer.read_long + + s.return(SSH2_AGENT_SIGN_RESPONSE, :string, "abcxyz123") + end + + assert_equal "abcxyz123", agent.sign(key, "hello world") + end + + private + + class MockSocket + def initialize + @expectation = nil + @buffer = Net::SSH::Buffer.new + end + + def expect(&block) + @expectation = block + end + + def return(type, *args) + data = Net::SSH::Buffer.from(*args) + @buffer.append([data.length+1, type, data.to_s].pack("NCA*")) + end + + def send(data, flags) + raise "got #{data.inspect} but no packet was expected" unless @expectation + buffer = Net::SSH::Buffer.new(data) + buffer.read_long # skip the length + type = buffer.read_byte + @expectation.call(self, type, buffer) + @expectation = nil + end + + def read(length) + @buffer.read(length) + end + end + + def key + @key ||= OpenSSL::PKey::RSA.new(32) + end + + def socket + @socket ||= MockSocket.new + end + + def factory + @factory ||= stub("socket factory", :open => socket) + end + + def agent(auto=:connect) + @agent ||= begin + agent = Net::SSH::Authentication::Agent.new + agent.stubs(:agent_socket_factory).returns(factory) + agent.connect! if auto == :connect + agent + end + end + + end + +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/test_key_manager.rb File: test_key_manager.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/test_key_manager.rb;tzinfo2 @@ -1,0 +1,105 @@ +require 'common' +require 'net/ssh/authentication/key_manager' + +module Authentication + + class TestKeyManager < Test::Unit::TestCase + def test_key_files_and_known_identities_are_empty_by_default + assert manager.key_files.empty? + assert manager.known_identities.empty? + end + + def test_assume_agent_is_available_by_default + assert manager.use_agent? + end + + def test_add_ensures_list_is_unique + manager.add "/first" + manager.add "/second" + manager.add "/third" + manager.add "/second" + assert_equal %w(/first /second /third), manager.key_files + end + + def test_use_agent_should_be_set_to_false_if_agent_could_not_be_found + Net::SSH::Authentication::Agent.expects(:connect).raises(Net::SSH::Authentication::AgentNotAvailable) + assert manager.use_agent? + assert_nil manager.agent + assert !manager.use_agent? + end + + def test_each_identity_should_load_from_key_files + manager.stubs(:agent).returns(nil) + + stub_file_key "/first", rsa + stub_file_key "/second", dsa + + identities = [] + manager.each_identity { |identity| identities << identity } + + assert_equal 2, identities.length + assert_equal rsa.to_blob, identities.first.to_blob + assert_equal dsa.to_blob, identities.last.to_blob + + assert_equal({:from => :file, :file => "/first", :key => rsa}, manager.known_identities[rsa]) + assert_equal({:from => :file, :file => "/second", :key => dsa}, manager.known_identities[dsa]) + end + + def test_identities_should_load_from_agent + manager.stubs(:agent).returns(agent) + + identities = [] + manager.each_identity { |identity| identities << identity } + + assert_equal 2, identities.length + assert_equal rsa.to_blob, identities.first.to_blob + assert_equal dsa.to_blob, identities.last.to_blob + + assert_equal({:from => :agent}, manager.known_identities[rsa]) + assert_equal({:from => :agent}, manager.known_identities[dsa]) + end + + def test_sign_with_agent_originated_key_should_request_signature_from_agent + manager.stubs(:agent).returns(agent) + manager.each_identity { |identity| } # preload the known_identities + agent.expects(:sign).with(rsa, "hello, world").returns("abcxyz123") + assert_equal "abcxyz123", manager.sign(rsa, "hello, world") + end + + def test_sign_with_file_originated_key_should_load_private_key_and_sign_with_it + manager.stubs(:agent).returns(nil) + stub_file_key "/first", rsa(512), true + rsa.expects(:ssh_do_sign).with("hello, world").returns("abcxyz123") + manager.each_identity { |identity| } # preload the known_identities + assert_equal "\0\0\0\assh-rsa\0\0\0\011abcxyz123", manager.sign(rsa, "hello, world") + end + + private + + def stub_file_key(name, key, also_private=false) + manager.add(name) + File.expects(:readable?).with(name).returns(true) + File.expects(:readable?).with(name + ".pub").returns(false) + Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil).returns(key).at_least_once + key.expects(:public_key).returns(key) + end + + def rsa(size=32) + @rsa ||= OpenSSL::PKey::RSA.new(size) + end + + def dsa + @dsa ||= OpenSSL::PKey::DSA.new(32) + end + + def agent + @agent ||= stub("agent", :identities => [rsa, dsa]) + end + + def manager + @manager ||= Net::SSH::Authentication::KeyManager.new(nil) + end + + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/test_session.rb File: test_session.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/test_session.rb;tzinfo2 @@ -1,0 +1,93 @@ +require 'common' +require 'net/ssh/authentication/session' + +module Authentication + + class TestSession < Test::Unit::TestCase + include Net::SSH::Transport::Constants + include Net::SSH::Authentication::Constants + + def test_constructor_should_set_defaults + assert_equal %w(publickey hostbased password keyboard-interactive), session.auth_methods + assert_equal session.auth_methods, session.allowed_auth_methods + end + + def test_authenticate_should_raise_error_if_service_request_fails + transport.expect do |t, packet| + assert_equal SERVICE_REQUEST, packet.type + assert_equal "ssh-userauth", packet.read_string + t.return(255) + end + + assert_raises(Net::SSH::Exception) { session.authenticate("next service", "username", "password") } + end + + def test_authenticate_should_return_false_if_all_auth_methods_fail + transport.expect do |t, packet| + assert_equal SERVICE_REQUEST, packet.type + assert_equal "ssh-userauth", packet.read_string + t.return(SERVICE_ACCEPT) + end + + Net::SSH::Authentication::Methods::Publickey.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) + Net::SSH::Authentication::Methods::Hostbased.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) + Net::SSH::Authentication::Methods::Password.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) + Net::SSH::Authentication::Methods::KeyboardInteractive.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) + + assert_equal false, session.authenticate("next service", "username", "password") + end + + def test_next_message_should_silently_handle_USERAUTH_BANNER_packets + transport.return(USERAUTH_BANNER, :string, "Howdy, folks!") + transport.return(SERVICE_ACCEPT) + assert_equal SERVICE_ACCEPT, session.next_message.type + end + + def test_next_message_should_understand_USERAUTH_FAILURE + transport.return(USERAUTH_FAILURE, :string, "a,b,c", :bool, false) + packet = session.next_message + assert_equal USERAUTH_FAILURE, packet.type + assert_equal %w(a b c), session.allowed_auth_methods + end + + (60..79).each do |type| + define_method("test_next_message_should_return_packets_of_type_#{type}") do + transport.return(type) + assert_equal type, session.next_message.type + end + end + + def test_next_message_should_understand_USERAUTH_SUCCESS + transport.return(USERAUTH_SUCCESS) + assert !transport.hints[:authenticated] + assert_equal USERAUTH_SUCCESS, session.next_message.type + assert transport.hints[:authenticated] + end + + def test_next_message_should_raise_error_on_unrecognized_packet_types + transport.return(1) + assert_raises(Net::SSH::Exception) { session.next_message } + end + + def test_expect_message_should_raise_exception_if_next_packet_is_not_expected_type + transport.return(SERVICE_ACCEPT) + assert_raises(Net::SSH::Exception) { session.expect_message(USERAUTH_BANNER) } + end + + def test_expect_message_should_return_packet_if_next_packet_is_expected_type + transport.return(SERVICE_ACCEPT) + assert_equal SERVICE_ACCEPT, session.expect_message(SERVICE_ACCEPT).type + end + + private + + def session(options={}) + @session ||= Net::SSH::Authentication::Session.new(transport(options), options) + end + + def transport(options={}) + @transport ||= MockTransport.new(options) + end + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/common.rb File: common.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/common.rb;tzinfo2 @@ -1,0 +1,28 @@ +module Authentication; module Methods + + module Common + include Net::SSH::Authentication::Constants + + private + + def socket(options={}) + @socket ||= stub("socket", :client_name => "me.ssh.test") + end + + def transport(options={}) + @transport ||= MockTransport.new(options.merge(:socket => socket)) + end + + def session(options={}) + @session ||= begin + sess = stub("auth-session", :logger => nil, :transport => transport(options)) + def sess.next_message + transport.next_message + end + sess + end + end + + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_abstract.rb File: test_abstract.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_abstract.rb;tzinfo2 @@ -1,0 +1,51 @@ +require 'common' +require 'authentication/methods/common' +require 'net/ssh/authentication/methods/abstract' + +module Authentication; module Methods + + class TestAbstract < Test::Unit::TestCase + include Common + + def test_constructor_should_set_defaults + assert_nil subject.key_manager + end + + def test_constructor_should_honor_options + assert_equal :manager, subject(:key_manager => :manager).key_manager + end + + def test_session_id_should_query_session_id_from_key_exchange + transport.stubs(:algorithms).returns(stub("algorithms", :session_id => "abcxyz123")) + assert_equal "abcxyz123", subject.session_id + end + + def test_send_message_should_delegate_to_transport + transport.expects(:send_message).with("abcxyz123") + subject.send_message("abcxyz123") + end + + def test_userauth_request_should_build_well_formed_userauth_packet + packet = subject.userauth_request("jamis", "ssh-connection", "password") + assert_equal "\062\0\0\0\005jamis\0\0\0\016ssh-connection\0\0\0\010password", packet.to_s + end + + def test_userauth_request_should_translate_extra_booleans_onto_end + packet = subject.userauth_request("jamis", "ssh-connection", "password", true, false) + assert_equal "\062\0\0\0\005jamis\0\0\0\016ssh-connection\0\0\0\010password\1\0", packet.to_s + end + + def test_userauth_request_should_translate_extra_strings_onto_end + packet = subject.userauth_request("jamis", "ssh-connection", "password", "foo", "bar") + assert_equal "\062\0\0\0\005jamis\0\0\0\016ssh-connection\0\0\0\010password\0\0\0\3foo\0\0\0\3bar", packet.to_s + end + + private + + def subject(options={}) + @subject ||= Net::SSH::Authentication::Methods::Abstract.new(session(options), options) + end + + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_hostbased.rb File: test_hostbased.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_hostbased.rb;tzinfo2 @@ -1,0 +1,114 @@ +require 'common' +require 'net/ssh/authentication/methods/hostbased' +require 'authentication/methods/common' + +module Authentication; module Methods + + class TestHostbased < Test::Unit::TestCase + include Common + + def test_authenticate_should_return_false_when_no_key_manager_has_been_set + assert_equal false, subject(:key_manager => nil).authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_return_false_when_key_manager_has_no_keys + assert_equal false, subject(:keys => []).authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_return_false_if_no_keys_can_authenticate + ENV.stubs(:[]).with('USER').returns(nil) + key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") + key_manager.expects(:sign).with(&signature_parameters(keys.last)).returns("sig-two") + + transport.expect do |t, packet| + assert_equal USERAUTH_REQUEST, packet.type + assert verify_userauth_request_packet(packet, keys.first) + assert_equal "sig-one", packet.read_string + t.return(USERAUTH_FAILURE, :string, "hostbased,password") + + t.expect do |t2, packet2| + assert_equal USERAUTH_REQUEST, packet2.type + assert verify_userauth_request_packet(packet2, keys.last) + assert_equal "sig-two", packet2.read_string + t2.return(USERAUTH_FAILURE, :string, "hostbased,password") + end + end + + assert_equal false, subject.authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_return_true_if_any_key_can_authenticate + ENV.stubs(:[]).with('USER').returns(nil) + key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") + + transport.expect do |t, packet| + assert_equal USERAUTH_REQUEST, packet.type + assert verify_userauth_request_packet(packet, keys.first) + assert_equal "sig-one", packet.read_string + t.return(USERAUTH_SUCCESS) + end + + assert subject.authenticate("ssh-connection", "jamis") + end + + private + + def signature_parameters(key) + Proc.new do |given_key, data| + next false unless given_key.to_blob == key.to_blob + buffer = Net::SSH::Buffer.new(data) + buffer.read_string == "abcxyz123" && # session-id + buffer.read_byte == USERAUTH_REQUEST && # type + verify_userauth_request_packet(buffer, key) + end + end + + def verify_userauth_request_packet(packet, key) + packet.read_string == "jamis" && # user-name + packet.read_string == "ssh-connection" && # next service + packet.read_string == "hostbased" && # auth-method + packet.read_string == key.ssh_type && # key type + packet.read_buffer.read_key.to_blob == key.to_blob && # key + packet.read_string == "me.ssh.test." && # client hostname + packet.read_string == "jamis" # client username + end + + @@keys = nil + def keys + @@keys ||= [OpenSSL::PKey::RSA.new(32), OpenSSL::PKey::DSA.new(32)] + end + + def key_manager(options={}) + @key_manager ||= begin + manager = stub("key_manager") + manager.stubs(:each_identity).multiple_yields(*(options[:keys] || keys)) + manager + end + end + + def subject(options={}) + options[:key_manager] = key_manager(options) unless options.key?(:key_manager) + @subject ||= Net::SSH::Authentication::Methods::Hostbased.new(session(options), options) + end + + def socket(options={}) + @socket ||= stub("socket", :client_name => "me.ssh.test") + end + + def transport(options={}) + @transport ||= MockTransport.new(options.merge(:socket => socket)) + end + + def session(options={}) + @session ||= begin + sess = stub("auth-session", :logger => nil, :transport => transport(options)) + def sess.next_message + transport.next_message + end + sess + end + end + + end + +end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_keyboard_interactive.rb File: test_keyboard_interactive.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_keyboard_interactive.rb;tzinfo2 @@ -1,0 +1,98 @@ +require 'common' +require 'net/ssh/authentication/methods/keyboard_interactive' +require 'authentication/methods/common' + +module Authentication; module Methods + + class TestKeyboardInteractive < Test::Unit::TestCase + include Common + + USERAUTH_INFO_REQUEST = 60 + USERAUTH_INFO_RESPONSE = 61 + + def test_authenticate_should_be_false_when_server_does_not_support_this_method + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + assert_equal "jamis", packet.read_string + assert_equal "ssh-connection", packet.read_string + assert_equal "keyboard-interactive", packet.read_string + assert_equal "", packet.read_string # language tags + assert_equal "", packet.read_string # submethods + + t.return(USERAUTH_FAILURE, :string, "password") + end + + assert_equal false, subject.authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_be_false_if_given_password_is_not_accepted + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 1, :string, "Password:", :bool, false) + t.expect do |t2,packet2| + assert_equal USERAUTH_INFO_RESPONSE, packet2.type + assert_equal 1, packet2.read_long + assert_equal "the-password", packet2.read_string + t2.return(USERAUTH_FAILURE, :string, "publickey") + end + end + + assert_equal false, subject.authenticate("ssh-connection", "jamis", "the-password") + end + + def test_authenticate_should_be_true_if_given_password_is_accepted + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 1, :string, "Password:", :bool, false) + t.expect do |t2,packet2| + assert_equal USERAUTH_INFO_RESPONSE, packet2.type + t2.return(USERAUTH_SUCCESS) + end + end + + assert subject.authenticate("ssh-connection", "jamis", "the-password") + end + + def test_authenticate_should_duplicate_password_as_needed_to_fill_request + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 2, :string, "Password:", :bool, false, :string, "Again:", :bool, false) + t.expect do |t2,packet2| + assert_equal USERAUTH_INFO_RESPONSE, packet2.type + assert_equal 2, packet2.read_long + assert_equal "the-password", packet2.read_string + assert_equal "the-password", packet2.read_string + t2.return(USERAUTH_SUCCESS) + end + end + + assert subject.authenticate("ssh-connection", "jamis", "the-password") + end + + def test_authenticate_should_prompt_for_input_when_password_is_not_given + subject.expects(:prompt).with("Name:", true).returns("name") + subject.expects(:prompt).with("Password:", false).returns("password") + + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 2, :string, "Name:", :bool, true, :string, "Password:", :bool, false) + t.expect do |t2,packet2| + assert_equal USERAUTH_INFO_RESPONSE, packet2.type + assert_equal 2, packet2.read_long + assert_equal "name", packet2.read_string + assert_equal "password", packet2.read_string + t2.return(USERAUTH_SUCCESS) + end + end + + assert subject.authenticate("ssh-connection", "jamis", nil) + end + + private + + def subject(options={}) + @subject ||= Net::SSH::Authentication::Methods::KeyboardInteractive.new(session(options), options) + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_password.rb File: test_password.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_password.rb;tzinfo2 @@ -1,0 +1,50 @@ +require 'common' +require 'net/ssh/authentication/methods/password' +require 'authentication/methods/common' + +module Authentication; module Methods + + class TestPassword < Test::Unit::TestCase + include Common + + def test_authenticate_when_password_is_unacceptible_should_return_false + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + assert_equal "jamis", packet.read_string + assert_equal "ssh-connection", packet.read_string + assert_equal "password", packet.read_string + assert_equal false, packet.read_bool + assert_equal "the-password", packet.read_string + + t.return(USERAUTH_FAILURE, :string, "publickey") + end + + assert !subject.authenticate("ssh-connection", "jamis", "the-password") + end + + def test_authenticate_when_password_is_acceptible_should_return_true + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + t.return(USERAUTH_SUCCESS) + end + + assert subject.authenticate("ssh-connection", "jamis", "the-password") + end + + def test_authenticate_should_return_false_if_password_change_request_is_received + transport.expect do |t,packet| + assert_equal USERAUTH_REQUEST, packet.type + t.return(USERAUTH_PASSWD_CHANGEREQ, :string, "Change your password:", :string, "") + end + + assert !subject.authenticate("ssh-connection", "jamis", "the-password") + end + + private + + def subject(options={}) + @subject ||= Net::SSH::Authentication::Methods::Password.new(session(options), options) + end + end + +end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_publickey.rb File: test_publickey.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/authentication/methods/test_publickey.rb;tzinfo2 @@ -1,0 +1,127 @@ +require 'common' +require 'net/ssh/authentication/methods/publickey' +require 'authentication/methods/common' + +module Authentication; module Methods + + class TestPublickey < Test::Unit::TestCase + include Common + + def test_authenticate_should_return_false_when_no_key_manager_has_been_set + assert_equal false, subject(:key_manager => nil).authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_return_false_when_key_manager_has_no_keys + assert_equal false, subject(:keys => []).authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_return_false_if_no_keys_can_authenticate + transport.expect do |t, packet| + assert_equal USERAUTH_REQUEST, packet.type + assert verify_userauth_request_packet(packet, keys.first, false) + t.return(USERAUTH_FAILURE, :string, "hostbased,password") + + t.expect do |t2, packet2| + assert_equal USERAUTH_REQUEST, packet2.type + assert verify_userauth_request_packet(packet2, keys.last, false) + t2.return(USERAUTH_FAILURE, :string, "hostbased,password") + end + end + + assert_equal false, subject.authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_return_false_if_signature_exchange_fails + key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") + key_manager.expects(:sign).with(&signature_parameters(keys.last)).returns("sig-two") + + transport.expect do |t, packet| + assert_equal USERAUTH_REQUEST, packet.type + assert verify_userauth_request_packet(packet, keys.first, false) + t.return(USERAUTH_PK_OK, :string, keys.first.ssh_type, :string, Net::SSH::Buffer.from(:key, keys.first)) + + t.expect do |t2,packet2| + assert_equal USERAUTH_REQUEST, packet2.type + assert verify_userauth_request_packet(packet2, keys.first, true) + assert_equal "sig-one", packet2.read_string + t2.return(USERAUTH_FAILURE, :string, "hostbased,password") + + t2.expect do |t3, packet3| + assert_equal USERAUTH_REQUEST, packet3.type + assert verify_userauth_request_packet(packet3, keys.last, false) + t3.return(USERAUTH_PK_OK, :string, keys.last.ssh_type, :string, Net::SSH::Buffer.from(:key, keys.last)) + + t3.expect do |t4,packet4| + assert_equal USERAUTH_REQUEST, packet4.type + assert verify_userauth_request_packet(packet4, keys.last, true) + assert_equal "sig-two", packet4.read_string + t4.return(USERAUTH_FAILURE, :string, "hostbased,password") + end + end + end + end + + assert !subject.authenticate("ssh-connection", "jamis") + end + + def test_authenticate_should_return_true_if_any_key_can_authenticate + key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") + + transport.expect do |t, packet| + assert_equal USERAUTH_REQUEST, packet.type + assert verify_userauth_request_packet(packet, keys.first, false) + t.return(USERAUTH_PK_OK, :string, keys.first.ssh_type, :string, Net::SSH::Buffer.from(:key, keys.first)) + + t.expect do |t2,packet2| + assert_equal USERAUTH_REQUEST, packet2.type + assert verify_userauth_request_packet(packet2, keys.first, true) + assert_equal "sig-one", packet2.read_string + t2.return(USERAUTH_SUCCESS) + end + end + + assert subject.authenticate("ssh-connection", "jamis") + end + + private + + def signature_parameters(key) + Proc.new do |given_key, data| + next false unless given_key.to_blob == key.to_blob + buffer = Net::SSH::Buffer.new(data) + buffer.read_string == "abcxyz123" && # session-id + buffer.read_byte == USERAUTH_REQUEST && # type + verify_userauth_request_packet(buffer, key, true) + end + end + + def verify_userauth_request_packet(packet, key, has_sig) + packet.read_string == "jamis" && # user-name + packet.read_string == "ssh-connection" && # next service + packet.read_string == "publickey" && # auth-method + packet.read_bool == has_sig && # whether a signature is appended + packet.read_string == key.ssh_type && # ssh key type + packet.read_buffer.read_key.to_blob == key.to_blob # key + end + + @@keys = nil + def keys + @@keys ||= [OpenSSL::PKey::RSA.new(32), OpenSSL::PKey::DSA.new(32)] + end + + def key_manager(options={}) + @key_manager ||= begin + manager = stub("key_manager") + manager.stubs(:each_identity).multiple_yields(*(options[:keys] || keys)) + manager + end + end + + def subject(options={}) + options[:key_manager] = key_manager(options) unless options.key?(:key_manager) + @subject ||= Net::SSH::Authentication::Methods::Publickey.new(session(options), options) + end + + end + +end; end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/eqsign File: eqsign =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/eqsign;tzinfo2 @@ -1,0 +1,3 @@ +Host=test.test + Port =1234 + Compression yes =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/exact_match File: exact_match =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/exact_match;tzinfo2 @@ -1,0 +1,8 @@ +Host other.host + Compression no + Port 1231 + +Host test.host + Compression yes + ForwardAgent yes + Port 1234 \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/multihost File: multihost =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/multihost;tzinfo2 @@ -1,0 +1,4 @@ +Host other.host, test.host + Compression yes + Port 1980 + RekeyLimit 2G =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/wild_cards File: wild_cards =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/configs/wild_cards;tzinfo2 @@ -1,0 +1,14 @@ +Host test.* + Port 1234 + Compression no + +Host tes?.host + Port 4321 + ForwardAgent yes + +Host *.hos? + IdentityFile ~/.ssh/id_dsa + Compression yes + +Host k*.host + RekeyLimit 1G \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/connection/test_channel.rb File: test_channel.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/connection/test_channel.rb;tzinfo2 @@ -1,0 +1,452 @@ +require 'common' +require 'net/ssh/connection/channel' + +module Connection + + class TestChannel < Test::Unit::TestCase + include Net::SSH::Connection::Constants + + def teardown + connection.test! + end + + def test_constructor_should_set_defaults + assert_equal 0x10000, channel.local_maximum_packet_size + assert_equal 0x20000, channel.local_maximum_window_size + assert channel.pending_requests.empty? + end + + def test_channel_properties + channel[:hello] = "some value" + assert_equal "some value", channel[:hello] + end + + def test_exec_should_be_syntactic_sugar_for_a_channel_request + channel.expects(:send_channel_request).with("exec", :string, "ls").yields + found_block = false + channel.exec("ls") { found_block = true } + assert found_block, "expected block to be passed to send_channel_request" + end + + def test_subsystem_should_be_syntactic_sugar_for_a_channel_request + channel.expects(:send_channel_request).with("subsystem", :string, "sftp").yields + found_block = false + channel.subsystem("sftp") { found_block = true } + assert found_block, "expected block to be passed to send_channel_request" + end + + def test_request_pty_with_invalid_option_should_raise_error + assert_raises(ArgumentError) do + channel.request_pty(:bogus => "thing") + end + end + + def test_request_pty_without_options_should_use_defaults + channel.expects(:send_channel_request).with("pty-req", :string, "xterm", + :long, 80, :long, 24, :long, 640, :long, 480, :string, "\0").yields + found_block = false + channel.request_pty { found_block = true } + assert found_block, "expected block to be passed to send_channel_request" + end + + def test_request_pty_with_options_should_honor_options + channel.expects(:send_channel_request).with("pty-req", :string, "vanilla", + :long, 60, :long, 15, :long, 400, :long, 200, :string, "\5\0\0\0\1\0") + channel.request_pty :term => "vanilla", :chars_wide => 60, :chars_high => 15, + :pixels_wide => 400, :pixels_high => 200, :modes => { 5 => 1 } + end + + def test_send_data_should_append_to_channels_output_buffer + channel.send_data("hello") + assert_equal "hello", channel.output.to_s + channel.send_data("world") + assert_equal "helloworld", channel.output.to_s + end + + def test_close_before_channel_has_been_confirmed_should_do_nothing + assert !channel.closing? + channel.close + assert !channel.closing? + end + + def test_close_should_set_closing_and_send_message + channel.do_open_confirmation(0, 100, 100) + assert !channel.closing? + + connection.expect { |t,packet| assert_equal CHANNEL_CLOSE, packet.type } + channel.close + + assert channel.closing? + end + + def test_close_while_closing_should_do_nothing + test_close_should_set_closing_and_send_message + assert_nothing_raised { channel.close } + end + + def test_process_when_process_callback_is_not_set_should_just_enqueue_data + channel.expects(:enqueue_pending_output) + channel.process + end + + def test_process_when_process_callback_is_set_should_yield_self_before_enqueuing_data + channel.expects(:enqueue_pending_output).never + channel.on_process { |ch| ch.expects(:enqueue_pending_output).once } + channel.process + end + + def test_enqueue_pending_output_should_have_no_effect_if_channel_has_not_been_confirmed + channel.send_data("hello") + assert_nothing_raised { channel.enqueue_pending_output } + end + + def test_enqueue_pending_output_should_have_no_effect_if_there_is_no_output + channel.do_open_confirmation(0, 100, 100) + assert_nothing_raised { channel.enqueue_pending_output } + end + + def test_enqueue_pending_output_should_not_enqueue_more_than_output_length + channel.do_open_confirmation(0, 100, 100) + channel.send_data("hello world") + + connection.expect do |t,packet| + assert_equal CHANNEL_DATA, packet.type + assert_equal 0, packet[:local_id] + assert_equal 11, packet[:data].length + end + + channel.enqueue_pending_output + end + + def test_enqueue_pending_output_should_not_enqueue_more_than_max_packet_length_at_once + channel.do_open_confirmation(0, 100, 8) + channel.send_data("hello world") + + connection.expect do |t,packet| + assert_equal CHANNEL_DATA, packet.type + assert_equal 0, packet[:local_id] + assert_equal "hello wo", packet[:data] + + t.expect do |t2,packet2| + assert_equal CHANNEL_DATA, packet2.type + assert_equal 0, packet2[:local_id] + assert_equal "rld", packet2[:data] + end + end + + channel.enqueue_pending_output + end + + def test_enqueue_pending_output_should_not_enqueue_more_than_max_window_size + channel.do_open_confirmation(0, 8, 100) + channel.send_data("hello world") + + connection.expect do |t,packet| + assert_equal CHANNEL_DATA, packet.type + assert_equal 0, packet[:local_id] + assert_equal "hello wo", packet[:data] + end + + channel.enqueue_pending_output + end + + def test_on_data_with_block_should_set_callback + flag = false + channel.on_data { flag = !flag } + channel.do_data("") + assert(flag, "callback should have been invoked") + channel.on_data + channel.do_data("") + assert(flag, "callback should have been removed") + end + + def test_on_extended_data_with_block_should_set_callback + flag = false + channel.on_extended_data { flag = !flag } + channel.do_extended_data(0, "") + assert(flag, "callback should have been invoked") + channel.on_extended_data + channel.do_extended_data(0, "") + assert(flag, "callback should have been removed") + end + + def test_on_process_with_block_should_set_callback + flag = false + channel.on_process { flag = !flag } + channel.process + assert(flag, "callback should have been invoked") + channel.on_process + channel.process + assert(flag, "callback should have been removed") + end + + def test_on_close_with_block_should_set_callback + flag = false + channel.on_close { flag = !flag } + channel.do_close + assert(flag, "callback should have been invoked") + channel.on_close + channel.do_close + assert(flag, "callback should have been removed") + end + + def test_on_eof_with_block_should_set_callback + flag = false + channel.on_eof { flag = !flag } + channel.do_eof + assert(flag, "callback should have been invoked") + channel.on_eof + channel.do_eof + assert(flag, "callback should have been removed") + end + + def test_do_request_for_unhandled_request_should_do_nothing_if_not_wants_reply + channel.do_open_confirmation(0, 100, 100) + assert_nothing_raised { channel.do_request "exit-status", false, nil } + end + + def test_do_request_for_unhandled_request_should_send_CHANNEL_FAILURE_if_wants_reply + channel.do_open_confirmation(0, 100, 100) + connection.expect { |t,packet| assert_equal CHANNEL_FAILURE, packet.type } + channel.do_request "keepalive@openssh.com", true, nil + end + + def test_do_request_for_handled_request_should_invoke_callback_and_do_nothing_if_returns_true_and_not_wants_reply + channel.do_open_confirmation(0, 100, 100) + flag = false + channel.on_request("exit-status") { flag = true; true } + assert_nothing_raised { channel.do_request "exit-status", false, nil } + assert flag, "callback should have been invoked" + end + + def test_do_request_for_handled_request_should_invoke_callback_and_do_nothing_if_fails_and_not_wants_reply + channel.do_open_confirmation(0, 100, 100) + flag = false + channel.on_request("exit-status") { flag = true; raise Net::SSH::ChannelRequestFailed } + assert_nothing_raised { channel.do_request "exit-status", false, nil } + assert flag, "callback should have been invoked" + end + + def test_do_request_for_handled_request_should_invoke_callback_and_send_CHANNEL_SUCCESS_if_returns_true_and_wants_reply + channel.do_open_confirmation(0, 100, 100) + flag = false + channel.on_request("exit-status") { flag = true; true } + connection.expect { |t,p| assert_equal CHANNEL_SUCCESS, p.type } + assert_nothing_raised { channel.do_request "exit-status", true, nil } + assert flag, "callback should have been invoked" + end + + def test_do_request_for_handled_request_should_invoke_callback_and_send_CHANNEL_FAILURE_if_returns_false_and_wants_reply + channel.do_open_confirmation(0, 100, 100) + flag = false + channel.on_request("exit-status") { flag = true; raise Net::SSH::ChannelRequestFailed } + connection.expect { |t,p| assert_equal CHANNEL_FAILURE, p.type } + assert_nothing_raised { channel.do_request "exit-status", true, nil } + assert flag, "callback should have been invoked" + end + + def test_send_channel_request_without_callback_should_not_want_reply + channel.do_open_confirmation(0, 100, 100) + connection.expect do |t,p| + assert_equal CHANNEL_REQUEST, p.type + assert_equal 0, p[:local_id] + assert_equal "exec", p[:request] + assert_equal false, p[:want_reply] + assert_equal "ls", p[:request_data].read_string + end + channel.send_channel_request("exec", :string, "ls") + assert channel.pending_requests.empty? + end + + def test_send_channel_request_with_callback_should_want_reply + channel.do_open_confirmation(0, 100, 100) + connection.expect do |t,p| + assert_equal CHANNEL_REQUEST, p.type + assert_equal 0, p[:local_id] + assert_equal "exec", p[:request] + assert_equal true, p[:want_reply] + assert_equal "ls", p[:request_data].read_string + end + callback = Proc.new {} + channel.send_channel_request("exec", :string, "ls", &callback) + assert_equal [callback], channel.pending_requests + end + + def test_do_open_confirmation_should_set_remote_parameters + channel.do_open_confirmation(1, 2, 3) + assert_equal 1, channel.remote_id + assert_equal 2, channel.remote_window_size + assert_equal 2, channel.remote_maximum_window_size + assert_equal 3, channel.remote_maximum_packet_size + end + + def test_do_open_confirmation_should_call_open_confirmation_callback + flag = false + channel { flag = true } + assert !flag, "callback should not have been invoked yet" + channel.do_open_confirmation(1,2,3) + assert flag, "callback should have been invoked" + end + + def test_do_open_confirmation_with_session_channel_should_invoke_agent_forwarding_if_agent_forwarding_requested + connection :forward_agent => true + forward = mock("forward") + forward.expects(:agent).with(channel) + connection.expects(:forward).returns(forward) + channel.do_open_confirmation(1,2,3) + end + + def test_do_open_confirmation_with_non_session_channel_should_not_invoke_agent_forwarding_even_if_agent_forwarding_requested + connection :forward_agent => true + channel :type => "direct-tcpip" + connection.expects(:forward).never + channel.do_open_confirmation(1,2,3) + end + + def test_do_window_adjust_should_adjust_remote_window_size_by_the_given_amount + channel.do_open_confirmation(0, 1000, 1000) + assert_equal 1000, channel.remote_window_size + assert_equal 1000, channel.remote_maximum_window_size + channel.do_window_adjust(500) + assert_equal 1500, channel.remote_window_size + assert_equal 1500, channel.remote_maximum_window_size + end + + def test_do_data_should_update_local_window_size + assert_equal 0x20000, channel.local_maximum_window_size + assert_equal 0x20000, channel.local_window_size + channel.do_data("here is some data") + assert_equal 0x20000, channel.local_maximum_window_size + assert_equal 0x1FFEF, channel.local_window_size + end + + def test_do_extended_data_should_update_local_window_size + assert_equal 0x20000, channel.local_maximum_window_size + assert_equal 0x20000, channel.local_window_size + channel.do_extended_data(1, "here is some data") + assert_equal 0x20000, channel.local_maximum_window_size + assert_equal 0x1FFEF, channel.local_window_size + end + + def test_do_data_when_local_window_size_drops_below_threshold_should_trigger_WINDOW_ADJUST_message + channel.do_open_confirmation(0, 1000, 1000) + assert_equal 0x20000, channel.local_maximum_window_size + assert_equal 0x20000, channel.local_window_size + + connection.expect do |t,p| + assert_equal CHANNEL_WINDOW_ADJUST, p.type + assert_equal 0, p[:local_id] + assert_equal 0x20000, p[:extra_bytes] + end + + channel.do_data("." * 0x10001) + assert_equal 0x40000, channel.local_maximum_window_size + assert_equal 0x2FFFF, channel.local_window_size + end + + def test_do_failure_should_grab_next_pending_request_and_call_it + result = nil + channel.pending_requests << Proc.new { |*args| result = args } + channel.do_failure + assert_equal [channel, false], result + assert channel.pending_requests.empty? + end + + def test_do_success_should_grab_next_pending_request_and_call_it + result = nil + channel.pending_requests << Proc.new { |*args| result = args } + channel.do_success + assert_equal [channel, true], result + assert channel.pending_requests.empty? + end + + def test_active_should_be_true_when_channel_appears_in_channel_list + connection.channels[channel.local_id] = channel + assert channel.active? + end + + def test_active_should_be_false_when_channel_is_not_in_channel_list + assert !channel.active? + end + + def test_wait_should_block_while_channel_is_active? + channel.expects(:active?).times(3).returns(true,true,false) + channel.wait + end + + def test_eof_bang_should_send_eof_to_server + channel.do_open_confirmation(0, 1000, 1000) + connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } + channel.eof! + end + + def test_eof_bang_should_not_send_eof_if_eof_was_already_declared + channel.do_open_confirmation(0, 1000, 1000) + connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } + channel.eof! + assert_nothing_raised { channel.eof! } + end + + def test_eof_q_should_return_true_if_eof_declared + channel.do_open_confirmation(0, 1000, 1000) + connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } + + assert !channel.eof? + channel.eof! + assert channel.eof? + end + + def test_send_data_should_raise_exception_if_eof_declared + channel.do_open_confirmation(0, 1000, 1000) + connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } + channel.eof! + assert_raises(EOFError) { channel.send_data("die! die! die!") } + end + + private + + class MockConnection + attr_reader :logger + attr_reader :options + attr_reader :channels + + def initialize(options={}) + @expectation = nil + @options = options + @channels = {} + end + + def expect(&block) + @expectation = block + end + + def send_message(msg) + raise "#{msg.to_s.inspect} recieved but no message was expected" unless @expectation + packet = Net::SSH::Packet.new(msg.to_s) + callback, @expectation = @expectation, nil + callback.call(self, packet) + end + + alias loop_forever loop + def loop(&block) + loop_forever { break unless block.call } + end + + def test! + raise "expected a packet but none were sent" if @expectation + end + end + + def connection(options={}) + @connection ||= MockConnection.new(options) + end + + def channel(options={}, &block) + @channel ||= Net::SSH::Connection::Channel.new(connection(options), + options[:type] || "session", + options[:local_id] || 0, + &block) + end + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/connection/test_session.rb File: test_session.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/connection/test_session.rb;tzinfo2 @@ -1,0 +1,488 @@ +require 'common' +require 'net/ssh/connection/session' + +module Connection + + class TestSession < Test::Unit::TestCase + include Net::SSH::Connection::Constants + + def test_constructor_should_set_defaults + assert session.channels.empty? + assert session.pending_requests.empty? + assert_equal({ socket => nil }, session.listeners) + end + + def test_on_open_channel_should_register_block_with_given_channel_type + flag = false + session.on_open_channel("testing") { flag = true } + assert_not_nil session.channel_open_handlers["testing"] + session.channel_open_handlers["testing"].call + assert flag, "callback should have been invoked" + end + + def test_forward_should_create_and_cache_instance_of_forward_service + assert_instance_of Net::SSH::Service::Forward, session.forward + assert_equal session.forward.object_id, session.forward.object_id + end + + def test_listen_to_without_callback_should_add_argument_as_listener + io = stub("io") + session.listen_to(io) + assert session.listeners.key?(io) + assert_nil session.listeners[io] + end + + def test_listen_to_should_add_argument_to_listeners_list_if_block_is_given + io = stub("io", :pending_write? => true) + flag = false + session.listen_to(io) { flag = true } + assert !flag, "callback should not be invoked immediately" + assert session.listeners.key?(io) + session.listeners[io].call + assert flag, "callback should have been invoked" + end + + def test_stop_listening_to_should_remove_argument_from_listeners + io = stub("io", :pending_write? => true) + + session.listen_to(io) + assert session.listeners.key?(io) + + session.stop_listening_to(io) + assert !session.listeners.key?(io) + end + + def test_send_message_should_enqueue_message_at_transport_layer + packet = P(:byte, REQUEST_SUCCESS) + session.send_message(packet) + assert_equal packet.to_s, socket.write_buffer + end + + def test_open_channel_defaults_should_use_session_channel + flag = false + channel = session.open_channel { flag = true } + assert !flag, "callback should not be invoked immediately" + channel.do_open_confirmation(1,2,3) + assert flag, "callback should have been invoked" + assert_equal "session", channel.type + assert_equal 0, channel.local_id + assert_equal channel, session.channels[channel.local_id] + + packet = P(:byte, CHANNEL_OPEN, :string, "session", :long, channel.local_id, + :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size) + assert_equal packet.to_s, socket.write_buffer + end + + def test_open_channel_with_type_should_use_type + channel = session.open_channel("direct-tcpip") + assert_equal "direct-tcpip", channel.type + packet = P(:byte, CHANNEL_OPEN, :string, "direct-tcpip", :long, channel.local_id, + :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size) + assert_equal packet.to_s, socket.write_buffer + end + + def test_open_channel_with_extras_should_append_extras_to_packet + channel = session.open_channel("direct-tcpip", :string, "other.host", :long, 1234) + packet = P(:byte, CHANNEL_OPEN, :string, "direct-tcpip", :long, channel.local_id, + :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size, + :string, "other.host", :long, 1234) + assert_equal packet.to_s, socket.write_buffer + end + + def test_send_global_request_without_callback_should_not_expect_reply + packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, false) + session.send_global_request("testing") + assert_equal packet.to_s, socket.write_buffer + assert session.pending_requests.empty? + end + + def test_send_global_request_with_callback_should_expect_reply + packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, true) + proc = Proc.new {} + session.send_global_request("testing", &proc) + assert_equal packet.to_s, socket.write_buffer + assert_equal [proc], session.pending_requests + end + + def test_send_global_request_with_extras_should_append_extras_to_packet + packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, false, :string, "other.host", :long, 1234) + session.send_global_request("testing", :string, "other.host", :long, 1234) + assert_equal packet.to_s, socket.write_buffer + end + + def test_process_should_exit_immediately_if_block_is_false + session.channels[0] = stub("channel", :closing? => false) + session.channels[0].expects(:process).never + process_times(0) + end + + def test_process_should_exit_after_processing_if_block_is_true_then_false + session.channels[0] = stub("channel", :closing? => false) + session.channels[0].expects(:process) + IO.expects(:select).never + process_times(2) + end + + def test_process_should_not_process_channels_that_are_closing + session.channels[0] = stub("channel", :closing? => true) + session.channels[0].expects(:process).never + IO.expects(:select).never + process_times(2) + end + + def test_global_request_packets_should_be_silently_handled_if_no_handler_exists_for_them + transport.return(GLOBAL_REQUEST, :string, "testing", :bool, false) + process_times(2) + assert transport.queue.empty? + assert !socket.pending_write? + end + + def test_global_request_packets_should_be_auto_replied_to_even_if_no_handler_exists + transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) + process_times(2) + assert_equal P(:byte, REQUEST_FAILURE).to_s, socket.write_buffer + end + + def test_global_request_handler_should_not_trigger_auto_reply_if_no_reply_is_wanted + flag = false + session.on_global_request("testing") { flag = true } + assert !flag, "callback should not be invoked yet" + transport.return(GLOBAL_REQUEST, :string, "testing", :bool, false) + process_times(2) + assert transport.queue.empty? + assert !socket.pending_write? + assert flag, "callback should have been invoked" + end + + def test_global_request_handler_returning_true_should_trigger_success_auto_reply + flag = false + session.on_global_request("testing") { flag = true } + transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) + process_times(2) + assert_equal P(:byte, REQUEST_SUCCESS).to_s, socket.write_buffer + assert flag + end + + def test_global_request_handler_returning_false_should_trigger_failure_auto_reply + flag = false + session.on_global_request("testing") { flag = true; false } + transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) + process_times(2) + assert_equal P(:byte, REQUEST_FAILURE).to_s, socket.write_buffer + assert flag + end + + def test_global_request_handler_returning_sent_should_not_trigger_auto_reply + flag = false + session.on_global_request("testing") { flag = true; :sent } + transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) + process_times(2) + assert !socket.pending_write? + assert flag + end + + def test_global_request_handler_returning_other_value_should_raise_error + session.on_global_request("testing") { "bug" } + transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) + assert_raises(RuntimeError) { process_times(2) } + end + + def test_request_success_packets_should_invoke_next_pending_request_with_true + result = nil + session.pending_requests << Proc.new { |*args| result = args } + transport.return(REQUEST_SUCCESS) + process_times(2) + assert_equal [true, P(:byte, REQUEST_SUCCESS)], result + assert session.pending_requests.empty? + end + + def test_request_failure_packets_should_invoke_next_pending_request_with_false + result = nil + session.pending_requests << Proc.new { |*args| result = args } + transport.return(REQUEST_FAILURE) + process_times(2) + assert_equal [false, P(:byte, REQUEST_FAILURE)], result + assert session.pending_requests.empty? + end + + def test_channel_open_packet_without_corresponding_channel_open_handler_should_result_in_channel_open_failure + transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20000, :long, 0x10000) + process_times(2) + assert_equal P(:byte, CHANNEL_OPEN_FAILURE, :long, 14, :long, 3, :string, "unknown channel type auth-agent", :string, "").to_s, socket.write_buffer + end + + def test_channel_open_packet_with_corresponding_handler_should_result_in_channel_open_failure_when_handler_returns_an_error + transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20000, :long, 0x10000) + session.on_open_channel "auth-agent" do |s, ch, p| + raise Net::SSH::ChannelOpenFailed.new(1234, "we iz in ur channelz!") + end + process_times(2) + assert_equal P(:byte, CHANNEL_OPEN_FAILURE, :long, 14, :long, 1234, :string, "we iz in ur channelz!", :string, "").to_s, socket.write_buffer + end + + def test_channel_open_packet_with_corresponding_handler_should_result_in_channel_open_confirmation_when_handler_succeeds + transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20001, :long, 0x10001) + result = nil + session.on_open_channel("auth-agent") { |*args| result = args } + process_times(2) + assert_equal P(:byte, CHANNEL_OPEN_CONFIRMATION, :long, 14, :long, 0, :long, 0x20000, :long, 0x10000).to_s, socket.write_buffer + assert_not_nil(ch = session.channels[0]) + assert_equal [session, ch, P(:byte, CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20001, :long, 0x10001)], result + assert_equal 0, ch.local_id + assert_equal 14, ch.remote_id + assert_equal 0x20001, ch.remote_maximum_window_size + assert_equal 0x10001, ch.remote_maximum_packet_size + assert_equal 0x20000, ch.local_maximum_window_size + assert_equal 0x10000, ch.local_maximum_packet_size + assert_equal "auth-agent", ch.type + end + + def test_channel_open_failure_should_remove_channel_and_tell_channel_that_open_failed + session.channels[1] = stub("channel") + session.channels[1].expects(:do_open_failed).with(1234, "some reason") + transport.return(CHANNEL_OPEN_FAILURE, :long, 1, :long, 1234, :string, "some reason", :string, "lang tag") + process_times(2) + assert session.channels.empty? + end + + def test_channel_open_confirmation_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_open_confirmation).with(1234, 0x20001, 0x10001) + transport.return(CHANNEL_OPEN_CONFIRMATION, :long, 14, :long, 1234, :long, 0x20001, :long, 0x10001) + process_times(2) + end + + def test_channel_window_adjust_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_window_adjust).with(5000) + transport.return(CHANNEL_WINDOW_ADJUST, :long, 14, :long, 5000) + process_times(2) + end + + def test_channel_request_for_nonexistant_channel_should_be_ignored + transport.return(CHANNEL_REQUEST, :long, 14, :string, "testing", :bool, false) + assert_nothing_raised { process_times(2) } + end + + def test_channel_request_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_request).with("testing", false, Net::SSH::Buffer.new) + transport.return(CHANNEL_REQUEST, :long, 14, :string, "testing", :bool, false) + process_times(2) + end + + def test_channel_data_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_data).with("bring it on down") + transport.return(CHANNEL_DATA, :long, 14, :string, "bring it on down") + process_times(2) + end + + def test_channel_extended_data_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_extended_data).with(1, "bring it on down") + transport.return(CHANNEL_EXTENDED_DATA, :long, 14, :long, 1, :string, "bring it on down") + process_times(2) + end + + def test_channel_eof_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_eof).with() + transport.return(CHANNEL_EOF, :long, 14) + process_times(2) + end + + def test_channel_success_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_success).with() + transport.return(CHANNEL_SUCCESS, :long, 14) + process_times(2) + end + + def test_channel_failure_packet_should_be_routed_to_corresponding_channel + channel_at(14).expects(:do_failure).with() + transport.return(CHANNEL_FAILURE, :long, 14) + process_times(2) + end + + def test_channel_close_packet_should_be_routed_to_corresponding_channel_and_channel_should_be_closed_and_removed + channel_at(14).expects(:do_close).with() + session.channels[14].expects(:close).with() + transport.return(CHANNEL_CLOSE, :long, 14) + process_times(2) + assert session.channels.empty? + end + + def test_multiple_pending_dispatches_should_be_dispatched_together + channel_at(14).expects(:do_eof).with() + session.channels[14].expects(:do_success).with() + transport.return(CHANNEL_SUCCESS, :long, 14) + transport.return(CHANNEL_EOF, :long, 14) + process_times(2) + end + + def test_writers_without_pending_writes_should_not_be_considered_for_select + IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]]) + session.process + end + + def test_writers_with_pending_writes_should_be_considered_for_select + socket.enqueue("laksdjflasdkf") + IO.expects(:select).with([socket],[socket],nil,nil).returns([[],[],[]]) + session.process + end + + def test_ready_readers_should_be_filled + socket.expects(:recv).returns("this is some data") + IO.expects(:select).with([socket],[],nil,nil).returns([[socket],[],[]]) + session.process + assert_equal [socket], session.listeners.keys + end + + def test_ready_readers_that_cant_be_filled_should_be_removed + socket.expects(:recv).returns("") + socket.expects(:close) + IO.expects(:select).with([socket],[],nil,nil).returns([[socket],[],[]]) + session.process + assert session.listeners.empty? + end + + def test_ready_readers_that_are_registered_with_a_block_should_call_block_instead_of_fill + io = stub("io", :pending_write? => false) + flag = false + session.stop_listening_to(socket) # so that we only have to test the presence of a single IO object + session.listen_to(io) { flag = true } + IO.expects(:select).with([io],[],nil,nil).returns([[io],[],[]]) + session.process + assert flag, "callback should have been invoked" + end + + def test_ready_writers_should_call_send_pending + socket.enqueue("laksdjflasdkf") + socket.expects(:send).with("laksdjflasdkf", 0).returns(13) + IO.expects(:select).with([socket],[socket],nil,nil).returns([[],[socket],[]]) + session.process + end + + def test_process_should_call_rekey_as_needed + transport.expects(:rekey_as_needed) + IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]]) + session.process + end + + def test_loop_should_call_process_until_process_returns_false + IO.stubs(:select).with([socket],[],nil,nil).returns([[],[],[]]) + session.expects(:process).with(nil).times(4).returns(true,true,true,false).yields + n = 0 + session.loop { n += 1 } + assert_equal 4, n + end + + def test_exec_should_open_channel_and_configure_default_callbacks + prep_exec("ls", :stdout, "data packet", :stderr, "extended data packet") + + call = :first + session.exec "ls" do |channel, type, data| + if call == :first + assert_equal :stdout, type + assert_equal "data packet", data + call = :second + elsif call == :second + assert_equal :stderr, type + assert_equal "extended data packet", data + call = :third + else + flunk "should never get here, call == #{call.inspect}" + end + end + + session.loop + assert_equal :third, call + end + + def test_exec_without_block_should_use_print_to_display_result + prep_exec("ls", :stdout, "data packet", :stderr, "extended data packet") + $stdout.expects(:print).with("data packet") + $stderr.expects(:print).with("extended data packet") + + session.exec "ls" + session.loop + end + + def test_exec_bang_should_block_until_command_finishes + prep_exec("ls", :stdout, "some data") + called = false + session.exec! "ls" do |channel, type, data| + called = true + assert_equal :stdout, type + assert_equal "some data", data + end + assert called + end + + def test_exec_bang_without_block_should_return_data_as_string + prep_exec("ls", :stdout, "some data") + assert_equal "some data", session.exec!("ls") + end + + private + + def prep_exec(command, *data) + transport.mock_enqueue = true + transport.expect do |t, p| + assert_equal CHANNEL_OPEN, p.type + t.return(CHANNEL_OPEN_CONFIRMATION, :long, p[:remote_id], :long, 0, :long, 0x20000, :long, 0x10000) + t.expect do |t2, p2| + assert_equal CHANNEL_REQUEST, p2.type + assert_equal "exec", p2[:request] + assert_equal true, p2[:want_reply] + assert_equal "ls", p2.read_string + + t2.return(CHANNEL_SUCCESS, :long, p[:remote_id]) + + 0.step(data.length-1, 2) do |index| + type = data[index] + datum = data[index+1] + + if type == :stdout + t2.return(CHANNEL_DATA, :long, p[:remote_id], :string, datum) + else + t2.return(CHANNEL_EXTENDED_DATA, :long, p[:remote_id], :long, 1, :string, datum) + end + end + + t2.return(CHANNEL_CLOSE, :long, p[:remote_id]) + t2.expect { |t3,p3| assert_equal CHANNEL_CLOSE, p3.type } + end + end + end + + module MockSocket + # so that we can easily test the contents that were enqueued, without + # worrying about all the packet stream overhead + def enqueue_packet(message) + enqueue(message.to_s) + end + end + + def socket + @socket ||= begin + socket ||= Object.new + socket.extend(Net::SSH::Transport::PacketStream) + socket.extend(MockSocket) + socket + end + end + + def channel_at(local_id) + session.channels[local_id] = stub("channel", :process => true, :closing? => false) + end + + def transport(options={}) + @transport ||= MockTransport.new(options.merge(:socket => socket)) + end + + def session(options={}) + @session ||= Net::SSH::Connection::Session.new(transport, options) + end + + def process_times(n) + i = 0 + session.process { (i += 1) < n } + end + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/kex add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_algorithms.rb File: test_algorithms.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_algorithms.rb;tzinfo2 @@ -1,0 +1,302 @@ +require 'common' +require 'net/ssh/transport/algorithms' + +module Transport + + class TestAlgorithms < Test::Unit::TestCase + include Net::SSH::Transport::Constants + + def test_allowed_packets + (0..255).each do |type| + packet = stub("packet", :type => type) + case type + when 1..4, 6..19, 21..49 then assert(Net::SSH::Transport::Algorithms.allowed_packet?(packet), "#{type} should be allowed during key exchange") + else assert(!Net::SSH::Transport::Algorithms.allowed_packet?(packet), "#{type} should not be allowed during key exchange") + end + end + end + + def test_constructor_should_build_default_list_of_preferred_algorithms + assert_equal %w(ssh-rsa ssh-dss), algorithms[:host_key] + assert_equal %w(diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1), algorithms[:kex] + assert_equal %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se idea-cbc none arcfour128 arcfour256), algorithms[:encryption] + assert_equal %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 none), algorithms[:hmac] + assert_equal %w(none zlib@openssh.com zlib), algorithms[:compression] + assert_equal %w(), algorithms[:language] + end + + def test_constructor_should_set_client_and_server_prefs_identically + %w(encryption hmac compression language).each do |key| + assert_equal algorithms[key.to_sym], algorithms[:"#{key}_client"], key + assert_equal algorithms[key.to_sym], algorithms[:"#{key}_server"], key + end + end + + def test_constructor_with_preferred_host_key_type_should_put_preferred_host_key_type_first + assert_equal %w(ssh-dss ssh-rsa), algorithms(:host_key => "ssh-dss")[:host_key] + end + + def test_constructor_with_known_hosts_reporting_known_host_key_should_use_that_host_key_type + Net::SSH::KnownHosts.expects(:search_for).with("net.ssh.test,127.0.0.1", {}).returns([stub("key", :ssh_type => "ssh-dss")]) + assert_equal %w(ssh-dss ssh-rsa), algorithms[:host_key] + end + + def test_constructor_with_unrecognized_host_key_type_should_raise_exception + assert_raises(NotImplementedError) { algorithms(:host_key => "bogus") } + end + + def test_constructor_with_preferred_kex_should_put_preferred_kex_first + assert_equal %w(diffie-hellman-group1-sha1 diffie-hellman-group-exchange-sha1), algorithms(:kex => "diffie-hellman-group1-sha1")[:kex] + end + + def test_constructor_with_unrecognized_kex_should_raise_exception + assert_raises(NotImplementedError) { algorithms(:kex => "bogus") } + end + + def test_constructor_with_preferred_encryption_should_put_preferred_encryption_first + assert_equal %w(aes256-cbc aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se idea-cbc none arcfour128 arcfour256), algorithms(:encryption => "aes256-cbc")[:encryption] + end + + def test_constructor_with_multiple_preferred_encryption_should_put_all_preferred_encryption_first + assert_equal %w(aes256-cbc 3des-cbc idea-cbc aes128-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se none arcfour128 arcfour256), algorithms(:encryption => %w(aes256-cbc 3des-cbc idea-cbc))[:encryption] + end + + def test_constructor_with_unrecognized_encryption_should_raise_exception + assert_raises(NotImplementedError) { algorithms(:encryption => "bogus") } + end + + def test_constructor_with_preferred_hmac_should_put_preferred_hmac_first + assert_equal %w(hmac-md5-96 hmac-sha1 hmac-md5 hmac-sha1-96 none), algorithms(:hmac => "hmac-md5-96")[:hmac] + end + + def test_constructor_with_multiple_preferred_hmac_should_put_all_preferred_hmac_first + assert_equal %w(hmac-md5-96 hmac-sha1-96 hmac-sha1 hmac-md5 none), algorithms(:hmac => %w(hmac-md5-96 hmac-sha1-96))[:hmac] + end + + def test_constructor_with_unrecognized_hmac_should_raise_exception + assert_raises(NotImplementedError) { algorithms(:hmac => "bogus") } + end + + def test_constructor_with_preferred_compression_should_put_preferred_compression_first + assert_equal %w(zlib none zlib@openssh.com), algorithms(:compression => "zlib")[:compression] + end + + def test_constructor_with_multiple_preferred_compression_should_put_all_preferred_compression_first + assert_equal %w(zlib@openssh.com zlib none), algorithms(:compression => %w(zlib@openssh.com zlib))[:compression] + end + + def test_constructor_with_general_preferred_compression_should_put_none_last + assert_equal %w(zlib@openssh.com zlib none), algorithms(:compression => true)[:compression] + end + + def test_constructor_with_unrecognized_compression_should_raise_exception + assert_raises(NotImplementedError) { algorithms(:compression => "bogus") } + end + + def test_initial_state_should_be_neither_pending_nor_initialized + assert !algorithms.pending? + assert !algorithms.initialized? + end + + def test_key_exchange_when_initiated_by_server + transport.expect do |t, buffer| + assert_kexinit(buffer) + install_mock_key_exchange(buffer) + end + + install_mock_algorithm_lookups + algorithms.accept_kexinit(kexinit) + + assert_exchange_results + end + + def test_key_exchange_when_initiated_by_client + state = nil + transport.expect do |t, buffer| + assert_kexinit(buffer) + state = :sent_kexinit + install_mock_key_exchange(buffer) + end + + algorithms.rekey! + assert_equal state, :sent_kexinit + assert algorithms.pending? + + install_mock_algorithm_lookups + algorithms.accept_kexinit(kexinit) + + assert_exchange_results + end + + def test_key_exchange_when_server_does_not_support_preferred_kex_should_fallback_to_secondary + kexinit :kex => "diffie-hellman-group1-sha1" + transport.expect do |t,buffer| + assert_kexinit(buffer) + install_mock_key_exchange(buffer, :kex => Net::SSH::Transport::Kex::DiffieHellmanGroup1SHA1) + end + algorithms.accept_kexinit(kexinit) + end + + def test_key_exchange_when_server_does_not_support_any_preferred_kex_should_raise_error + kexinit :kex => "something-obscure" + transport.expect { |t,buffer| assert_kexinit(buffer) } + assert_raises(Net::SSH::Exception) { algorithms.accept_kexinit(kexinit) } + end + + def test_allow_when_not_pending_should_be_true_for_all_packets + (0..255).each do |type| + packet = stub("packet", :type => type) + assert algorithms.allow?(packet), type + end + end + + def test_allow_when_pending_should_be_true_only_for_packets_valid_during_key_exchange + transport.expect! + algorithms.rekey! + assert algorithms.pending? + + (0..255).each do |type| + packet = stub("packet", :type => type) + case type + when 1..4, 6..19, 21..49 then assert(algorithms.allow?(packet), "#{type} should be allowed during key exchange") + else assert(!algorithms.allow?(packet), "#{type} should not be allowed during key exchange") + end + end + end + + def test_exchange_with_zlib_compression_enabled_sets_compression_to_standard + algorithms :compression => "zlib" + + transport.expect do |t, buffer| + assert_kexinit(buffer, :compression_client => "zlib,none,zlib@openssh.com", :compression_server => "zlib,none,zlib@openssh.com") + install_mock_key_exchange(buffer) + end + + install_mock_algorithm_lookups + algorithms.accept_kexinit(kexinit) + + assert_equal :standard, transport.client_options[:compression] + assert_equal :standard, transport.server_options[:compression] + end + + def test_exchange_with_zlib_at_openssh_dot_com_compression_enabled_sets_compression_to_delayed + algorithms :compression => "zlib@openssh.com" + + transport.expect do |t, buffer| + assert_kexinit(buffer, :compression_client => "zlib@openssh.com,none,zlib", :compression_server => "zlib@openssh.com,none,zlib") + install_mock_key_exchange(buffer) + end + + install_mock_algorithm_lookups + algorithms.accept_kexinit(kexinit) + + assert_equal :delayed, transport.client_options[:compression] + assert_equal :delayed, transport.server_options[:compression] + end + + private + + def install_mock_key_exchange(buffer, options={}) + kex = options[:kex] || Net::SSH::Transport::Kex::DiffieHellmanGroupExchangeSHA1 + + Net::SSH::Transport::Kex::MAP.each do |name, klass| + next if klass == kex + klass.expects(:new).never + end + + kex.expects(:new). + with(algorithms, transport, + :client_version_string => Net::SSH::Transport::ServerVersion::PROTO_VERSION, + :server_version_string => transport.server_version.version, + :server_algorithm_packet => kexinit.to_s, + :client_algorithm_packet => buffer.to_s, + :need_bytes => 20, + :logger => nil). + returns(stub("kex", :exchange_keys => { :shared_secret => shared_secret, :session_id => session_id, :hashing_algorithm => hashing_algorithm })) + end + + def install_mock_algorithm_lookups(options={}) + Net::SSH::Transport::CipherFactory.expects(:get). + with(options[:client_cipher] || "aes128-cbc", :iv => key("A"), :key => key("C"), :shared => shared_secret.to_ssh, :hash => session_id, :digester => hashing_algorithm, :encrypt => true). + returns(:client_cipher) + Net::SSH::Transport::CipherFactory.expects(:get). + with(options[:server_cipher] || "aes128-cbc", :iv => key("B"), :key => key("D"), :shared => shared_secret.to_ssh, :hash => session_id, :digester => hashing_algorithm, :decrypt => true). + returns(:server_cipher) + + Net::SSH::Transport::HMAC.expects(:get).with(options[:client_hmac] || "hmac-sha1", key("E")).returns(:client_hmac) + Net::SSH::Transport::HMAC.expects(:get).with(options[:server_hmac] || "hmac-sha1", key("F")).returns(:server_hmac) + end + + def shared_secret + @shared_secret ||= OpenSSL::BN.new("1234567890", 10) + end + + def session_id + @session_id ||= "this is the session id" + end + + def hashing_algorithm + OpenSSL::Digest::SHA1 + end + + def key(salt) + hashing_algorithm.digest(shared_secret.to_ssh + session_id + salt + session_id) + end + + def cipher(type, options={}) + Net::SSH::Transport::CipherFactory.get(type, options) + end + + def kexinit(options={}) + @kexinit ||= P(:byte, KEXINIT, + :long, rand(0xFFFFFFFF), :long, rand(0xFFFFFFFF), :long, rand(0xFFFFFFFF), :long, rand(0xFFFFFFFF), + :string, options[:kex] || "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1", + :string, options[:host_key] || "ssh-rsa,ssh-dss", + :string, options[:encryption_client] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc", + :string, options[:encryption_server] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc", + :string, options[:hmac_client] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96", + :string, options[:hmac_server] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96", + :string, options[:compmression_client] || "none,zlib@openssh.com,zlib", + :string, options[:compmression_server] || "none,zlib@openssh.com,zlib", + :string, options[:language_client] || "", + :string, options[:langauge_server] || "", + :bool, options[:first_kex_follows]) + end + + def assert_kexinit(buffer, options={}) + assert_equal KEXINIT, buffer.type + assert_equal 16, buffer.read(16).length + assert_equal options[:kex] || "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1", buffer.read_string + assert_equal options[:host_key] || "ssh-rsa,ssh-dss", buffer.read_string + assert_equal options[:encryption_client] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,none,arcfour128,arcfour256", buffer.read_string + assert_equal options[:encryption_server] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,none,arcfour128,arcfour256", buffer.read_string + assert_equal options[:hmac_client] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,none", buffer.read_string + assert_equal options[:hmac_server] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,none", buffer.read_string + assert_equal options[:compression_client] || "none,zlib@openssh.com,zlib", buffer.read_string + assert_equal options[:compression_server] || "none,zlib@openssh.com,zlib", buffer.read_string + assert_equal options[:language_client] || "", buffer.read_string + assert_equal options[:language_server] || "", buffer.read_string + assert_equal options[:first_kex_follows] || false, buffer.read_bool + end + + def assert_exchange_results + assert algorithms.initialized? + assert !algorithms.pending? + assert !transport.client_options[:compression] + assert !transport.server_options[:compression] + assert_equal :client_cipher, transport.client_options[:cipher] + assert_equal :server_cipher, transport.server_options[:cipher] + assert_equal :client_hmac, transport.client_options[:hmac] + assert_equal :server_hmac, transport.server_options[:hmac] + end + + def algorithms(options={}) + @algorithms ||= Net::SSH::Transport::Algorithms.new(transport, options) + end + + def transport + @transport ||= MockTransport.new + end + end + +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_cipher_factory.rb File: test_cipher_factory.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_cipher_factory.rb;tzinfo2 @@ -1,0 +1,213 @@ +require 'common' +require 'net/ssh/transport/cipher_factory' + +module Transport + + class TestCipherFactory < Test::Unit::TestCase + def self.if_supported?(name) + yield if Net::SSH::Transport::CipherFactory.supported?(name) + end + + def test_lengths_for_none + assert_equal [0,0], factory.get_lengths("none") + assert_equal [0,0], factory.get_lengths("bogus") + end + + def test_lengths_for_blowfish_cbc + assert_equal [16,8], factory.get_lengths("blowfish-cbc") + end + + if_supported?("idea-cbc") do + def test_lengths_for_idea_cbc + assert_equal [16,8], factory.get_lengths("idea-cbc") + end + end + + def test_lengths_for_rijndael_cbc + assert_equal [32,16], factory.get_lengths("rijndael-cbc@lysator.liu.se") + end + + def test_lengths_for_cast128_cbc + assert_equal [16,8], factory.get_lengths("cast128-cbc") + end + + def test_lengths_for_3des_cbc + assert_equal [24,8], factory.get_lengths("3des-cbc") + end + + def test_lengths_for_aes192_cbc + assert_equal [24,16], factory.get_lengths("aes192-cbc") + end + + def test_lengths_for_aes128_cbc + assert_equal [16,16], factory.get_lengths("aes128-cbc") + end + + def test_lengths_for_aes256_cbc + assert_equal [32,16], factory.get_lengths("aes256-cbc") + end + + def test_lengths_for_arcfour128 + assert_equal [16,8], factory.get_lengths("arcfour128") + end + + def test_lengths_for_arcfour256 + assert_equal [32,8], factory.get_lengths("arcfour256") + end + + def test_lengths_for_arcfour512 + assert_equal [64,8], factory.get_lengths("arcfour512") + end + + BLOWFISH = "\210\021\200\315\240_\026$\352\204g\233\244\242x\332e\370\001\327\224Nv@9_\323\037\252kb\037\036\237\375]\343/y\037\237\312Q\f7]\347Y\005\275%\377\0010$G\272\250B\265Nd\375\342\372\025r6}+Y\213y\n\237\267\\\374^\346BdJ$\353\220Ik\023<\236&H\277=\225" + + def test_blowfish_cbc_for_encryption + assert_equal BLOWFISH, encrypt("blowfish-cbc") + end + + def test_blowfish_cbc_for_decryption + assert_equal TEXT, decrypt("blowfish-cbc", BLOWFISH) + end + + if_supported?("idea-cbc") do + IDEA = "W\234\017G\231\b\357\370H\b\256U]\343M\031k\233]~\023C\363\263\177\262-\261\341$\022\376mv\217\322\b\2763\270H\306\035\343z\313\312\3531\351\t\201\302U\022\360\300\354ul7$z\320O]\360g\024\305\005`V\005\335A\351\312\270c\320D\232\eQH1\340\265\2118\031g*\303v" + + def test_idea_cbc_for_encryption + assert_equal IDEA, encrypt("idea-cbc") + end + + def test_idea_cbc_for_decryption + assert_equal TEXT, decrypt("idea-cbc", IDEA) + end + end + + RIJNDAEL = "$\253\271\255\005Z\354\336&\312\324\221\233\307Mj\315\360\310Fk\241EfN\037\231\213\361{'\310\204\347I\343\271\005\240`\325;\034\346uM>#\241\231C`\374\261\vo\226;Z\302:\b\250\366T\330\\#V\330\340\226\363\374!\bm\266\232\207!\232\347\340\t\307\370\356z\236\343=v\210\206y" + + def test_rijndael_cbc_for_encryption + assert_equal RIJNDAEL, encrypt("rijndael-cbc@lysator.liu.se") + end + + def test_rijndael_cbc_for_decryption + assert_equal TEXT, decrypt("rijndael-cbc@lysator.liu.se", RIJNDAEL) + end + + CAST128 = "qW\302\331\333P\223t[9 ~(sg\322\271\227\272\022I\223\373p\255>k\326\314\260\2003\236C_W\211\227\373\205>\351\334\322\227\223\e\236\202Ii\032!P\214\035:\017\360h7D\371v\210\264\317\236a\262w1\2772\023\036\331\227\240:\f/X\351\324I\t[x\350\323E\2301\016m" + + def test_cast128_cbc_for_encryption + assert_equal CAST128, encrypt("cast128-cbc") + end + + def test_cast128_cbc_for_decryption + assert_equal TEXT, decrypt("cast128-cbc", CAST128) + end + + TRIPLE_DES = "\322\252\216D\303Q\375gg\367A{\177\313\3436\272\353%\223K?\257\206|\r&\353/%\340\336 \203E8rY\206\234\004\274\267\031\233T/{\"\227/B!i?[qGaw\306T\206\223\213n \212\032\244%]@\355\250\334\312\265E\251\017\361\270\357\230\274KP&^\031r+r%\370" + + def test_3des_cbc_for_encryption + assert_equal TRIPLE_DES, encrypt("3des-cbc") + end + + def test_3des_cbc_for_decryption + assert_equal TEXT, decrypt("3des-cbc", TRIPLE_DES) + end + + AES128 = "k\026\350B\366-k\224\313\3277}B\035\004\200\035\r\233\024$\205\261\231Q\2214r\245\250\360\315\237\266hg\262C&+\321\346Pf\267v\376I\215P\327\345-\232&HK\375\326_\030<\a\276\212\303g\342C\242O\233\260\006\001a&V\345`\\T\e\236.\207\223l\233ri^\v\252\363\245" + + def test_aes128_cbc_for_encryption + assert_equal AES128, encrypt("aes128-cbc") + end + + def test_aes128_cbc_for_decryption + assert_equal TEXT, decrypt("aes128-cbc", AES128) + end + + AES192 = "\256\017)x\270\213\336\303L\003f\235'jQ\3231k9\225\267\242\364C4\370\224\201\302~\217I\202\374\2167='\272\037\225\223\177Y\r\212\376(\275\n\3553\377\177\252C\254\236\016MA\274Z@H\331<\rL\317\205\323[\305X8\376\237=\374\352bH9\244\0231\353\204\352p\226\326~J\242" + + def test_aes192_cbc_for_encryption + assert_equal AES192, encrypt("aes192-cbc") + end + + def test_aes192_cbc_for_decryption + assert_equal TEXT, decrypt("aes192-cbc", AES192) + end + + AES256 = "$\253\271\255\005Z\354\336&\312\324\221\233\307Mj\315\360\310Fk\241EfN\037\231\213\361{'\310\204\347I\343\271\005\240`\325;\034\346uM>#\241\231C`\374\261\vo\226;Z\302:\b\250\366T\330\\#V\330\340\226\363\374!\bm\266\232\207!\232\347\340\t\307\370\356z\236\343=v\210\206y" + + def test_aes256_cbc_for_encryption + assert_equal AES256, encrypt("aes256-cbc") + end + + def test_aes256_cbc_for_decryption + assert_equal TEXT, decrypt("aes256-cbc", AES256) + end + + ARCFOUR128 = "\n\x90\xED*\xD4\xBE\xCBg5\xA5\a\xEC]\x97\xB7L\x06)6\x12FL\x90@\xF4Sqxqh\r\x11\x1Aq \xC8\xE6v\xC6\x12\xD9 "ABC", + :key => "abc", + :digester => OpenSSL::Digest::MD5, + :shared => "1234567890123456780", + :hash => '!@#$%#$^%$&^&%#$@$' + } + + def factory + Net::SSH::Transport::CipherFactory + end + + def encrypt(type) + cipher = factory.get(type, OPTIONS.merge(:encrypt => true)) + padding = TEXT.length % cipher.block_size + result = cipher.update(TEXT.dup) + result << cipher.update(" " * (cipher.block_size - padding)) if padding > 0 + result << cipher.final + end + + def decrypt(type, data) + cipher = factory.get(type, OPTIONS.merge(:decrypt => true)) + result = cipher.update(data.dup) + result << cipher.final + result.strip + end + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_hmac.rb File: test_hmac.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_hmac.rb;tzinfo2 @@ -1,0 +1,34 @@ +require 'common' +require 'net/ssh/transport/hmac' + +module Transport + + class TestHMAC < Test::Unit::TestCase + Net::SSH::Transport::HMAC::MAP.each do |name, value| + method = name.tr("-", "_") + define_method("test_get_with_#{method}_returns_new_hmac_instance") do + key = "abcdefghijklmnopqrstuvwxyz"[0,Net::SSH::Transport::HMAC::MAP[name].key_length] + hmac = Net::SSH::Transport::HMAC.get(name, key) + assert_instance_of Net::SSH::Transport::HMAC::MAP[name], hmac + assert_equal key, hmac.key + end + + define_method("test_key_length_with_#{method}_returns_correct_key_length") do + assert_equal Net::SSH::Transport::HMAC::MAP[name].key_length, Net::SSH::Transport::HMAC.key_length(name) + end + end + + def test_get_with_unrecognized_hmac_raises_argument_error + assert_raises(ArgumentError) do + Net::SSH::Transport::HMAC.get("bogus") + end + end + + def test_key_length_with_unrecognized_hmac_raises_argument_error + assert_raises(ArgumentError) do + Net::SSH::Transport::HMAC.get("bogus") + end + end + end + +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_identity_cipher.rb File: test_identity_cipher.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_identity_cipher.rb;tzinfo2 @@ -1,0 +1,40 @@ +require 'common' +require 'net/ssh/transport/identity_cipher' + +module Transport + + class TestIdentityCipher < Test::Unit::TestCase + + def test_block_size_should_be_8 + assert_equal 8, cipher.block_size + end + + def test_encrypt_should_return_self + assert_equal cipher, cipher.encrypt + end + + def test_decrypt_should_return_self + assert_equal cipher, cipher.decrypt + end + + def test_update_should_return_argument + assert_equal "hello, world", cipher.update("hello, world") + end + + def test_final_should_return_empty_string + assert_equal "", cipher.final + end + + def test_name_should_be_identity + assert_equal "identity", cipher.name + end + + private + + def cipher + Net::SSH::Transport::IdentityCipher + end + + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_packet_stream.rb File: test_packet_stream.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_packet_stream.rb;tzinfo2 @@ -1,0 +1,441 @@ +require 'common' +require 'net/ssh/transport/packet_stream' + +module Transport + + class TestPacketStream < Test::Unit::TestCase + include Net::SSH::Transport::Constants + + def test_client_name_when_getnameinfo_works + stream.expects(:getsockname).returns(:sockaddr) + Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).returns(["net.ssh.test"]) + assert_equal "net.ssh.test", stream.client_name + end + + def test_client_name_when_getnameinfo_fails_first_and_then_works + stream.expects(:getsockname).returns(:sockaddr) + Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).raises(SocketError) + Socket.expects(:getnameinfo).with(:sockaddr).returns(["1.2.3.4"]) + assert_equal "1.2.3.4", stream.client_name + end + + def test_client_name_when_getnameinfo_fails_but_gethostbyname_works + stream.expects(:getsockname).returns(:sockaddr) + Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).raises(SocketError) + Socket.expects(:getnameinfo).with(:sockaddr).raises(SocketError) + Socket.expects(:gethostname).returns(:hostname) + Socket.expects(:gethostbyname).with(:hostname).returns(["net.ssh.test"]) + assert_equal "net.ssh.test", stream.client_name + end + + def test_client_name_when_getnameinfo_and_gethostbyname_all_fail + stream.expects(:getsockname).returns(:sockaddr) + Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).raises(SocketError) + Socket.expects(:getnameinfo).with(:sockaddr).raises(SocketError) + Socket.expects(:gethostname).returns(:hostname) + Socket.expects(:gethostbyname).with(:hostname).raises(SocketError) + assert_equal "unknown", stream.client_name + end + + def test_peer_ip_should_query_socket_for_info_about_peer + stream.expects(:getpeername).returns(:sockaddr) + Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).returns(["1.2.3.4"]) + assert_equal "1.2.3.4", stream.peer_ip + end + + def test_available_for_read_should_return_nontrue_when_select_fails + IO.expects(:select).returns(nil) + assert !stream.available_for_read? + end + + def test_available_for_read_should_return_nontrue_when_self_is_not_ready + IO.expects(:select).with([stream], nil, nil, 0).returns([[],[],[]]) + assert !stream.available_for_read? + end + + def test_available_for_read_should_return_true_when_self_is_ready + IO.expects(:select).with([stream], nil, nil, 0).returns([[self],[],[]]) + assert stream.available_for_read? + end + + def test_cleanup_should_delegate_cleanup_to_client_and_server_states + stream.client.expects(:cleanup) + stream.server.expects(:cleanup) + stream.cleanup + end + + def test_if_needs_rekey_should_not_yield_if_neither_client_nor_server_states_need_rekey + stream.if_needs_rekey? { flunk "shouldn't need rekey" } + assert(true) + end + + def test_if_needs_rekey_should_yield_and_cleanup_if_client_needs_rekey + stream.client.stubs(:needs_rekey?).returns(true) + stream.client.expects(:reset!) + stream.server.expects(:reset!).never + rekeyed = false + stream.if_needs_rekey? { rekeyed = true } + assert(rekeyed) + end + + def test_if_needs_rekey_should_yield_and_cleanup_if_server_needs_rekey + stream.server.stubs(:needs_rekey?).returns(true) + stream.server.expects(:reset!) + stream.client.expects(:reset!).never + rekeyed = false + stream.if_needs_rekey? { rekeyed = true } + assert(rekeyed) + end + + def test_if_needs_rekey_should_yield_and_cleanup_if_both_need_rekey + stream.server.stubs(:needs_rekey?).returns(true) + stream.client.stubs(:needs_rekey?).returns(true) + stream.server.expects(:reset!) + stream.client.expects(:reset!) + rekeyed = false + stream.if_needs_rekey? { rekeyed = true } + assert(rekeyed) + end + + def test_next_packet_should_not_block_by_default + IO.expects(:select).returns(nil) + assert_nothing_raised do + timeout(1) { stream.next_packet } + end + end + + def test_next_packet_should_return_nil_when_non_blocking_and_not_ready + IO.expects(:select).returns(nil) + assert_nil stream.next_packet(:nonblock) + end + + def test_next_packet_should_return_nil_when_non_blocking_and_partial_read + IO.expects(:select).returns([[stream]]) + stream.expects(:recv).returns([8].pack("N")) + assert_nil stream.next_packet(:nonblock) + assert !stream.read_buffer.empty? + end + + def test_next_packet_should_return_packet_when_non_blocking_and_full_read + IO.expects(:select).returns([[stream]]) + stream.expects(:recv).returns(packet) + packet = stream.next_packet(:nonblock) + assert_not_nil packet + assert_equal DEBUG, packet.type + end + + def test_next_packet_should_eventually_return_packet_when_non_blocking_and_partial_read + IO.stubs(:select).returns([[stream]]) + stream.stubs(:recv).returns(packet[0,10], packet[10..-1]) + assert_nil stream.next_packet(:nonblock) + packet = stream.next_packet(:nonblock) + assert_not_nil packet + assert_equal DEBUG, packet.type + end + + def test_next_packet_should_block_when_requested_until_entire_packet_is_available + IO.stubs(:select).returns([[stream]]) + stream.stubs(:recv).returns(packet[0,10], packet[10,20], packet[20..-1]) + packet = stream.next_packet(:block) + assert_not_nil packet + assert_equal DEBUG, packet.type + end + + def test_next_packet_when_blocking_should_fail_when_fill_could_not_read_any_data + IO.stubs(:select).returns([[stream]]) + stream.stubs(:recv).returns("") + assert_raises(Net::SSH::Disconnect) { stream.next_packet(:block) } + end + + def test_next_packet_fails_with_invalid_argument + assert_raises(ArgumentError) { stream.next_packet("invalid") } + end + + def test_send_packet_should_enqueue_and_send_data_immediately + stream.expects(:send).times(3).with { |a,b| a == stream.write_buffer && b == 0 }.returns(15) + IO.expects(:select).times(2).returns([[], [stream]]) + stream.send_packet(ssh_packet) + assert !stream.pending_write? + end + + def test_enqueue_short_packet_should_ensure_packet_is_at_least_16_bytes_long + packet = Net::SSH::Buffer.from(:byte, 0) + stream.enqueue_packet(packet) + # 12 originally, plus the block-size (8), plus the 4-byte length field + assert_equal 24, stream.write_buffer.length + end + + PACKETS = { + "3des-cbc" => { + "hmac-md5" => { + false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, #[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200A\305\027\341\261\331x\353\0372\3643h`\177\202", + }, + "hmac-md5-96" => { + false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, #[\343\200Sb\377\265\322\003=S", + :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200A\305\027\341\261\331x\353\0372\3643", + }, + "hmac-sha1" => { + false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, \235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\345\a{|\0367\355\2735\310'\n\342\250\246\030*1\353\330", + }, + "hmac-sha1-96" => { + false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, \235J\004f\262\3730t\376\273\323n", + :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\345\a{|\0367\355\2735\310'\n", + }, + "none" => { + false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, ", + :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200", + }, + }, + "aes128-cbc" => { + "hmac-md5" => { + false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\337\227`9L\324[bPd\253XY\205\241\310", + }, + "hmac-md5-96" => { + false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY#[\343\200Sb\377\265\322\003=S", + :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\337\227`9L\324[bPd\253X", + }, + "hmac-sha1" => { + false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\314\r\224%\316I\370t\251\372]\031\322pH%\267\337r\247", + }, + "hmac-sha1-96" => { + false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY\235J\004f\262\3730t\376\273\323n", + :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\314\r\224%\316I\370t\251\372]\031", + }, + "none" => { + false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY", + :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251", + }, + }, + "aes192-cbc" => { + "hmac-md5" => { + false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\337\227`9L\324[bPd\253XY\205\241\310", + }, + "hmac-md5-96" => { + false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003#[\343\200Sb\377\265\322\003=S", + :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\337\227`9L\324[bPd\253X", + }, + "hmac-sha1" => { + false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\314\r\224%\316I\370t\251\372]\031\322pH%\267\337r\247", + }, + "hmac-sha1-96" => { + false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003\235J\004f\262\3730t\376\273\323n", + :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\314\r\224%\316I\370t\251\372]\031", + }, + "none" => { + false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003", + :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D", + }, + }, + "aes256-cbc" => { + "hmac-md5" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\337\227`9L\324[bPd\253XY\205\241\310", + }, + "hmac-md5-96" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#[\343\200Sb\377\265\322\003=S", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\337\227`9L\324[bPd\253X", + }, + "hmac-sha1" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\314\r\224%\316I\370t\251\372]\031\322pH%\267\337r\247", + }, + "hmac-sha1-96" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\235J\004f\262\3730t\376\273\323n", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\314\r\224%\316I\370t\251\372]\031", + }, + "none" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365", + }, + }, + "blowfish-cbc" => { + "hmac-md5" => { + false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006A\305\027\341\261\331x\353\0372\3643h`\177\202", + }, + "hmac-md5-96" => { + false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)#[\343\200Sb\377\265\322\003=S", + :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006A\305\027\341\261\331x\353\0372\3643", + }, + "hmac-sha1" => { + false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\345\a{|\0367\355\2735\310'\n\342\250\246\030*1\353\330", + }, + "hmac-sha1-96" => { + false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)\235J\004f\262\3730t\376\273\323n", + :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\345\a{|\0367\355\2735\310'\n", + }, + "none" => { + false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)", + :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006", + }, + }, + "cast128-cbc" => { + "hmac-md5" => { + false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325A\305\027\341\261\331x\353\0372\3643h`\177\202", + }, + "hmac-md5-96" => { + false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376#[\343\200Sb\377\265\322\003=S", + :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325A\305\027\341\261\331x\353\0372\3643", + }, + "hmac-sha1" => { + false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\345\a{|\0367\355\2735\310'\n\342\250\246\030*1\353\330", + }, + "hmac-sha1-96" => { + false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376\235J\004f\262\3730t\376\273\323n", + :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\345\a{|\0367\355\2735\310'\n", + }, + "none" => { + false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376", + :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325", + }, + }, + "idea-cbc" => { + "hmac-md5" => { + false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317A\305\027\341\261\331x\353\0372\3643h`\177\202", + }, + "hmac-md5-96" => { + false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H#[\343\200Sb\377\265\322\003=S", + :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317A\305\027\341\261\331x\353\0372\3643", + }, + "hmac-sha1" => { + false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\345\a{|\0367\355\2735\310'\n\342\250\246\030*1\353\330", + }, + "hmac-sha1-96" => { + false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H\235J\004f\262\3730t\376\273\323n", + :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\345\a{|\0367\355\2735\310'\n", + }, + "none" => { + false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H", + :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317", + }, + }, + "none" => { + "hmac-md5" => { + false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^A\305\027\341\261\331x\353\0372\3643h`\177\202", + }, + "hmac-md5-96" => { + false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212#[\343\200Sb\377\265\322\003=S", + :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^A\305\027\341\261\331x\353\0372\3643", + }, + "hmac-sha1" => { + false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\345\a{|\0367\355\2735\310'\n\342\250\246\030*1\353\330", + }, + "hmac-sha1-96" => { + false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212\235J\004f\262\3730t\376\273\323n", + :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\345\a{|\0367\355\2735\310'\n", + }, + "none" => { + false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212", + :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^", + }, + }, + "rijndael-cbc@lysator.liu.se" => { + "hmac-md5" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#[\343\200Sb\377\265\322\003=S\255N\2654", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\337\227`9L\324[bPd\253XY\205\241\310", + }, + "hmac-md5-96" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#[\343\200Sb\377\265\322\003=S", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\337\227`9L\324[bPd\253X", + }, + "hmac-sha1" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\235J\004f\262\3730t\376\273\323n\260\275\202\223\214\370D\204", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\314\r\224%\316I\370t\251\372]\031\322pH%\267\337r\247", + }, + "hmac-sha1-96" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\235J\004f\262\3730t\376\273\323n", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\314\r\224%\316I\370t\251\372]\031", + }, + "none" => { + false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340", + :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365", + }, + }, + } + + ciphers = Net::SSH::Transport::CipherFactory::SSH_TO_OSSL.keys + hmacs = Net::SSH::Transport::HMAC::MAP.keys + + ciphers.each do |cipher_name| + next unless Net::SSH::Transport::CipherFactory.supported?(cipher_name) + + # TODO: How are the expected packets generated? + if cipher_name =~ /arcfour/ + puts "Skipping packet stream test for #{cipher_name}" + next + end + + hmacs.each do |hmac_name| + [false, :standard].each do |compress| + cipher_method_name = cipher_name.gsub(/\W/, "_") + hmac_method_name = hmac_name.gsub(/\W/, "_") + + define_method("test_next_packet_with_#{cipher_method_name}_and_#{hmac_method_name}_and_#{compress}_compression") do + cipher = Net::SSH::Transport::CipherFactory.get(cipher_name, :key => "ABC", :iv => "abc", :shared => "123", :digester => OpenSSL::Digest::SHA1, :hash => "^&*", :decrypt => true) + hmac = Net::SSH::Transport::HMAC.get(hmac_name, "{}|") + + stream.server.set :cipher => cipher, :hmac => hmac, :compression => compress + stream.stubs(:recv).returns(PACKETS[cipher_name][hmac_name][compress]) + IO.stubs(:select).returns([[stream]]) + packet = stream.next_packet(:nonblock) + assert_not_nil packet + assert_equal DEBUG, packet.type + assert packet[:always_display] + assert_equal "debugging", packet[:message] + assert_equal "", packet[:language] + stream.cleanup + end + + define_method("test_enqueue_packet_with_#{cipher_method_name}_and_#{hmac_method_name}_and_#{compress}_compression") do + cipher = Net::SSH::Transport::CipherFactory.get(cipher_name, :key => "ABC", :iv => "abc", :shared => "123", :digester => OpenSSL::Digest::SHA1, :hash => "^&*", :encrypt => true) + hmac = Net::SSH::Transport::HMAC.get(hmac_name, "{}|") + + srand(100) + stream.client.set :cipher => cipher, :hmac => hmac, :compression => compress + stream.enqueue_packet(ssh_packet) + assert_equal stream.write_buffer, PACKETS[cipher_name][hmac_name][compress] + stream.cleanup + end + end + end + end + + private + + def stream + @stream ||= begin + stream = mock("packet_stream") + stream.extend(Net::SSH::Transport::PacketStream) + stream + end + end + + def ssh_packet + Net::SSH::Buffer.from(:byte, DEBUG, :bool, true, :string, "debugging", :string, "") + end + + def packet + @packet ||= begin + data = ssh_packet + length = data.length + 4 + 1 # length + padding length + padding = stream.server.cipher.block_size - (length % stream.server.cipher.block_size) + padding += stream.server.cipher.block_size if padding < 4 + Net::SSH::Buffer.from(:long, length + padding - 4, :byte, padding, :raw, data, :raw, "\0" * padding).to_s + end + end + end + +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_server_version.rb File: test_server_version.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_server_version.rb;tzinfo2 @@ -1,0 +1,68 @@ +require 'common' +require 'net/ssh/transport/server_version' + +module Transport + + class TestServerVersion < Test::Unit::TestCase + + def test_1_99_server_version_should_be_acceptible + s = subject(socket(true, "SSH-1.99-Testing_1.0\r\n")) + assert s.header.empty? + assert_equal "SSH-1.99-Testing_1.0", s.version + end + + def test_2_0_server_version_should_be_acceptible + s = subject(socket(true, "SSH-2.0-Testing_1.0\r\n")) + assert s.header.empty? + assert_equal "SSH-2.0-Testing_1.0", s.version + end + + def test_trailing_whitespace_should_be_preserved + # some servers, like Mocana, send a version string with trailing + # spaces, which are significant when exchanging keys later. + s = subject(socket(true, "SSH-2.0-Testing_1.0 \r\n")) + assert_equal "SSH-2.0-Testing_1.0 ", s.version + end + + def test_unacceptible_server_version_should_raise_exception + assert_raises(Net::SSH::Exception) { subject(socket(false, "SSH-1.4-Testing_1.0\r\n")) } + end + + def test_header_lines_should_be_accumulated + s = subject(socket(true, "Welcome\r\nAnother line\r\nSSH-2.0-Testing_1.0\r\n")) + assert_equal "Welcome\r\nAnother line\r\n", s.header + assert_equal "SSH-2.0-Testing_1.0", s.version + end + + def test_server_disconnect_should_raise_exception + assert_raises(Net::SSH::Disconnect) { subject(socket(false, "SSH-2.0-Aborting")) } + end + + private + + def socket(good, version_header) + socket = mock("socket") + + data = version_header.split('') + recv_times = data.length + if data[-1] != "\n" + recv_times += 1 + end + socket.expects(:recv).with(1).times(recv_times).returns(*data).then.returns(nil) + + if good + socket.expects(:write).with("#{Net::SSH::Transport::ServerVersion::PROTO_VERSION}\r\n") + socket.expects(:flush) + else + socket.expects(:write).never + end + + socket + end + + def subject(socket) + Net::SSH::Transport::ServerVersion.new(socket, nil) + end + end + +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_session.rb File: test_session.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_session.rb;tzinfo2 @@ -1,0 +1,315 @@ +require 'common' +require 'net/ssh/transport/session' + +# mocha adds #verify to Object, which throws off the host-key-verifier part of +# these tests. + +# can't use .include? because ruby18 uses strings and ruby19 uses symbols :/ +if Object.instance_methods.any? { |v| v.to_sym == :verify } + Object.send(:undef_method, :verify) +end + +module Transport + + class TestSession < Test::Unit::TestCase + include Net::SSH::Transport::Constants + + def test_constructor_defaults + assert_equal "net.ssh.test", session.host + assert_equal 22, session.port + assert_instance_of Net::SSH::Verifiers::Lenient, session.host_key_verifier + end + + def test_paranoid_true_uses_lenient_verifier + assert_instance_of Net::SSH::Verifiers::Lenient, session(:paranoid => true).host_key_verifier + end + + def test_paranoid_very_uses_strict_verifier + assert_instance_of Net::SSH::Verifiers::Strict, session(:paranoid => :very).host_key_verifier + end + + def test_paranoid_false_uses_null_verifier + assert_instance_of Net::SSH::Verifiers::Null, session(:paranoid => false).host_key_verifier + end + + def test_unknown_paranoid_value_raises_exception_if_value_does_not_respond_to_verify + assert_raises(ArgumentError) { session(:paranoid => :bogus).host_key_verifier } + end + + def test_paranoid_value_responding_to_verify_should_pass_muster + object = stub("thingy", :verify => true) + assert_equal object, session(:paranoid => object).host_key_verifier + end + + def test_host_as_string_should_return_host_and_ip_when_port_is_default + session! + socket.stubs(:peer_ip).returns("1.2.3.4") + assert_equal "net.ssh.test,1.2.3.4", session.host_as_string + end + + def test_host_as_string_should_return_host_and_ip_with_port_when_port_is_not_default + session(:port => 1234) # force session to be instantiated + socket.stubs(:peer_ip).returns("1.2.3.4") + assert_equal "[net.ssh.test]:1234,[1.2.3.4]:1234", session.host_as_string + end + + def test_host_as_string_should_return_only_host_when_host_is_ip + session!(:host => "1.2.3.4") + socket.stubs(:peer_ip).returns("1.2.3.4") + assert_equal "1.2.3.4", session.host_as_string + end + + def test_host_as_string_should_return_only_host_and_port_when_host_is_ip_and_port_is_not_default + session!(:host => "1.2.3.4", :port => 1234) + socket.stubs(:peer_ip).returns("1.2.3.4") + assert_equal "[1.2.3.4]:1234", session.host_as_string + end + + def test_close_should_cleanup_and_close_socket + session! + socket.expects(:cleanup) + socket.expects(:close) + session.close + end + + def test_service_request_should_return_buffer + assert_equal "\005\000\000\000\004sftp", session.service_request('sftp').to_s + end + + def test_rekey_when_kex_is_pending_should_do_nothing + algorithms.stubs(:pending? => true) + algorithms.expects(:rekey!).never + session.rekey! + end + + def test_rekey_when_no_kex_is_pending_should_initiate_rekey_and_block_until_it_completes + algorithms.stubs(:pending? => false) + algorithms.expects(:rekey!) + session.expects(:wait).yields + algorithms.expects(:initialized?).returns(true) + session.rekey! + end + + def test_rekey_as_needed_when_kex_is_pending_should_do_nothing + session! + algorithms.stubs(:pending? => true) + socket.expects(:if_needs_rekey?).never + session.rekey_as_needed + end + + def test_rekey_as_needed_when_no_kex_is_pending_and_no_rekey_is_needed_should_do_nothing + session! + algorithms.stubs(:pending? => false) + socket.stubs(:if_needs_rekey? => false) + session.expects(:rekey!).never + session.rekey_as_needed + end + + def test_rekey_as_needed_when_no_kex_is_pending_and_rekey_is_needed_should_initiate_rekey_and_block + session! + algorithms.stubs(:pending? => false) + socket.expects(:if_needs_rekey?).yields + session.expects(:rekey!) + session.rekey_as_needed + end + + def test_peer_should_return_hash_of_info_about_peer + session! + socket.stubs(:peer_ip => "1.2.3.4") + assert_equal({:ip => "1.2.3.4", :port => 22, :host => "net.ssh.test", :canonized => "net.ssh.test,1.2.3.4"}, session.peer) + end + + def test_next_message_should_block_until_next_message_is_available + session.expects(:poll_message).with(:block) + session.next_message + end + + def test_poll_message_should_query_next_packet_using_the_given_blocking_parameter + session! + socket.expects(:next_packet).with(:blocking_parameter).returns(nil) + session.poll_message(:blocking_parameter) + end + + def test_poll_message_should_default_to_non_blocking + session! + socket.expects(:next_packet).with(:nonblock).returns(nil) + session.poll_message + end + + def test_poll_message_should_silently_handle_disconnect_packets + session! + socket.expects(:next_packet).returns(P(:byte, DISCONNECT, :long, 1, :string, "testing", :string, "")) + assert_raises(Net::SSH::Disconnect) { session.poll_message } + end + + def test_poll_message_should_silently_handle_ignore_packets + session! + socket.expects(:next_packet).times(2).returns(P(:byte, IGNORE, :string, "test"), nil) + assert_nil session.poll_message + end + + def test_poll_message_should_silently_handle_unimplemented_packets + session! + socket.expects(:next_packet).times(2).returns(P(:byte, UNIMPLEMENTED, :long, 15), nil) + assert_nil session.poll_message + end + + def test_poll_message_should_silently_handle_debug_packets_with_always_display + session! + socket.expects(:next_packet).times(2).returns(P(:byte, DEBUG, :bool, true, :string, "testing", :string, ""), nil) + assert_nil session.poll_message + end + + def test_poll_message_should_silently_handle_debug_packets_without_always_display + session! + socket.expects(:next_packet).times(2).returns(P(:byte, DEBUG, :bool, false, :string, "testing", :string, ""), nil) + assert_nil session.poll_message + end + + def test_poll_message_should_silently_handle_kexinit_packets + session! + packet = P(:byte, KEXINIT, :raw, "lasdfalksdjfa;slkdfja;slkfjsdfaklsjdfa;df") + socket.expects(:next_packet).times(2).returns(packet, nil) + algorithms.expects(:accept_kexinit).with(packet) + assert_nil session.poll_message + end + + def test_poll_message_should_return_other_packets + session! + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + socket.expects(:next_packet).returns(packet) + assert_equal packet, session.poll_message + end + + def test_poll_message_should_enqueue_packets_when_algorithm_disallows_packet + session! + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + algorithms.stubs(:allow?).with(packet).returns(false) + socket.expects(:next_packet).times(2).returns(packet, nil) + assert_nil session.poll_message + assert_equal [packet], session.queue + end + + def test_poll_message_should_read_from_queue_when_next_in_queue_is_allowed_and_consume_queue_is_true + session! + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + session.push(packet) + socket.expects(:next_packet).never + assert_equal packet, session.poll_message + assert session.queue.empty? + end + + def test_poll_message_should_not_read_from_queue_when_next_in_queue_is_not_allowed + session! + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + algorithms.stubs(:allow?).with(packet).returns(false) + session.push(packet) + socket.expects(:next_packet).returns(nil) + assert_nil session.poll_message + assert_equal [packet], session.queue + end + + def test_poll_message_should_not_read_from_queue_when_consume_queue_is_false + session! + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + session.push(packet) + socket.expects(:next_packet).returns(nil) + assert_nil session.poll_message(:nonblock, false) + assert_equal [packet], session.queue + end + + def test_wait_with_block_should_return_immediately_if_block_returns_truth + session.expects(:poll_message).never + session.wait { true } + end + + def test_wait_should_not_consume_queue_on_reads + n = 0 + session.expects(:poll_message).with(:nonblock, false).returns(nil) + session.wait { (n += 1) > 1 } + end + + def test_wait_without_block_should_return_after_first_read + session.expects(:poll_message).returns(nil) + session.wait + end + + def test_wait_should_enqueue_packets + session! + + p1 = P(:byte, SERVICE_REQUEST, :string, "test") + p2 = P(:byte, SERVICE_ACCEPT, :string, "test") + socket.expects(:next_packet).times(2).returns(p1, p2) + + n = 0 + session.wait { (n += 1) > 2 } + assert_equal [p1, p2], session.queue + end + + def test_push_should_enqueue_packet + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + session.push(packet) + assert_equal [packet], session.queue + end + + def test_send_message_should_delegate_to_socket + session! + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + socket.expects(:send_packet).with(packet) + session.send_message(packet) + end + + def test_enqueue_message_should_delegate_to_socket + session! + packet = P(:byte, SERVICE_ACCEPT, :string, "test") + socket.expects(:enqueue_packet).with(packet) + session.enqueue_message(packet) + end + + def test_configure_client_should_pass_options_to_socket_client_state + session.configure_client :compression => :standard + assert_equal :standard, socket.client.compression + end + + def test_configure_server_should_pass_options_to_socket_server_state + session.configure_server :compression => :standard + assert_equal :standard, socket.server.compression + end + + def test_hint_should_set_hint_on_socket + assert !socket.hints[:authenticated] + session.hint :authenticated + assert socket.hints[:authenticated] + end + + private + + def socket + @socket ||= stub("socket", :hints => {}) + end + + def server_version + @server_version ||= stub("server_version") + end + + def algorithms + @algorithms ||= stub("algorithms", :initialized? => true, :allow? => true) + end + + def session(options={}) + @session ||= begin + host = options.delete(:host) || "net.ssh.test" + TCPSocket.stubs(:open).with(host, options[:port] || 22).returns(socket) + Net::SSH::Transport::ServerVersion.stubs(:new).returns(server_version) + Net::SSH::Transport::Algorithms.stubs(:new).returns(algorithms) + + Net::SSH::Transport::Session.new(host, options) + end + end + + # a simple alias to make the tests more self-documenting. the bang + # version makes it look more like the session is being instantiated + alias session! session + end + +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_state.rb File: test_state.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/test_state.rb;tzinfo2 @@ -1,0 +1,173 @@ +require 'common' +require 'net/ssh/transport/state' + +module Transport + + class TestState < Test::Unit::TestCase + + def setup + @socket = @state = @deflater = @inflater = nil + end + + def teardown + if @deflater + @deflater.finish if !@deflater.finished? + @deflater.close + end + + if @inflater + @inflater.finish if !@inflater.finished? + @inflater.close + end + + state.cleanup + end + + def test_constructor_should_initialize_all_values + assert_equal 0, state.sequence_number + assert_equal 0, state.packets + assert_equal 0, state.blocks + + assert_nil state.compression + assert_nil state.compression_level + assert_nil state.max_packets + assert_nil state.max_blocks + assert_nil state.rekey_limit + + assert_equal "identity", state.cipher.name + assert_instance_of Net::SSH::Transport::HMAC::None, state.hmac + end + + def test_increment_should_increment_counters + state.increment(24) + assert_equal 1, state.sequence_number + assert_equal 1, state.packets + assert_equal 3, state.blocks + end + + def test_reset_should_reset_counters_and_fix_defaults_for_maximums + state.increment(24) + state.reset! + assert_equal 1, state.sequence_number + assert_equal 0, state.packets + assert_equal 0, state.blocks + assert_equal 2147483648, state.max_packets + assert_equal 134217728, state.max_blocks + end + + def test_set_should_set_variables_and_reset_counters + state.expects(:reset!) + state.set :cipher => :a, :hmac => :b, :compression => :c, + :compression_level => :d, :max_packets => 500, :max_blocks => 1000, + :rekey_limit => 1500 + assert_equal :a, state.cipher + assert_equal :b, state.hmac + assert_equal :c, state.compression + assert_equal :d, state.compression_level + assert_equal 500, state.max_packets + assert_equal 1000, state.max_blocks + assert_equal 1500, state.rekey_limit + end + + def test_set_with_max_packets_should_respect_max_packets_setting + state.set :max_packets => 500 + assert_equal 500, state.max_packets + end + + def test_set_with_max_blocks_should_respect_max_blocks_setting + state.set :max_blocks => 1000 + assert_equal 1000, state.max_blocks + end + + def test_set_with_rekey_limit_should_include_rekey_limit_in_computation_of_max_blocks + state.set :rekey_limit => 4000 + assert_equal 500, state.max_blocks + end + + def test_compressor_defaults_to_default_zlib_compression + expect = deflater.deflate("hello world") + assert_equal expect, state.compressor.deflate("hello world") + end + + def test_compressor_uses_compression_level_when_given + state.set :compression_level => 1 + expect = deflater(1).deflate("hello world") + assert_equal expect, state.compressor.deflate("hello world") + end + + def test_compress_when_no_compression_is_active_returns_text + assert_equal "hello everybody", state.compress("hello everybody") + end + + def test_decompress_when_no_compression_is_active_returns_text + assert_equal "hello everybody", state.decompress("hello everybody") + end + + def test_compress_when_compression_is_delayed_and_no_auth_hint_is_set_should_return_text + state.set :compression => :delayed + assert_equal "hello everybody", state.compress("hello everybody") + end + + def test_decompress_when_compression_is_delayed_and_no_auth_hint_is_set_should_return_text + state.set :compression => :delayed + assert_equal "hello everybody", state.decompress("hello everybody") + end + + def test_compress_when_compression_is_enabled_should_return_compressed_text + state.set :compression => :standard + assert_equal "x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377", state.compress("hello everybody") + end + + def test_decompress_when_compression_is_enabled_should_return_decompressed_text + state.set :compression => :standard + assert_equal "hello everybody", state.decompress("x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377") + end + + def test_compress_when_compression_is_delayed_and_auth_hint_is_set_should_return_compressed_text + socket.hints[:authenticated] = true + state.set :compression => :delayed + assert_equal "x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377", state.compress("hello everybody") + end + + def test_decompress_when_compression_is_delayed_and_auth_hint_is_set_should_return_decompressed_text + socket.hints[:authenticated] = true + state.set :compression => :delayed + assert_equal "hello everybody", state.decompress("x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377") + end + + def test_needs_rekey_should_be_true_if_packets_exceeds_max_packets + state.set :max_packets => 2 + state.increment(8) + state.increment(8) + assert !state.needs_rekey? + state.increment(8) + assert state.needs_rekey? + end + + def test_needs_rekey_should_be_true_if_blocks_exceeds_max_blocks + state.set :max_blocks => 10 + assert !state.needs_rekey? + state.increment(88) + assert state.needs_rekey? + end + + private + + def deflater(level=Zlib::DEFAULT_COMPRESSION) + @deflater ||= Zlib::Deflate.new(level) + end + + def inflater + @inflater ||= Zlib::Inflate.new(nil) + end + + def socket + @socket ||= stub("socket", :hints => {}) + end + + def state + @state ||= Net::SSH::Transport::State.new(socket, :test) + end + end + +end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_md5.rb File: test_md5.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_md5.rb;tzinfo2 @@ -1,0 +1,39 @@ +require 'common' +require 'net/ssh/transport/hmac/md5' + +module Transport; module HMAC + + class TestMD5 < Test::Unit::TestCase + def test_expected_digest_class + assert_equal OpenSSL::Digest::MD5, subject.digest_class + assert_equal OpenSSL::Digest::MD5, subject.new.digest_class + end + + def test_expected_key_length + assert_equal 16, subject.key_length + assert_equal 16, subject.new.key_length + end + + def test_expected_mac_length + assert_equal 16, subject.mac_length + assert_equal 16, subject.new.mac_length + end + + def test_expected_digest + hmac = subject.new("1234567890123456") + assert_equal "\275\345\006\307y~Oi\035<.\341\031\250<\257", hmac.digest("hello world") + end + + def test_key_should_be_truncated_to_required_length + hmac = subject.new("12345678901234567890") + assert_equal "1234567890123456", hmac.key + end + + private + + def subject + Net::SSH::Transport::HMAC::MD5 + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_md5_96.rb File: test_md5_96.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_md5_96.rb;tzinfo2 @@ -1,0 +1,25 @@ +require 'common' +require 'transport/hmac/test_md5' +require 'net/ssh/transport/hmac/md5_96' + +module Transport; module HMAC + + class TestMD5_96 < TestMD5 + def test_expected_mac_length + assert_equal 12, subject.mac_length + assert_equal 12, subject.new.mac_length + end + + def test_expected_digest + hmac = subject.new("1234567890123456") + assert_equal "\275\345\006\307y~Oi\035<.\341", hmac.digest("hello world") + end + + private + + def subject + Net::SSH::Transport::HMAC::MD5_96 + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_none.rb File: test_none.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_none.rb;tzinfo2 @@ -1,0 +1,34 @@ +require 'common' +require 'net/ssh/transport/hmac/none' + +module Transport; module HMAC + + class TestNone < Test::Unit::TestCase + def test_expected_digest_class + assert_equal nil, subject.digest_class + assert_equal nil, subject.new.digest_class + end + + def test_expected_key_length + assert_equal 0, subject.key_length + assert_equal 0, subject.new.key_length + end + + def test_expected_mac_length + assert_equal 0, subject.mac_length + assert_equal 0, subject.new.mac_length + end + + def test_expected_digest + hmac = subject.new("1234567890123456") + assert_equal "", hmac.digest("hello world") + end + + private + + def subject + Net::SSH::Transport::HMAC::None + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_sha1.rb File: test_sha1.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_sha1.rb;tzinfo2 @@ -1,0 +1,34 @@ +require 'common' +require 'net/ssh/transport/hmac/sha1' + +module Transport; module HMAC + + class TestSHA1 < Test::Unit::TestCase + def test_expected_digest_class + assert_equal OpenSSL::Digest::SHA1, subject.digest_class + assert_equal OpenSSL::Digest::SHA1, subject.new.digest_class + end + + def test_expected_key_length + assert_equal 20, subject.key_length + assert_equal 20, subject.new.key_length + end + + def test_expected_mac_length + assert_equal 20, subject.mac_length + assert_equal 20, subject.new.mac_length + end + + def test_expected_digest + hmac = subject.new("1234567890123456") + assert_equal "\000\004W\202\204+&\335\311\251P\266\250\214\276\206;\022U\365", hmac.digest("hello world") + end + + private + + def subject + Net::SSH::Transport::HMAC::SHA1 + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_sha1_96.rb File: test_sha1_96.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/hmac/test_sha1_96.rb;tzinfo2 @@ -1,0 +1,25 @@ +require 'common' +require 'transport/hmac/test_sha1' +require 'net/ssh/transport/hmac/sha1_96' + +module Transport; module HMAC + + class TestSHA1_96 < TestSHA1 + def test_expected_mac_length + assert_equal 12, subject.mac_length + assert_equal 12, subject.new.mac_length + end + + def test_expected_digest + hmac = subject.new("1234567890123456") + assert_equal "\000\004W\202\204+&\335\311\251P\266", hmac.digest("hello world") + end + + private + + def subject + Net::SSH::Transport::HMAC::SHA1_96 + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb File: test_diffie_hellman_group_exchange_sha1.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb;tzinfo2 @@ -1,0 +1,92 @@ +require 'common' +require 'transport/kex/test_diffie_hellman_group1_sha1' +require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1' + +module Transport; module Kex + + class TestDiffieHellmanGroupExchangeSHA1 < TestDiffieHellmanGroup1SHA1 + KEXDH_GEX_GROUP = 31 + KEXDH_GEX_INIT = 32 + KEXDH_GEX_REPLY = 33 + KEXDH_GEX_REQUEST = 34 + + def test_exchange_with_fewer_than_minimum_bits_uses_minimum_bits + dh_options :need_bytes => 20 + assert_equal 1024, need_bits + assert_nothing_raised { exchange! } + end + + def test_exchange_with_fewer_than_maximum_bits_uses_need_bits + dh_options :need_bytes => 500 + need_bits(4000) + assert_nothing_raised { exchange! } + end + + def test_exchange_with_more_than_maximum_bits_uses_maximum_bits + dh_options :need_bytes => 2000 + need_bits(8192) + assert_nothing_raised { exchange! } + end + + def test_that_p_and_g_are_provided_by_the_server + assert_nothing_raised { exchange! :p => default_p+2, :g => 3 } + assert_equal default_p+2, dh.dh.p + assert_equal 3, dh.dh.g + end + + private + + def need_bits(bits=1024) + @need_bits ||= bits + end + + def default_p + 142326151570335518660743995281621698377057354949884468943021767573608899048361360422513557553514790045512299468953431585300812548859419857171094366358158903433167915517332113861059747425408670144201099811846875730766487278261498262568348338476437200556998366087779709990807518291581860338635288400119315130179 + end + + def exchange!(options={}) + connection.expect do |t, buffer| + assert_equal KEXDH_GEX_REQUEST, buffer.type + assert_equal 1024, buffer.read_long + assert_equal need_bits, buffer.read_long + assert_equal 8192, buffer.read_long + t.return(KEXDH_GEX_GROUP, :bignum, bn(options[:p] || default_p), :bignum, bn(options[:g] || 2)) + t.expect do |t2, buffer2| + assert_equal KEXDH_GEX_INIT, buffer2.type + assert_equal dh.dh.pub_key, buffer2.read_bignum + t2.return(KEXDH_GEX_REPLY, :string, b(:key, server_key), :bignum, server_dh_pubkey, :string, b(:string, options[:key_type] || "ssh-rsa", :string, signature)) + t2.expect do |t3, buffer3| + assert_equal NEWKEYS, buffer3.type + t3.return(NEWKEYS) + end + end + end + + dh.exchange_keys + end + + def subject + Net::SSH::Transport::Kex::DiffieHellmanGroupExchangeSHA1 + end + + def session_id + @session_id ||= begin + buffer = Net::SSH::Buffer.from(:string, packet_data[:client_version_string], + :string, packet_data[:server_version_string], + :string, packet_data[:client_algorithm_packet], + :string, packet_data[:server_algorithm_packet], + :string, Net::SSH::Buffer.from(:key, server_key), + :long, 1024, + :long, need_bits, # need bits, figure this part out, + :long, 8192, + :bignum, dh.dh.p, + :bignum, dh.dh.g, + :bignum, dh.dh.pub_key, + :bignum, server_dh_pubkey, + :bignum, shared_secret) + OpenSSL::Digest::SHA1.digest(buffer.to_s) + end + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/kex/test_diffie_hellman_group1_sha1.rb File: test_diffie_hellman_group1_sha1.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/net-ssh-2.0.13/test/transport/kex/test_diffie_hellman_group1_sha1.rb;tzinfo2 @@ -1,0 +1,146 @@ +require 'common' +require 'net/ssh/transport/kex/diffie_hellman_group1_sha1' +require 'ostruct' + +module Transport; module Kex + + class TestDiffieHellmanGroup1SHA1 < Test::Unit::TestCase + include Net::SSH::Transport::Constants + + def setup + @dh_options = @dh = @algorithms = @connection = @server_key = + @packet_data = @shared_secret = nil + end + + def test_exchange_keys_should_return_expected_results_when_successful + result = exchange! + assert_equal session_id, result[:session_id] + assert_equal server_key.to_blob, result[:server_key].to_blob + assert_equal shared_secret, result[:shared_secret] + assert_equal OpenSSL::Digest::SHA1, result[:hashing_algorithm] + end + + def test_exchange_keys_with_unverifiable_host_should_raise_exception + connection.verifier { false } + assert_raises(Net::SSH::Exception) { exchange! } + end + + def test_exchange_keys_with_signature_key_type_mismatch_should_raise_exception + assert_raises(Net::SSH::Exception) { exchange! :key_type => "ssh-dss" } + end + + def test_exchange_keys_with_host_key_type_mismatch_should_raise_exception + algorithms :host_key => "ssh-dss" + assert_raises(Net::SSH::Exception) { exchange! :key_type => "ssh-dss" } + end + + def test_exchange_keys_when_server_signature_could_not_be_verified_should_raise_exception + @signature = "1234567890" + assert_raises(Net::SSH::Exception) { exchange! } + end + + def test_exchange_keys_should_pass_expected_parameters_to_host_key_verifier + verified = false + connection.verifier do |data| + verified = true + assert_equal server_key.to_blob, data[:key].to_blob + + blob = b(:key, data[:key]).to_s + fingerprint = OpenSSL::Digest::MD5.hexdigest(blob).scan(/../).join(":") + + assert_equal blob, data[:key_blob] + assert_equal fingerprint, data[:fingerprint] + assert_equal connection, data[:session] + + true + end + + assert_nothing_raised { exchange! } + assert verified + end + + private + + def exchange!(options={}) + connection.expect do |t, buffer| + assert_equal KEXDH_INIT, buffer.type + assert_equal dh.dh.pub_key, buffer.read_bignum + t.return(KEXDH_REPLY, :string, b(:key, server_key), :bignum, server_dh_pubkey, :string, b(:string, options[:key_type] || "ssh-rsa", :string, signature)) + connection.expect do |t2, buffer2| + assert_equal NEWKEYS, buffer2.type + t2.return(NEWKEYS) + end + end + + dh.exchange_keys + end + + def dh_options(options={}) + @dh_options = options + end + + def dh + @dh ||= subject.new(algorithms, connection, packet_data.merge(:need_bytes => 20).merge(@dh_options || {})) + end + + def algorithms(options={}) + @algorithms ||= OpenStruct.new(:host_key => options[:host_key] || "ssh-rsa") + end + + def connection + @connection ||= MockTransport.new + end + + def subject + Net::SSH::Transport::Kex::DiffieHellmanGroup1SHA1 + end + + # 368 bits is the smallest possible key that will work with this, so + # we use it for speed reasons + def server_key(bits=368) + @server_key ||= OpenSSL::PKey::RSA.new(bits) + end + + def packet_data + @packet_data ||= { :client_version_string => "client version string", + :server_version_string => "server version string", + :server_algorithm_packet => "server algorithm packet", + :client_algorithm_packet => "client algorithm packet" } + end + + def server_dh_pubkey + @server_dh_pubkey ||= bn(1234567890) + end + + def shared_secret + @shared_secret ||= OpenSSL::BN.new(dh.dh.compute_key(server_dh_pubkey), 2) + end + + def session_id + @session_id ||= begin + buffer = Net::SSH::Buffer.from(:string, packet_data[:client_version_string], + :string, packet_data[:server_version_string], + :string, packet_data[:client_algorithm_packet], + :string, packet_data[:server_algorithm_packet], + :string, Net::SSH::Buffer.from(:key, server_key), + :bignum, dh.dh.pub_key, + :bignum, server_dh_pubkey, + :bignum, shared_secret) + OpenSSL::Digest::SHA1.digest(buffer.to_s) + end + end + + def signature + @signature ||= server_key.ssh_do_sign(session_id) + end + + def bn(number, base=10) + OpenSSL::BN.new(number.to_s, base) + end + + def b(*args) + Net::SSH::Buffer.from(*args) + end + end + +end; end \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/CHANGES File: CHANGES =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/CHANGES;tzinfo2 @@ -1,0 +1,125 @@ +== 1.4.5 - 24-Aug-2009 +* Reverted the change in 1.4.4. We need unsigned longs in a few cases. + Consequently, you should upgrade windows-pr to 1.0.8 or later because + some return values were set to -1 instead of 0xFFFFFFFF as they are now. +* Updated one test to validate return value from failed function. + +== 1.4.4 - 18-Aug-2009 +* Fixed a bug where functions that returned a long return type were unsigned + instead of signed. +* The Rakefile has been refactored somewhat, including the removal of FFI + related tasks. Those are now in a separate branch in the repository. +* The test-unit library is now a development dependency instead of a runtime + dependency. + +== 1.4.3 - 23-Jun-2009 +* Bug fix for mingw. +* License now set to Artistic 2.0. + +== 1.4.2 - 31-May-2009 +* Updated the internal StringError() function to better handle the possibility + of the English .mui file not being found. Thanks go to Michel Demazure for + the spot. + +== 1.4.1 - 29-May-2009 +* Callback handling improvements. +* Updated the gemspec description. + +== 1.4.0 - 19-Feb-2009 +* Now compatible with Ruby 1.9.x. +* In what will go down as, "It seemed like a good idea at the time", I have + removed the feature where W (wide) character functions were used first if + your $KCODE environment variable was set to "UTF8". It caused too many + headaches in practice, especially amongst Rails users. You must now always + explicitly specify 'W' in the constructor if that's what you want. +* Fixed RF bug #23944 - bad error message for MSVCRT functions that failed + to load properly. + +== 1.3.0 - 1-Jan-2009 +* Fixed RubyForge bug #23395, which was caused by inadvertently modifying + a variable within a loop. This caused callbacks to fail in certain + situations. +* Added the Win32::API::LoadLibraryError and Win32::API::PrototypeError classes + to provide more fine grained handling of possible error conditions in the + constructor. These are both subclasses of Win32::API::Error. +* Removed the Win32::API::CallbackError class. +* Changed the upper limit on prototypes from 16 to 20. It turns out that + there are actually Windows functions with more than 16 prototypes. +* Refactored a high iteration test so that it counts as only one test + instead of 1000. + +== 1.2.2 - 27-Nov-2008 +* Fixed bug in the error message for illegal prototypes and illegal return + types where the character in question would come out as empty or garbage. +* Passing a bad return type to Win32::API::Callback now raises an error. +* Updated the error message for illegal return types to say, "Illegal return + type" instead of "Illegal prototype" as it did previously. +* The error message for a bad function name passed to Win32::API.new now + matches JRuby's FFI error message. +* Improved the handling of msvcrt functions with regards to skipping 'A' + and 'W' checks. Previously it was checking against the literal string + 'msvcrt'. Now it checks against any string that starts with 'msvcr'. +* Added test-unit 2.x as a prerequisite. +* Added tests for the Win32::API::Callback#address method. +* Added tests to all Win32::API classes that explicitly check error messages. + +== 1.2.1 - 14-Nov-2008 +* Fixed and updated callback handling. +* Fixed wide string return handling for pointers and strings. +* Added the Win32::API::Callback#address instance method. +* All errors are now in English instead of your native language, because + that's what Ruby itself does. + +== 1.2.0 - 22-Jul-2008 +* Added support for the 'S' (string) prototype and return type. It can be + used instead of 'P' (pointer) for const char*. +* Some internal refactoring. The attempts to load ANSI and/or Wide character + versions of functions are skipped for MSVCRT functions, since they do not + exist. This eliminates some unnecessary LoadLibrary() calls. +* Added a couple of gem building Rake tasks. +* Added a few more tests. + +== 1.1.0 - 12-Jun-2008 +* Added the Windows::API::Function class. This is a subclass of Win32::API + meant only for use with raw function pointers. +* Some documentation updates in the source and README files. + +== 1.0.6 - 18-Apr-2008 +* Added the effective_function_name method. This allows you to see what the + actual function name is that was defined, e.g. GetUserNameA vs GetUserNameW. +* Replaced an instance of _tcscmp with strcmp. The case in question was always + going to be ASCII. +* Cleaned up some -W3 warnings. +* Added the build_manifest task to the Rakefile, which is automatically run if + you're using a version of Ruby built with VC++ 8 or later. This builds a + ruby.exe.manifest file (if it doesn't already exist). + +== 1.0.5 - 20-Nov-2007 +* The API.new method now defaults to "W" (wide character functions) before "A" + (ANSI functions) if the $KCODE global variable is set to 'u' (UTF8). +* Minor improvements to the Rakefile. + +== 1.0.4 - 26-Oct-2007 +* Fixed a bug where methods that returned pointers ('P') could choke if the + resulting pointer was 0 or NULL. In this case, nil is now returned instead. +* Tweak to the extconf.rb file that helps the gem build it from source + properly. + +== 1.0.3 - 28-Sep-2007 +* Fixed a subtle but dangerous copy-on-write bug in the API#call method. + +== 1.0.2 - 28-Sep-2007 +* Fixed a bug in an internal struct member that was causing segfaults. Thanks + go to Lars Olsson for the spot. +* Fixed the 'install' task in the Rakefile. This only affected native builds, + not the prebuilt binary. +* Added a few more tests. + +== 1.0.1 - 27-Sep-2007 +* Functions declared with a void prototype no longer require an explicit nil + argument to fulfill the arity requirement. You can still call them with an + explicit nil if you wish, however. +* Fixed the gemspec for the native build. + +== 1.0.0 - 14-Sep-2007 +* Initial release =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/MANIFEST File: MANIFEST =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/MANIFEST;tzinfo2 @@ -1,0 +1,10 @@ +* CHANGES +* MANIFEST +* README +* Rakefile +* win32-api.gemspec +* ext/extconf.rb +* ext/win32/api.c +* test/test_win32_api.rb +* test/test_win32_api_callback.rb +* test/test_win32_api_function.rb \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/Rakefile File: Rakefile =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/Rakefile;tzinfo2 @@ -1,0 +1,120 @@ +require 'rake' +require 'rake/clean' +require 'rake/testtask' +require 'rbconfig' +include Config + +desc 'Build the ruby.exe.manifest if it does not already exist' +task :build_manifest do + version = CONFIG['host_os'].split('_')[1] + + if version && version.to_i >= 80 + unless File.exist?(File.join(CONFIG['bindir'], 'ruby.exe.manifest')) + Dir.chdir(CONFIG['bindir']) do + sh "mt -inputresource:ruby.exe;2 -out:ruby.exe.manifest" + end + end + end +end + +desc 'Install the win32-api library (non-gem)' +task :install => [:build] do + Dir.chdir('ext'){ + sh 'nmake install' + } +end + +task :install_gem do + ruby 'win32-api.gemspec' + file = Dir["*.gem"].first + sh "gem install #{file}" +end + +desc 'Clean any build files for Win32::API' +task :clean do + Dir.chdir('ext') do + if File.exists?('api.so') || File.exists?('win32/api.so') || + File.exists?('api.obj') + then + sh 'nmake distclean' + rm 'win32/api.so' if File.exists?('win32/api.so') + rm 'api.so' if File.exists?('api.so') + end + end + + # Cleanup any files generated by the build_binary_gem task + rm_rf('lib') if File.exists?('lib') +end + +desc "Build Win32::API (but don't install it)" +task :build => [:clean, :build_manifest] do + Dir.chdir('ext') do + ruby "extconf.rb" + sh "nmake" + mv "api.so", "win32" # For the test suite + end +end + +desc 'Build a standard gem' +task :build_gem => [:clean] do + eval(IO.read('win32-api.gemspec')) +end + +desc 'Build a binary gem' +task :build_binary_gem => [:build] do + mkdir_p 'lib/win32' + mv 'ext/win32/api.so', 'lib/win32' + + task :build_binary_gem => [:clean] + + spec = Gem::Specification.new do |gem| + gem.name = 'win32-api' + gem.version = '1.4.5' + gem.authors = ['Daniel J. Berger', 'Park Heesob'] + gem.license = 'Artistic 2.0' + gem.email = 'djberg96@gmail.com' + gem.homepage = 'http://www.rubyforge.org/projects/win32utils' + gem.platform = Gem::Platform::CURRENT + gem.summary = 'A superior replacement for Win32API' + gem.has_rdoc = true + gem.test_files = Dir['test/test*'] + + gem.files = [ + Dir['*'], + Dir['test/*'], + 'ext/extconf.rb', + 'ext/win32/api.c', + 'lib/win32/api.so' + ].flatten.reject{ |f| f.include?('CVS') } + + gem.rubyforge_project = 'win32utils' + gem.required_ruby_version = '>= 1.8.2' + + gem.extra_rdoc_files = [ + 'README', + 'CHANGES', + 'MANIFEST', + 'ext/win32/api.c' + ] + + gem.description = <<-EOF + The Win32::API library is meant as a replacement for the Win32API + library that ships as part of the standard library. It contains several + advantages over Win32API, including callback support, raw function + pointers, an additional string type, and more. + EOF + end + + Gem::Builder.new(spec).build +end + +Rake::TestTask.new(:test) do |test| + task :test => [:build] + test.libs << 'ext' + test.warning = true + test.verbose = true +end + +task :test do + Rake.application[:clean].execute +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/README File: README =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/README;tzinfo2 @@ -1,0 +1,103 @@ += Description + This is a drop-in replacement for the Win32API library currently part of + Ruby's standard library. + += Synopsis + require 'win32/api' + include Win32 + + # Typical example - Get user name + buf = 0.chr * 260 + len = [buf.length].pack('L') + + GetUserName = API.new('GetUserName', 'PP', 'I', 'advapi32') + GetUserName.call(buf, len) + + puts buf.strip + + # Callback example - Enumerate windows + EnumWindows = API.new('EnumWindows', 'KP', 'L', 'user32') + GetWindowText = API.new('GetWindowText', 'LPI', 'I', 'user32') + EnumWindowsProc = API::Callback.new('LP', 'I'){ |handle, param| + buf = "\0" * 200 + GetWindowText.call(handle, buf, 200); + puts buf.strip unless buf.strip.empty? + buf.index(param).nil? ? true : false + } + + EnumWindows.call(EnumWindowsProc, 'UEDIT32') + + # Raw function pointer example - System beep + LoadLibrary = API.new('LoadLibrary', 'P', 'L') + GetProcAddress = API.new('GetProcAddress', 'LP', 'L') + + hlib = LoadLibrary.call('user32') + addr = GetProcAddress.call(hlib, 'MessageBeep') + func = Win32::API::Function.new(addr, 'L', 'L') + func.call(0) + += Differences between win32-api and Win32API + * This library has callback support + * This library supports raw function pointers. + * This library supports a separate string type for const char* (S). + * Argument order change. The DLL name is now last, not first. + * Removed the 'N' and 'n' prototypes. Always use 'L' for longs now. + * Sensible default arguments for the prototype, return type and DLL name. + * Reader methods for the function name, effective function name, prototype, + return type and DLL. + * Removed the support for lower case prototype and return types. Always + use capital letters. + * Resorts to wide character functions (where possible) when $KCODE is set + to UTF8. + += Developer's Notes + The current Win32API library that ships with the standard library has been + slated for removal from Ruby 2.0 and it will not receive any updates in the + Ruby 1.8.x branch. I have far too many libraries invested in it to let it + die at this point. + + In addition, the current Win32API library was written in the bad old Ruby + 1.6.x days, which means it doesn't use the newer allocation framework. + There were several other refactorings that I felt it needed to more closely + match how it was actually being used in practice. + + The first order of business was changing the order of the arguments. By + moving the DLL name from first to last, I was able to provide reasonable + default arguments for the prototype, return type and the DLL. Only the + function name is required now. + + There was a laundry list of other refactorings that were needed: sensical + instance variable names with proper accessors, removing support for lower + case prototype and return value characters that no one used in practice, + better naming conventions, the addition of RDoc ready comments and, + especially, callback and raw function pointer support. + + Most importantly, we can now add, modify and fix any features that we feel + best benefit our end users. + += Documentation + The source file contains inline RDoc documentation. If you installed + this file as a gem, then you have the docs. + += Warranty + This package is provided "as is" and without any express or + implied warranties, including, without limitation, the implied + warranties of merchantability and fitness for a particular purpose. + += Known Issues + Possible callback issues when dealing with multi-threaded applications. + We are working on this for the next 1.2.x release. + + Please submit any bug reports to the project page at + http://www.rubyforge.org/projects/win32utils. + += Copyright + (C) 2003-2009 Daniel J. Berger + All Rights Reserved + += License + Artistic 2.0 + += Authors + Daniel J. Berger + Park Heesob =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/test add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/win32-api.gemspec File: win32-api.gemspec =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/win32-api.gemspec;tzinfo2 @@ -1,0 +1,31 @@ +require 'rubygems' + +spec = Gem::Specification.new do |gem| + gem.name = 'win32-api' + gem.version = '1.4.5' + gem.authors = ['Daniel J. Berger', 'Park Heesob'] + gem.license = 'Artistic 2.0' + gem.email = 'djberg96@gmail.com' + gem.homepage = 'http://www.rubyforge.org/projects/win32utils' + gem.platform = Gem::Platform::RUBY + gem.summary = 'A superior replacement for Win32API' + gem.has_rdoc = true + gem.test_files = Dir['test/test*'] + gem.extensions = ['ext/extconf.rb'] + gem.files = Dir['**/*'].reject{ |f| f.include?('CVS') || f.include?('lib') } + + gem.rubyforge_project = 'win32utils' + gem.required_ruby_version = '>= 1.8.2' + gem.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST', 'ext/win32/api.c'] + + gem.add_development_dependency('test-unit', '>= 2.0.2') + + gem.description = <<-EOF + The Win32::API library is meant as a replacement for the Win32API + library that ships as part of the standard library. It contains several + advantages over Win32API, including callback support, raw function + pointers, an additional string type, and more. + EOF +end + +Gem::Builder.new(spec).build =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/extconf.rb File: extconf.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/extconf.rb;tzinfo2 @@ -1,0 +1,11 @@ +########################################################################## +# extconf.rb +# +# The Windows::API binary should be built using the Rake task, i.e. +# 'rake build' or 'rake install'. +########################################################################## +require 'mkmf' + +have_func('strncpy_s') + +create_makefile('win32/api', 'win32') =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/gem_make.out File: gem_make.out =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/gem_make.out;tzinfo2 @@ -1,0 +1,7 @@ +C:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/bin/ruby.exe extconf.rb +checking for strncpy_s()... no +creating Makefile + +make +'make' is not recognized as an internal or external command, +operable program or batch file. =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/Makefile File: Makefile =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/Makefile;tzinfo2 @@ -1,0 +1,149 @@ + +SHELL = /bin/sh + +#### Start of system configuration section. #### + +srcdir = win32 +topdir = /C/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/1.8/i386-mingw32 +hdrdir = $(topdir) +VPATH = $(srcdir):$(topdir):$(hdrdir) + +DESTDIR = C: +prefix = $(DESTDIR)/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368 +exec_prefix = $(prefix) +sitedir = $(prefix)/lib/ruby/site_ruby +rubylibdir = $(libdir)/ruby/$(ruby_version) +docdir = $(datarootdir)/doc/$(PACKAGE) +dvidir = $(docdir) +datarootdir = $(prefix)/share +archdir = $(rubylibdir)/$(arch) +sbindir = $(exec_prefix)/sbin +psdir = $(docdir) +localedir = $(datarootdir)/locale +htmldir = $(docdir) +datadir = $(datarootdir) +includedir = $(prefix)/include +infodir = $(datarootdir)/info +sysconfdir = $(prefix)/etc +mandir = $(datarootdir)/man +libdir = $(exec_prefix)/lib +sharedstatedir = $(prefix)/com +oldincludedir = $(DESTDIR)/usr/include +pdfdir = $(docdir) +sitearchdir = $(sitelibdir)/$(sitearch) +bindir = $(exec_prefix)/bin +localstatedir = $(prefix)/var +sitelibdir = $(sitedir)/$(ruby_version) +libexecdir = $(exec_prefix)/libexec + +CC = gcc +LIBRUBY = lib$(LIBRUBY_SO).a +LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a +LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME) +LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static + +RUBY_EXTCONF_H = +CFLAGS = -g -O2 +INCFLAGS = -I. -I. -IC:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/1.8/i386-mingw32 -I. +DEFS = +CPPFLAGS = +CXXFLAGS = $(CFLAGS) +DLDFLAGS = -L. -Wl,--enable-auto-image-base,--enable-auto-import,--export-all +LDSHARED = gcc -shared -s +AR = ar +EXEEXT = .exe + +RUBY_INSTALL_NAME = ruby +RUBY_SO_NAME = msvcrt-ruby18 +arch = i386-mingw32 +sitearch = i386-msvcrt +ruby_version = 1.8 +ruby = C:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/bin/ruby +RUBY = $(ruby) +RM = rm -f +MAKEDIRS = mkdir -p +INSTALL = /bin/install -c +INSTALL_PROG = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 644 +COPY = cp + +#### End of system configuration section. #### + +preload = + +libpath = . $(libdir) +LIBPATH = -L. -L$(libdir) +DEFFILE = + +CLEANFILES = mkmf.log +DISTCLEANFILES = + +extout = +extout_prefix = +target_prefix = /win32 +LOCAL_LIBS = +LIBS = $(LIBRUBYARG_SHARED) -lshell32 -lws2_32 +SRCS = api.c +OBJS = api.o +TARGET = api +DLLIB = $(TARGET).so +EXTSTATIC = +STATIC_LIB = + +RUBYCOMMONDIR = $(sitedir)$(target_prefix) +RUBYLIBDIR = C:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/lib$(target_prefix) +RUBYARCHDIR = C:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/lib$(target_prefix) + +TARGET_SO = $(DLLIB) +CLEANLIBS = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map +CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak + +all: $(DLLIB) +static: $(STATIC_LIB) + +clean: + @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) + +distclean: clean + @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) + +realclean: distclean +install: install-so install-rb + +install-so: $(RUBYARCHDIR) +install-so: $(RUBYARCHDIR)/$(DLLIB) +$(RUBYARCHDIR)/$(DLLIB): $(DLLIB) + $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) +install-rb: pre-install-rb install-rb-default +install-rb-default: pre-install-rb-default +pre-install-rb: Makefile +pre-install-rb-default: Makefile +$(RUBYARCHDIR): + $(MAKEDIRS) $@ + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + +.SUFFIXES: .c .m .cc .cxx .cpp .o + +.cc.o: + $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< + +.cxx.o: + $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< + +.cpp.o: + $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< + +.c.o: + $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $< + +$(DLLIB): $(OBJS) Makefile + @-$(RM) $@ + $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) + + + +$(OBJS): ruby.h defines.h =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/mkmf.log File: mkmf.log =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/mkmf.log;tzinfo2 @@ -1,0 +1,23 @@ +have_func: checking for strncpy_s()... -------------------- no + +"gcc -o conftest -I. -IC:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/1.8/i386-mingw32 -I. -g -O2 conftest.c -L. -LC:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib -L. -lmsvcrt-ruby18-static -lshell32 -lws2_32 " +checked program was: +/* begin */ +1: #include +2: #include +3: +4: /*top*/ +5: int main() { return 0; } +6: int t() { void ((*volatile p)()); p = (void ((*)()))strncpy_s; return 0; } +/* end */ + +"gcc -o conftest -I. -IC:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/1.8/i386-mingw32 -I. -g -O2 conftest.c -L. -LC:/vsl/m1/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib -L. -lmsvcrt-ruby18-static -lshell32 -lws2_32 " +checked program was: +/* begin */ +1: /*top*/ +2: int main() { return 0; } +3: int t() { strncpy_s(); return 0; } +/* end */ + +-------------------- + =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/win32 add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/win32/api.c File: api.c =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/ext/win32/api.c;tzinfo2 @@ -1,0 +1,948 @@ +#include +#include + +// Ruby 1.9.x +#ifndef RSTRING_PTR +#define RSTRING_PTR(s) (RSTRING(s)->ptr) +#endif +#ifndef RSTRING_LEN +#define RSTRING_LEN(s) (RSTRING(s)->len) +#endif + +#ifndef RARRAY_PTR +#define RARRAY_PTR(a) (RARRAY(a)->ptr) +#endif +#ifndef RARRAY_LEN +#define RARRAY_LEN(a) (RARRAY(a)->len) +#endif + +#define MAX_BUF 1024 +#define WINDOWS_API_VERSION "1.4.5" + +#define _T_VOID 0 +#define _T_LONG 1 +#define _T_POINTER 2 +#define _T_INTEGER 3 +#define _T_CALLBACK 4 +#define _T_STRING 5 + +VALUE cAPIError, cAPIProtoError, cAPILoadError; +static VALUE ActiveCallback = Qnil; + +typedef struct { + HANDLE library; + FARPROC function; + int return_type; + int prototype[20]; +} Win32API; + +static void api_free(Win32API* ptr){ + if(ptr->library) + FreeLibrary(ptr->library); + + if(ptr) + free(ptr); +} + +static VALUE api_allocate(VALUE klass){ + Win32API* ptr = malloc(sizeof(Win32API)); + return Data_Wrap_Struct(klass, 0, api_free, ptr); +} + +/* Helper function that converts the error number returned by GetLastError() + * into a human readable string. Note that we always use English for error + * output because that's what Ruby itself does. + * + * Internal use only. + */ +char* StringError(DWORD dwError){ + LPVOID lpMsgBuf; + static char buf[MAX_PATH]; + DWORD dwLen; + + // Assume ASCII error messages from the Windows API + dwLen = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwError, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&lpMsgBuf, + 0, + NULL + ); + + /* It appears that Windows doesn't necessarily ship with the DLL + * required to always use English error messages. Check for error + * ERROR_MUI_FILE_NOT_FOUND (15100), and try again if necessary. + */ + if(!dwLen && GetLastError() == 15100){ + dwLen = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwError, + 0, + (LPSTR)&lpMsgBuf, + 0, + NULL + ); + } + + if(!dwLen){ + rb_raise( + cAPIError, + "Attempt to format message failed (error = '%d')", + GetLastError() + ); + } + + memset(buf, 0, MAX_PATH); + + /* remove \r\n */ +#ifdef HAVE_STRNCPY_S + strncpy_s(buf, MAX_PATH, lpMsgBuf, dwLen - 2); +#else + strncpy(buf, lpMsgBuf, dwLen - 2); +#endif + + LocalFree(lpMsgBuf); + + return buf; +} + +/* + * call-seq: + * Win32::API::Callback.new(prototype, return='L'){ |proto| ... } + * + * Creates and returns a new Win32::API::Callback object. The prototype + * arguments are yielded back to the block in the same order they were + * declared. + * + * The +prototype+ is the function prototype for the callback function. This + * is a string. The possible valid characters are 'I' (integer), 'L' (long), + * 'V' (void), 'P' (pointer) or 'S' (string). Unlike API objects, API::Callback + * objects do not have a default prototype. + * + * The +return+ argument is the return type for the callback function. The + * valid characters are the same as for the +prototype+. The default is + * 'L' (long). + * + * Example: + * require 'win32/api' + * include Win32 + * + * EnumWindows = API.new('EnumWindows', 'KP', 'L', 'user32') + * GetWindowText = API.new('GetWindowText', 'LPI', 'I', 'user32') + * + * EnumWindowsProc = API::Callback.new('LP', 'I'){ |handle, param| + * buf = "\0" * 200 + * GetWindowText.call(handle, buf, 200); + * puts buf.strip unless buf.strip.empty? + * buf.index(param).nil? ? true : false + * } + * + * EnumWindows.call(EnumWindowsProc, 'UEDIT32') + */ +static VALUE callback_init(int argc, VALUE* argv, VALUE self) +{ + void *find_callback(VALUE,int); + VALUE v_proto, v_return, v_proc; + int i; + + rb_scan_args(argc, argv, "11&", &v_proto, &v_return, &v_proc); + + /* Validate prototype characters */ + for(i = 0; i < RSTRING_LEN(v_proto); i++){ + switch(RSTRING_PTR(v_proto)[i]){ + case 'I': case 'L': case 'P': case 'V': case 'S': + break; + default: + rb_raise(cAPIProtoError, "Illegal prototype '%c'", + RSTRING_PTR(v_proto)[i] + ); + } + } + + if(NIL_P(v_return) || RARRAY_LEN(v_return) == 0){ + v_return = rb_str_new2("L"); + } + else{ + switch(*(char*)RSTRING_PTR(v_return)){ + case 'I': case 'L': case 'P': case 'V': case 'S': + break; + default: + rb_raise(cAPIProtoError, "Illegal return type '%s'", + RSTRING_PTR(v_return) + ); + } + } + + rb_iv_set(self, "@function", v_proc); + rb_iv_set(self, "@prototype", v_proto); + rb_iv_set(self, "@return_type", v_return); + rb_iv_set(self, "@address", ULONG2NUM((LPARAM)find_callback(self,RSTRING_LEN(v_proto)))); + ActiveCallback = self; + + return self; +} + +/* + * call-seq: + * Win32::API.new(function, prototype='V', return='L', dll='kernel32') + * + * Creates and returns a new Win32::API object. The +function+ is the name + * of the Windows function. + * + * The +prototype+ is the function prototype for +function+. This can be a + * string or an array of characters. The possible valid characters are 'I' + * (integer), 'L' (long), 'V' (void), 'P' (pointer), 'K' (callback) or 'S' + * (string). + * + * The default is void ('V'). + * + * Constant (const char*) strings should use 'S'. Pass by reference string + * buffers should use 'P'. The former is faster, but cannot be modified. + * + * The +return+ argument is the return type for the function. The valid + * characters are the same as for the +prototype+. The default is 'L' (long). + * + * The +dll+ is the name of the DLL file that the function is exported from. + * The default is 'kernel32'. + * + * If the function cannot be found then an API::Error is raised (a subclass + * of RuntimeError). + * + * Example: + * + * require 'win32/api' + * include Win32 + * + * buf = 0.chr * 260 + * len = [buf.length].pack('L') + * + * GetUserName = API.new('GetUserName', 'PP', 'I', 'advapi32') + * GetUserName.call(buf, len) + * + * puts buf.strip + */ +static VALUE api_init(int argc, VALUE* argv, VALUE self) +{ + HMODULE hLibrary; + FARPROC fProc; + Win32API* ptr; + int i; + char* first = "A"; + char* second = "W"; + VALUE v_proc, v_proto, v_return, v_dll; + + rb_scan_args(argc, argv, "13", &v_proc, &v_proto, &v_return, &v_dll); + + Data_Get_Struct(self, Win32API, ptr); + + // Convert a string prototype to an array of characters + if(rb_respond_to(v_proto, rb_intern("split"))) + v_proto = rb_str_split(v_proto, ""); + + // Convert a nil or empty prototype to 'V' (void) automatically + if(NIL_P(v_proto) || RARRAY_LEN(v_proto) == 0){ + v_proto = rb_ary_new(); + rb_ary_push(v_proto, rb_str_new2("V")); + } + + // Set an arbitrary limit of 20 parameters + if(20 < RARRAY_LEN(v_proto)) + rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY_LEN(v_proto)); + + // Set the default dll to 'kernel32' + if(NIL_P(v_dll)) + v_dll = rb_str_new2("kernel32"); + + // Set the default return type to 'L' (DWORD) + if(NIL_P(v_return)) + v_return = rb_str_new2("L"); + + SafeStringValue(v_dll); + SafeStringValue(v_proc); + + hLibrary = LoadLibrary(TEXT(RSTRING_PTR(v_dll))); + + // The most likely cause of failure is a bad DLL load path + if(!hLibrary){ + rb_raise(cAPILoadError, "LoadLibrary() function failed for '%s': %s", + RSTRING_PTR(v_dll), + StringError(GetLastError()) + ); + } + + ptr->library = hLibrary; + + /* Attempt to get the function. If it fails, try again with an 'A' + * appended. If that fails, try again with a 'W' appended. If that + * still fails, raise an API::LoadLibraryError. + */ + + fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_proc))); + + // Skip the ANSI and Wide function checks for MSVCRT functions. + if(!fProc){ + if(strstr(RSTRING_PTR(v_dll), "msvcr")){ + rb_raise( + cAPILoadError, + "Unable to load function '%s'", + RSTRING_PTR(v_proc) + ); + } + else{ + VALUE v_ascii = rb_str_new3(v_proc); + v_ascii = rb_str_cat(v_ascii, first, 1); + fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_ascii))); + + if(!fProc){ + VALUE v_unicode = rb_str_new3(v_proc); + v_unicode = rb_str_cat(v_unicode, second, 1); + fProc = GetProcAddress(hLibrary, TEXT(RSTRING_PTR(v_unicode))); + + if(!fProc){ + rb_raise( + cAPILoadError, + "Unable to load function '%s', '%s', or '%s'", + RSTRING_PTR(v_proc), + RSTRING_PTR(v_ascii), + RSTRING_PTR(v_unicode) + ); + } + else{ + rb_iv_set(self, "@effective_function_name", v_unicode); + } + } + else{ + rb_iv_set(self, "@effective_function_name", v_ascii); + } + } + } + else{ + rb_iv_set(self, "@effective_function_name", v_proc); + } + + ptr->function = fProc; + + // Push the numeric prototypes onto our int array for later use. + + for(i = 0; i < RARRAY_LEN(v_proto); i++){ + SafeStringValue(RARRAY_PTR(v_proto)[i]); + switch(*(char*)StringValuePtr(RARRAY_PTR(v_proto)[i])){ + case 'L': + ptr->prototype[i] = _T_LONG; + break; + case 'P': + ptr->prototype[i] = _T_POINTER; + break; + case 'I': case 'B': + ptr->prototype[i] = _T_INTEGER; + break; + case 'V': + ptr->prototype[i] = _T_VOID; + break; + case 'K': + ptr->prototype[i] = _T_CALLBACK; + break; + case 'S': + ptr->prototype[i] = _T_STRING; + break; + default: + rb_raise(cAPIProtoError, "Illegal prototype '%s'", + StringValuePtr(RARRAY_PTR(v_proto)[i]) + ); + } + } + + // Store the return type for later use. + + // Automatically convert empty strings or nil to type void. + if(NIL_P(v_return) || RSTRING_LEN(v_return) == 0){ + v_return = rb_str_new2("V"); + ptr->return_type = _T_VOID; + } + else{ + SafeStringValue(v_return); + switch(*RSTRING_PTR(v_return)){ + case 'L': + ptr->return_type = _T_LONG; + break; + case 'P': + ptr->return_type = _T_POINTER; + break; + case 'I': case 'B': + ptr->return_type = _T_INTEGER; + break; + case 'V': + ptr->return_type = _T_VOID; + break; + case 'S': + ptr->return_type = _T_STRING; + break; + default: + rb_raise(cAPIProtoError, "Illegal return type '%s'", + RSTRING_PTR(v_return) + ); + } + } + + rb_iv_set(self, "@dll_name", v_dll); + rb_iv_set(self, "@function_name", v_proc); + rb_iv_set(self, "@prototype", v_proto); + rb_iv_set(self, "@return_type", v_return); + + return self; +} + +/* + * call-seq: + * + * API::Function.new(address, prototype = 'V', return_type = 'L') + * + * Creates and returns an API::Function object. This object is similar to an + * API object, except that instead of a character function name you pass a + * function pointer address as the first argument, and there's no associated + * DLL file. + * + * Once you have your API::Function object you can then call it the same way + * you would an API object. + * + * Example: + * + * require 'win32/api' + * include Win32 + * + * LoadLibrary = API.new('LoadLibrary', 'P', 'L') + * GetProcAddress = API.new('GetProcAddress', 'LP', 'L') + * + * # Play a system beep + * hlib = LoadLibrary.call('user32') + * addr = GetProcAddress.call(hlib, 'MessageBeep') + * func = Win32::API::Function.new(addr, 'L', 'L') + * func.call(0) + */ +static VALUE func_init(int argc, VALUE* argv, VALUE self){ + Win32API* ptr; + int i; + VALUE v_address, v_proto, v_return; + + rb_scan_args(argc, argv, "12", &v_address, &v_proto, &v_return); + + Data_Get_Struct(self, Win32API, ptr); + + // Convert a string prototype to an array of characters + if(rb_respond_to(v_proto, rb_intern("split"))) + v_proto = rb_str_split(v_proto, ""); + + // Convert a nil or empty prototype to 'V' (void) automatically + if(NIL_P(v_proto) || RARRAY_LEN(v_proto) == 0){ + v_proto = rb_ary_new(); + rb_ary_push(v_proto, rb_str_new2("V")); + } + + // Set an arbitrary limit of 20 parameters + if(20 < RARRAY_LEN(v_proto)) + rb_raise(rb_eArgError, "too many parameters: %d\n", RARRAY_LEN(v_proto)); + + // Set the default return type to 'L' (DWORD) + if(NIL_P(v_return)) + v_return = rb_str_new2("L"); + + ptr->function = (FARPROC)NUM2LONG(v_address); + + // Push the numeric prototypes onto our int array for later use. + + for(i = 0; i < RARRAY_LEN(v_proto); i++){ + SafeStringValue(RARRAY_PTR(v_proto)[i]); + switch(*(char*)StringValuePtr(RARRAY_PTR(v_proto)[i])){ + case 'L': + ptr->prototype[i] = _T_LONG; + break; + case 'P': + ptr->prototype[i] = _T_POINTER; + break; + case 'I': case 'B': + ptr->prototype[i] = _T_INTEGER; + break; + case 'V': + ptr->prototype[i] = _T_VOID; + break; + case 'K': + ptr->prototype[i] = _T_CALLBACK; + break; + case 'S': + ptr->prototype[i] = _T_STRING; + break; + default: + rb_raise(cAPIProtoError, "Illegal prototype '%s'", + StringValuePtr(RARRAY_PTR(v_proto)[i]) + ); + } + } + + // Store the return type for later use. + + // Automatically convert empty strings or nil to type void. + if(NIL_P(v_return) || RSTRING_LEN(v_return) == 0){ + v_return = rb_str_new2("V"); + ptr->return_type = _T_VOID; + } + else{ + SafeStringValue(v_return); + switch(*RSTRING_PTR(v_return)){ + case 'L': + ptr->return_type = _T_LONG; + break; + case 'P': + ptr->return_type = _T_POINTER; + break; + case 'I': case 'B': + ptr->return_type = _T_INTEGER; + break; + case 'V': + ptr->return_type = _T_VOID; + break; + case 'S': + ptr->return_type = _T_STRING; + break; + default: + rb_raise(cAPIProtoError, "Illegal return type '%s'", + RSTRING_PTR(v_return) + ); + } + } + + rb_iv_set(self, "@address", v_address); + rb_iv_set(self, "@prototype", v_proto); + rb_iv_set(self, "@return_type", v_return); + + return self; +} + +typedef struct { + DWORD params[20]; +} CALLPARAM; + + +DWORD CallbackFunction(CALLPARAM param,VALUE callback) +{ + VALUE v_proto, v_return, v_proc, v_retval; + VALUE argv[20]; + int i, argc; + char *a_proto; + char *a_return; + + if(callback && !NIL_P(callback)){ + v_proto = rb_iv_get(callback, "@prototype"); + a_proto = RSTRING_PTR(v_proto); + + v_return = rb_iv_get(callback, "@return_type"); + a_return = RSTRING_PTR(v_return); + + v_proc = rb_iv_get(callback, "@function"); + argc = RSTRING_LEN(v_proto); + + for(i=0; i < RSTRING_LEN(v_proto); i++){ + argv[i] = Qnil; + switch(a_proto[i]){ + case 'L': + argv[i] = ULONG2NUM(param.params[i]); + break; + case 'P': + if(param.params[i]) + argv[i] = rb_str_new2((char *)param.params[i]); + break; + case 'I': + argv[i] = INT2NUM(param.params[i]); + break; + default: + rb_raise(cAPIProtoError, "Illegal prototype '%s'", + RSTRING_PTR(a_proto[i]) + ); + } + } + + v_retval = rb_funcall2(v_proc, rb_intern("call"), argc, argv); + + /* Handle true and false explicitly, as some CALLBACK functions + * require TRUE or FALSE to break out of loops, etc. + */ + if(v_retval == Qtrue) + return TRUE; + else if(v_retval == Qfalse) + return FALSE; + + switch (*a_return) { + case 'I': + return NUM2INT(v_retval); + break; + case 'L': + return NUM2ULONG(v_retval); + break; + case 'S': + return (unsigned long)RSTRING_PTR(v_retval); + break; + case 'P': + if(NIL_P(v_retval)){ + return 0; + } + else if(FIXNUM_P(v_retval)){ + return NUM2ULONG(v_retval); + } + else{ + StringValue(v_retval); + rb_str_modify(v_retval); + return (unsigned long)StringValuePtr(v_retval); + } + break; + } + } + + return 0; +} + +#define CALLBACK0(x) DWORD CALLBACK CallbackFunction0_##x() {\ + CALLPARAM param = {0};\ + param.params[0] = 0;\ + return CallbackFunction(param,FuncTable[0][x]);\ +} + +#define CALLBACK1(x) DWORD CALLBACK CallbackFunction1_##x(DWORD p1) {\ + CALLPARAM param = {p1};\ + return CallbackFunction(param,FuncTable[1][x]);\ +} + +#define CALLBACK2(x) DWORD CALLBACK CallbackFunction2_##x(\ +DWORD p1, DWORD p2){\ + CALLPARAM param = {p1,p2};\ + return CallbackFunction(param,FuncTable[2][x]);\ +} + +#define CALLBACK3(x) DWORD CALLBACK CallbackFunction3_##x(\ +DWORD p1, DWORD p2, DWORD p3){\ + CALLPARAM param = {p1,p2,p3};\ + return CallbackFunction(param,FuncTable[3][x]);\ +} + +#define CALLBACK4(x) DWORD CALLBACK CallbackFunction4_##x(\ +DWORD p1, DWORD p2, DWORD p3, DWORD p4){\ + CALLPARAM param = {p1,p2,p3,p4};\ + return CallbackFunction(param,FuncTable[4][x]);\ +} + +#define CALLBACK5(x) DWORD CALLBACK CallbackFunction5_##x(\ + DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5\ +){\ + CALLPARAM param = {p1,p2,p3,p4,p5};\ + return CallbackFunction(param,FuncTable[5][x]);\ +} + +#define CALLBACK6(x) DWORD CALLBACK CallbackFunction6_##x(\ + DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5, DWORD p6\ +){\ + CALLPARAM param = {p1,p2,p3,p4,p5,p6};\ + return CallbackFunction(param,FuncTable[6][x]);\ +} + +#define CALLBACK7(x) DWORD CALLBACK CallbackFunction7_##x(\ + DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5, DWORD p6, DWORD p7\ +){\ + CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7};\ + return CallbackFunction(param,FuncTable[7][x]);\ +} + +#define CALLBACK8(x) DWORD CALLBACK CallbackFunction8_##x(\ + DWORD p1, DWORD p2, DWORD p3, DWORD p4,\ + DWORD p5, DWORD p6, DWORD p7, DWORD p8\ +){\ + CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7,p8};\ + return CallbackFunction(param,FuncTable[8][x]);\ +} + +#define CALLBACK9(x) DWORD CALLBACK CallbackFunction9_##x(\ + DWORD p1, DWORD p2, DWORD p3, DWORD p4, DWORD p5,\ + DWORD p6, DWORD p7, DWORD p8, DWORD p9\ +){\ + CALLPARAM param = {p1,p2,p3,p4,p5,p6,p7,p8,p9};\ + return CallbackFunction(param,FuncTable[9][x]);\ +} + +#define DEFCALLBACK(x) CALLBACK##x(0)\ +CALLBACK##x(1)\ +CALLBACK##x(2)\ +CALLBACK##x(3)\ +CALLBACK##x(4)\ +CALLBACK##x(5)\ +CALLBACK##x(6)\ +CALLBACK##x(7)\ +CALLBACK##x(8)\ +CALLBACK##x(9) + +#define CF(x,y) CallbackFunction##x##_##y + +static VALUE FuncTable[10][10]; + +DEFCALLBACK(0) +DEFCALLBACK(1) +DEFCALLBACK(2) +DEFCALLBACK(3) +DEFCALLBACK(4) +DEFCALLBACK(5) +DEFCALLBACK(6) +DEFCALLBACK(7) +DEFCALLBACK(8) +DEFCALLBACK(9) + +void *CallbackTable[10][10] = { +{CF(0,0),CF(0,1),CF(0,2),CF(0,3),CF(0,4),CF(0,5),CF(0,6),CF(0,7),CF(0,8),CF(0,9)}, +{CF(1,0),CF(1,1),CF(1,2),CF(1,3),CF(1,4),CF(1,5),CF(1,6),CF(1,7),CF(1,8),CF(1,9)}, +{CF(2,0),CF(2,1),CF(2,2),CF(2,3),CF(2,4),CF(2,5),CF(2,6),CF(2,7),CF(2,8),CF(2,9)}, +{CF(3,0),CF(3,1),CF(3,2),CF(3,3),CF(3,4),CF(3,5),CF(3,6),CF(3,7),CF(3,8),CF(3,9)}, +{CF(4,0),CF(4,1),CF(4,2),CF(4,3),CF(4,4),CF(4,5),CF(4,6),CF(4,7),CF(4,8),CF(4,9)}, +{CF(5,0),CF(5,1),CF(5,2),CF(5,3),CF(5,4),CF(5,5),CF(5,6),CF(5,7),CF(5,8),CF(5,9)}, +{CF(6,0),CF(6,1),CF(6,2),CF(6,3),CF(6,4),CF(6,5),CF(6,6),CF(6,7),CF(6,8),CF(6,9)}, +{CF(7,0),CF(7,1),CF(7,2),CF(7,3),CF(7,4),CF(7,5),CF(7,6),CF(7,7),CF(7,8),CF(7,9)}, +{CF(8,0),CF(8,1),CF(8,2),CF(8,3),CF(8,4),CF(8,5),CF(8,6),CF(8,7),CF(8,8),CF(8,9)}, +{CF(9,0),CF(9,1),CF(9,2),CF(9,3),CF(9,4),CF(9,5),CF(9,6),CF(9,7),CF(9,8),CF(9,9)}}; + + +void *find_callback(VALUE obj,int len) +{ + int i; + for(i=0;i<10;i++) + { + if(FuncTable[len][i]==0) + break; + } + if(i>=10) + rb_raise(cAPIError,"too many callbacks are defined."); + FuncTable[len][i] = obj; + return CallbackTable[len][i]; +} + +/* + * call-seq: + * Win32::API#call(arg1, arg2, ...) + * + * Calls the function pointer with the given arguments (if any). Note that, + * while this method will catch some prototype mismatches (raising a TypeError + * in the process), it is not fulproof. It is ultimately your job to make + * sure the arguments match the +prototype+ specified in the constructor. + * + * For convenience, nil is converted to NULL, true is converted to TRUE (1) + * and false is converted to FALSE (0). + */ +static VALUE api_call(int argc, VALUE* argv, VALUE self){ + VALUE v_proto, v_args, v_arg, v_return; + Win32API* ptr; + unsigned long return_value; + int i = 0; + int len; + + struct{ + unsigned long params[20]; + } param; + + Data_Get_Struct(self, Win32API, ptr); + + rb_scan_args(argc, argv, "0*", &v_args); + + v_proto = rb_iv_get(self, "@prototype"); + + // For void prototypes, allow either no args or an explicit nil + if(RARRAY_LEN(v_proto) != RARRAY_LEN(v_args)){ + char* c = StringValuePtr(RARRAY_PTR(v_proto)[0]); + if(!strcmp(c, "V")){ + rb_ary_push(v_args, Qnil); + } + else{ + rb_raise(rb_eArgError, + "wrong number of parameters: expected %d, got %d", + RARRAY_LEN(v_proto), RARRAY_LEN(v_args) + ); + } + } + + len = RARRAY_LEN(v_proto); + + for(i = 0; i < len; i++){ + v_arg = RARRAY_PTR(v_args)[i]; + + // Convert nil to NULL. Otherwise convert as appropriate. + if(NIL_P(v_arg)) + param.params[i] = (unsigned long)NULL; + else if(v_arg == Qtrue) + param.params[i] = TRUE; + else if(v_arg == Qfalse) + param.params[i] = FALSE; + else + switch(ptr->prototype[i]){ + case _T_LONG: + param.params[i] = NUM2ULONG(v_arg); + break; + case _T_INTEGER: + param.params[i] = NUM2INT(v_arg); + break; + case _T_POINTER: + if(FIXNUM_P(v_arg)){ + param.params[i] = NUM2ULONG(v_arg); + } + else{ + StringValue(v_arg); + rb_str_modify(v_arg); + param.params[i] = (unsigned long)StringValuePtr(v_arg); + } + break; + case _T_CALLBACK: + ActiveCallback = v_arg; + v_proto = rb_iv_get(ActiveCallback, "@prototype"); + param.params[i] = (LPARAM)NUM2ULONG(rb_iv_get(ActiveCallback, "@address"));; + break; + case _T_STRING: + param.params[i] = (unsigned long)RSTRING_PTR(v_arg); + break; + default: + param.params[i] = NUM2ULONG(v_arg); + } + } + + /* Call the function, get the return value */ + return_value = ptr->function(param); + + + /* Return the appropriate type based on the return type specified + * in the constructor. + */ + switch(ptr->return_type){ + case _T_INTEGER: + v_return = INT2NUM(return_value); + break; + case _T_LONG: + v_return = ULONG2NUM(return_value); + break; + case _T_VOID: + v_return = Qnil; + break; + case _T_POINTER: + if(!return_value){ + v_return = Qnil; + } + else{ + VALUE v_efunc = rb_iv_get(self, "@effective_function_name"); + char* efunc = RSTRING_PTR(v_efunc); + if(efunc[strlen(efunc)-1] == 'W'){ + v_return = rb_str_new( + (TCHAR*)return_value, + wcslen((wchar_t*)return_value)*2 + ); + } + else{ + v_return = rb_str_new2((TCHAR*)return_value); + } + } + break; + case _T_STRING: + { + VALUE v_efunc = rb_iv_get(self, "@effective_function_name"); + char* efunc = RSTRING_PTR(v_efunc); + + if(efunc[strlen(efunc)-1] == 'W'){ + v_return = rb_str_new( + (TCHAR*)return_value, + wcslen((wchar_t*)return_value)*2 + ); + } + else{ + v_return = rb_str_new2((TCHAR*)return_value); + } + } + break; + default: + v_return = INT2NUM(0); + } + + return v_return; +} + +/* + * Wraps the Windows API functions in a Ruby interface. + */ +void Init_api(){ + VALUE mWin32, cAPI, cCallback, cFunction; + + /* Modules and Classes */ + + /* The Win32 module serves as a namespace only */ + mWin32 = rb_define_module("Win32"); + + /* The API class encapsulates a function pointer to Windows API function */ + cAPI = rb_define_class_under(mWin32, "API", rb_cObject); + + /* The API::Callback class encapsulates a Windows CALLBACK function */ + cCallback = rb_define_class_under(cAPI, "Callback", rb_cObject); + + /* The API::Function class encapsulates a raw function pointer */ + cFunction = rb_define_class_under(cAPI, "Function", cAPI); + + /* The API::Error class serves as a base class for other errors */ + cAPIError = rb_define_class_under(cAPI, "Error", rb_eRuntimeError); + + /* The LoadError class is raised if a function cannot be found or loaded */ + cAPILoadError = rb_define_class_under(cAPI, "LoadLibraryError", cAPIError); + + /* The PrototypeError class is raised if an invalid prototype is passed */ + cAPIProtoError = rb_define_class_under(cAPI, "PrototypeError", cAPIError); + + /* Miscellaneous */ + rb_define_alloc_func(cAPI, api_allocate); + + /* Win32::API Instance Methods */ + rb_define_method(cAPI, "initialize", api_init, -1); + rb_define_method(cAPI, "call", api_call, -1); + + /* Win32::API::Callback Instance Methods */ + rb_define_method(cCallback, "initialize", callback_init, -1); + + /* Win32::API::Function Instance Methods */ + rb_define_method(cFunction, "initialize", func_init, -1); + + /* The name of the DLL that exports the API function */ + rb_define_attr(cAPI, "dll_name", 1, 0); + + /* The name of the function passed to the constructor */ + rb_define_attr(cAPI, "function_name", 1, 0); + + /* The name of the actual function that is returned by the constructor. + * For example, if you passed 'GetUserName' to the constructor, then the + * effective function name would be either 'GetUserNameA' or 'GetUserNameW'. + */ + rb_define_attr(cAPI, "effective_function_name", 1, 0); + + /* The prototype, returned as an array of characters */ + rb_define_attr(cAPI, "prototype", 1, 0); + + /* The return type, returned as a single character, S, P, L, I, V or B */ + rb_define_attr(cAPI, "return_type", 1, 0); + + /* Win32::API::Callback Instance Methods */ + + /* The prototype, returned as an array of characters */ + rb_define_attr(cCallback, "prototype", 1, 0); + + /* The return type, returned as a single character, S, P, L, I, V or B */ + rb_define_attr(cCallback, "return_type", 1, 0); + + /* The numeric address of the function pointer */ + rb_define_attr(cCallback, "address", 1, 0); + rb_define_attr(cFunction, "address", 1, 0); + + /* Constants */ + + /* 1.4.5: The version of the win32-api library */ + rb_define_const(cAPI, "VERSION", rb_str_new2(WINDOWS_API_VERSION)); +} =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/test/test_win32_api.rb File: test_win32_api.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/test/test_win32_api.rb;tzinfo2 @@ -1,0 +1,140 @@ +############################################################################ +# test_win32_api.rb +# +# Test case for the Win32::API class. You should run this as Rake task, +# i.e. 'rake test', instead of running it directly. +############################################################################ +require 'rubygems' +gem 'test-unit' + +require 'win32/api' +require 'test/unit' +include Win32 + +class TC_Win32_API < Test::Unit::TestCase + def setup + @buf = 0.chr * 260 + @gfa = API.new('GetFileAttributes', 'S', 'L') + @gcd = API.new('GetCurrentDirectory', 'LP') + @gle = API.new('GetLastError', 'V', 'L') + @str = API.new('strstr', 'PP', 'P', 'msvcrt') + end + + def test_version + assert_equal('1.4.5', API::VERSION) + end + + def test_constructor_basic + assert_nothing_raised{ API.new('GetCurrentDirectory') } + assert_nothing_raised{ API.new('GetCurrentDirectory', 'LP') } + assert_nothing_raised{ API.new('GetCurrentDirectory', 'LP', 'L') } + assert_nothing_raised{ API.new('GetCurrentDirectory', 'LP', 'L', 'kernel32') } + end + + def test_call + assert_respond_to(@gcd, :call) + assert_nothing_raised{ @gcd.call(@buf.length, @buf) } + assert_equal(Dir.pwd.tr('/', "\\"), @buf.strip) + end + + def test_call_with_void + assert_nothing_raised{ @gle.call } + assert_nothing_raised{ @gle.call(nil) } + end + + def test_call_return_value_on_failure + assert_equal(0xFFFFFFFF, @gfa.call('C:/foobarbazblah')) + end + + def test_dll_name + assert_respond_to(@gcd, :dll_name) + assert_equal('kernel32', @gcd.dll_name) + end + + def test_function_name + assert_respond_to(@gcd, :function_name) + assert_equal('GetCurrentDirectory', @gcd.function_name) + assert_equal('strstr', @str.function_name) + end + + def test_effective_function_name_default + assert_respond_to(@gcd, :effective_function_name) + assert_equal('GetCurrentDirectoryA', @gcd.effective_function_name) + assert_equal('strstr', @str.effective_function_name) + end + + def test_effective_function_name_default_explicit_ansi + @gcd = API.new('GetCurrentDirectoryA', 'LP') + assert_equal('GetCurrentDirectoryA', @gcd.effective_function_name) + end + + def test_effective_function_name_default_explicit_wide + @gcd = API.new('GetCurrentDirectoryW', 'LP') + assert_equal('GetCurrentDirectoryW', @gcd.effective_function_name) + end + + def test_prototype + assert_respond_to(@gcd, :prototype) + assert_equal(['L', 'P'], @gcd.prototype) + end + + def test_return_type + assert_respond_to(@gcd, :return_type) + assert_equal('L', @gcd.return_type) + end + + def test_constructor_high_iteration + assert_nothing_raised{ + 1000.times{ API.new('GetUserName', 'P', 'P', 'advapi32') } + } + end + + def test_constructor_expected_failures + assert_raise(ArgumentError){ API.new } + assert_raise(ArgumentError){ API.new('GetUserName', ('L' * 21), 'X') } + assert_raise(API::LoadLibraryError){ API.new('GetUserName', 'PL', 'I', 'foo') } + assert_raise(API::PrototypeError){ API.new('GetUserName', 'X', 'I', 'advapi32') } + assert_raise(API::PrototypeError){ API.new('GetUserName', 'PL', 'X', 'advapi32') } + end + + def test_constructor_expected_failure_messages + assert_raise_message("Unable to load function 'Zap', 'ZapA', or 'ZapW'"){ + API.new('Zap') + } + + assert_raise_message("Unable to load function 'strxxx'"){ + API.new('strxxx', 'P', 'L', 'msvcrt') + } + + assert_raise_message("Illegal prototype 'X'"){ + API.new('GetUserName', 'X', 'I', 'advapi32') + } + + assert_raise_message("Illegal return type 'Y'"){ + API.new('GetUserName', 'PL', 'Y', 'advapi32') + } + end + + def test_call_expected_failures + assert_raise(TypeError){ @gcd.call('test', @buf) } + end + + def test_error_classes + assert_not_nil(Win32::API::Error) + assert_not_nil(Win32::API::LoadLibraryError) + assert_not_nil(Win32::API::PrototypeError) + end + + def test_error_class_relationships + assert_kind_of(RuntimeError, Win32::API::Error.new) + assert_kind_of(Win32::API::Error, Win32::API::LoadLibraryError.new) + assert_kind_of(Win32::API::Error, Win32::API::PrototypeError.new) + end + + def teardown + @buf = nil + @gcd = nil + @gle = nil + @str = nil + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/test/test_win32_api_callback.rb File: test_win32_api_callback.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/test/test_win32_api_callback.rb;tzinfo2 @@ -1,0 +1,74 @@ +############################################################################ +# test_win32_api_callback.rb +# +# Test case for the Win32::API::Callback class. You should run this as Rake +# task, i.e. 'rake test', instead of running it directly. +############################################################################ +require 'rubygems' +gem 'test-unit' + +require 'win32/api' +require 'test/unit' +include Win32 + +class TC_Win32_API_Callback < Test::Unit::TestCase + def setup + @buffer = 0.chr * 260 + @api_ew = API.new('EnumWindows', 'KP', 'L', 'user32') + @api_gwt = API.new('GetWindowText', 'LPI', 'I', 'user32') + @callback = nil + end + + def test_constructor + assert_respond_to(API::Callback, :new) + assert_nothing_raised{ API::Callback.new('LP', 'I') } + assert_nothing_raised{ API::Callback.new('LP', 'I'){} } + end + + def test_prototype + assert_nothing_raised{ @callback = API::Callback.new('LP', 'I') } + assert_respond_to(@callback, :prototype) + assert_equal('LP', @callback.prototype) + end + + def test_return_value + assert_nothing_raised{ @callback = API::Callback.new('LP', 'I') } + assert_respond_to(@callback, :return_type) + assert_equal('I', @callback.return_type) + end + + def test_address + assert_nothing_raised{ @callback = API::Callback.new('LP', 'I') } + assert_respond_to(@callback, :address) + assert_kind_of(Fixnum, @callback.address) + assert_equal(true, @callback.address > 0) + end + + def test_callback + assert_nothing_raised{ + @callback = API::Callback.new('LP', 'I'){ |handle, param| + buf = "\0" * 200 + @api_gwt.call(handle, buf, 200); + buf.index(param).nil? ? true : false + } + } + assert_nothing_raised{ @api_ew.call(@callback, 'UEDIT32') } + end + + def test_constructor_expected_errors + assert_raise(API::PrototypeError){ API::Callback.new('X') } + assert_raise(API::PrototypeError){ API::Callback.new('L', 'Y') } + end + + def test_constructor_expected_error_messages + assert_raise_message("Illegal prototype 'X'"){ API::Callback.new('X') } + assert_raise_message("Illegal return type 'Y'"){ API::Callback.new('L', 'Y') } + end + + def teardown + @buffer = nil + @api_ew = nil + @api_gwt = nil + @callback = nil + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/test/test_win32_api_function.rb File: test_win32_api_function.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/gems/win32-api-1.4.5/test/test_win32_api_function.rb;tzinfo2 @@ -1,0 +1,66 @@ +######################################################################## +# test_win32_api_function.rb +# +# Test case for the Win32::API::Function class. You should run these +# tests via the 'rake test' task. +######################################################################## +require 'rubygems' +gem 'test-unit' + +require 'test/unit' +require 'win32/api' +include Win32 + +class TC_Win32_API_Function < Test::Unit::TestCase + def setup + @func = Win32::API::Function.new(123456789, 'LP', 'L') + end + + def test_constructor + assert_nothing_raised{ Win32::API::Function.new(1) } + assert_nothing_raised{ Win32::API::Function.new(1, 'LL') } + assert_nothing_raised{ Win32::API::Function.new(1, 'LL', 'I') } + end + + def test_subclass + assert_kind_of(Win32::API, @func) + assert_respond_to(@func, :call) + end + + def test_address + assert_respond_to(@func, :address) + assert_equal(123456789, @func.address) + end + + def test_prototype + assert_respond_to(@func, :prototype) + assert_equal(['L', 'P'], @func.prototype) + end + + def test_return_type + assert_respond_to(@func, :return_type) + assert_equal('L', @func.return_type) + end + + def test_expected_errors_from_arguments + assert_raise(ArgumentError){ Win32::API::Function.new } + assert_raise(TypeError){ Win32::API::Function.new('L') } + assert_raise(Win32::API::PrototypeError){ Win32::API::Function.new(1, 'X') } + assert_raise(Win32::API::PrototypeError){ Win32::API::Function.new(1, 'X', 'Y') } + end + + def test_expected_error_messages + assert_raise_message("Illegal prototype 'X'"){ API::Function.new(1, 'X') } + assert_raise_message("Illegal return type 'Y'"){ API::Function.new(1, 'L', 'Y') } + end + + def test_expected_errors_from_call + assert_raise(ArgumentError){ @func.call } + assert_raise(ArgumentError){ @func.call(1) } + assert_raise(ArgumentError){ @func.call(1, 'a', 2) } + end + + def teardown + @func = nil + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/specifications/net-scp-1.0.2.gemspec File: net-scp-1.0.2.gemspec =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/specifications/net-scp-1.0.2.gemspec;tzinfo2 @@ -1,0 +1,37 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{net-scp} + s.version = "1.0.2" + + s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version= + s.authors = ["Jamis Buck"] + s.date = %q{2009-02-03} + s.description = %q{A pure Ruby implementation of the SCP client protocol} + s.email = %q{jamis@jamisbuck.org} + s.extra_rdoc_files = ["CHANGELOG.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", "README.rdoc"] + s.files = ["CHANGELOG.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.gemspec"] + s.homepage = %q{http://net-ssh.rubyforge.org/scp} + s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Net-scp", "--main", "README.rdoc"] + s.require_paths = ["lib"] + s.rubyforge_project = %q{net-ssh} + s.rubygems_version = %q{1.3.5} + s.summary = %q{A pure Ruby implementation of the SCP client protocol} + s.test_files = ["test/test_all.rb"] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 2 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, [">= 1.99.1"]) + s.add_development_dependency(%q, [">= 0"]) + else + s.add_dependency(%q, [">= 1.99.1"]) + s.add_dependency(%q, [">= 0"]) + end + else + s.add_dependency(%q, [">= 1.99.1"]) + s.add_dependency(%q, [">= 0"]) + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/specifications/net-ssh-2.0.13.gemspec File: net-ssh-2.0.13.gemspec =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/specifications/net-ssh-2.0.13.gemspec;tzinfo2 @@ -1,0 +1,30 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{net-ssh} + s.version = "2.0.13" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Jamis Buck", "Delano Mandelbaum"] + s.date = %q{2009-08-16} + s.description = %q{Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.} + s.email = ["net-ssh@solutious.com", "net-ssh@solutious.com"] + s.extra_rdoc_files = ["README.rdoc", "THANKS.rdoc", "CHANGELOG.rdoc"] + s.files = ["CHANGELOG.rdoc", "Manifest", "README.rdoc", "Rakefile", "Rudyfile", "THANKS.rdoc", "lib/net/ssh.rb", "lib/net/ssh/authentication/agent.rb", "lib/net/ssh/authentication/constants.rb", "lib/net/ssh/authentication/key_manager.rb", "lib/net/ssh/authentication/methods/abstract.rb", "lib/net/ssh/authentication/methods/hostbased.rb", "lib/net/ssh/authentication/methods/keyboard_interactive.rb", "lib/net/ssh/authentication/methods/password.rb", "lib/net/ssh/authentication/methods/publickey.rb", "lib/net/ssh/authentication/pageant.rb", "lib/net/ssh/authentication/session.rb", "lib/net/ssh/buffer.rb", "lib/net/ssh/buffered_io.rb", "lib/net/ssh/config.rb", "lib/net/ssh/connection/channel.rb", "lib/net/ssh/connection/constants.rb", "lib/net/ssh/connection/session.rb", "lib/net/ssh/connection/term.rb", "lib/net/ssh/errors.rb", "lib/net/ssh/key_factory.rb", "lib/net/ssh/known_hosts.rb", "lib/net/ssh/loggable.rb", "lib/net/ssh/packet.rb", "lib/net/ssh/prompt.rb", "lib/net/ssh/proxy/errors.rb", "lib/net/ssh/proxy/http.rb", "lib/net/ssh/proxy/socks4.rb", "lib/net/ssh/proxy/socks5.rb", "lib/net/ssh/ruby_compat.rb", "lib/net/ssh/service/forward.rb", "lib/net/ssh/test.rb", "lib/net/ssh/test/channel.rb", "lib/net/ssh/test/extensions.rb", "lib/net/ssh/test/kex.rb", "lib/net/ssh/test/local_packet.rb", "lib/net/ssh/test/packet.rb", "lib/net/ssh/test/remote_packet.rb", "lib/net/ssh/test/script.rb", "lib/net/ssh/test/socket.rb", "lib/net/ssh/transport/algorithms.rb", "lib/net/ssh/transport/cipher_factory.rb", "lib/net/ssh/transport/constants.rb", "lib/net/ssh/transport/hmac.rb", "lib/net/ssh/transport/hmac/abstract.rb", "lib/net/ssh/transport/hmac/md5.rb", "lib/net/ssh/transport/hmac/md5_96.rb", "lib/net/ssh/transport/hmac/none.rb", "lib/net/ssh/transport/hmac/sha1.rb", "lib/net/ssh/transport/hmac/sha1_96.rb", "lib/net/ssh/transport/identity_cipher.rb", "lib/net/ssh/transport/kex.rb", "lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb", "lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb", "lib/net/ssh/transport/openssl.rb", "lib/net/ssh/transport/packet_stream.rb", "lib/net/ssh/transport/server_version.rb", "lib/net/ssh/transport/session.rb", "lib/net/ssh/transport/state.rb", "lib/net/ssh/verifiers/lenient.rb", "lib/net/ssh/verifiers/null.rb", "lib/net/ssh/verifiers/strict.rb", "lib/net/ssh/version.rb", "net-ssh.gemspec", "setup.rb", "support/arcfour_check.rb", "test/authentication/methods/common.rb", "test/authentication/methods/test_abstract.rb", "test/authentication/methods/test_hostbased.rb", "test/authentication/methods/test_keyboard_interactive.rb", "test/authentication/methods/test_password.rb", "test/authentication/methods/test_publickey.rb", "test/authentication/test_agent.rb", "test/authentication/test_key_manager.rb", "test/authentication/test_session.rb", "test/common.rb", "test/configs/eqsign", "test/configs/exact_match", "test/configs/multihost", "test/configs/wild_cards", "test/connection/test_channel.rb", "test/connection/test_session.rb", "test/test_all.rb", "test/test_buffer.rb", "test/test_buffered_io.rb", "test/test_config.rb", "test/test_key_factory.rb", "test/transport/hmac/test_md5.rb", "test/transport/hmac/test_md5_96.rb", "test/transport/hmac/test_none.rb", "test/transport/hmac/test_sha1.rb", "test/transport/hmac/test_sha1_96.rb", "test/transport/kex/test_diffie_hellman_group1_sha1.rb", "test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb", "test/transport/test_algorithms.rb", "test/transport/test_cipher_factory.rb", "test/transport/test_hmac.rb", "test/transport/test_identity_cipher.rb", "test/transport/test_packet_stream.rb", "test/transport/test_server_version.rb", "test/transport/test_session.rb", "test/transport/test_state.rb"] + s.homepage = %q{http://rubyforge.org/projects/net-ssh/} + s.rdoc_options = ["--line-numbers", "--title", "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.", "--main", "README.rdoc"] + s.require_paths = ["lib"] + s.rubyforge_project = %q{net-ssh} + s.rubygems_version = %q{1.3.5} + s.summary = %q{Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.} + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 3 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + else + end + else + end +end =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/specifications/tzinfo-0.3.15.gemspec File: tzinfo-0.3.15.gemspec =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/Ruby/ruby-1.8.6p368/lib/ruby/gems/1.8/specifications/tzinfo-0.3.15.gemspec;tzinfo2 @@ -1,0 +1,31 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{tzinfo} + s.version = "0.3.15" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Philip Ross"] + s.date = %q{2009-10-25} + s.description = %q{TZInfo is a Ruby library that uses the standard tz (Olson) database to provide daylight savings aware transformations between times in different time zones.} + s.email = %q{phil.ross@gmail.com} + s.extra_rdoc_files = ["README", "CHANGES"] + s.files = ["CHANGES", "LICENSE", "Rakefile", "README", "lib/tzinfo/country.rb", "lib/tzinfo/country_index_definition.rb", "lib/tzinfo/country_info.rb", "lib/tzinfo/country_timezone.rb", "lib/tzinfo/data_timezone.rb", "lib/tzinfo/data_timezone_info.rb", "lib/tzinfo/definitions/Africa/Abidjan.rb", "lib/tzinfo/definitions/Africa/Accra.rb", "lib/tzinfo/definitions/Africa/Addis_Ababa.rb", "lib/tzinfo/definitions/Africa/Algiers.rb", "lib/tzinfo/definitions/Africa/Asmara.rb", "lib/tzinfo/definitions/Africa/Asmera.rb", "lib/tzinfo/definitions/Africa/Bamako.rb", "lib/tzinfo/definitions/Africa/Bangui.rb", "lib/tzinfo/definitions/Africa/Banjul.rb", "lib/tzinfo/definitions/Africa/Bissau.rb", "lib/tzinfo/definitions/Africa/Blantyre.rb", "lib/tzinfo/definitions/Africa/Brazzaville.rb", "lib/tzinfo/definitions/Africa/Bujumbura.rb", "lib/tzinfo/definitions/Africa/Cairo.rb", "lib/tzinfo/definitions/Africa/Casablanca.rb", "lib/tzinfo/definitions/Africa/Ceuta.rb", "lib/tzinfo/definitions/Africa/Conakry.rb", "lib/tzinfo/definitions/Africa/Dakar.rb", "lib/tzinfo/definitions/Africa/Dar_es_Salaam.rb", "lib/tzinfo/definitions/Africa/Djibouti.rb", "lib/tzinfo/definitions/Africa/Douala.rb", "lib/tzinfo/definitions/Africa/El_Aaiun.rb", "lib/tzinfo/definitions/Africa/Freetown.rb", "lib/tzinfo/definitions/Africa/Gaborone.rb", "lib/tzinfo/definitions/Africa/Harare.rb", "lib/tzinfo/definitions/Africa/Johannesburg.rb", "lib/tzinfo/definitions/Africa/Kampala.rb", "lib/tzinfo/definitions/Africa/Khartoum.rb", "lib/tzinfo/definitions/Africa/Kigali.rb", "lib/tzinfo/definitions/Africa/Kinshasa.rb", "lib/tzinfo/definitions/Africa/Lagos.rb", "lib/tzinfo/definitions/Africa/Libreville.rb", "lib/tzinfo/definitions/Africa/Lome.rb", "lib/tzinfo/definitions/Africa/Luanda.rb", "lib/tzinfo/definitions/Africa/Lubumbashi.rb", "lib/tzinfo/definitions/Africa/Lusaka.rb", "lib/tzinfo/definitions/Africa/Malabo.rb", "lib/tzinfo/definitions/Africa/Maputo.rb", "lib/tzinfo/definitions/Africa/Maseru.rb", "lib/tzinfo/definitions/Africa/Mbabane.rb", "lib/tzinfo/definitions/Africa/Mogadishu.rb", "lib/tzinfo/definitions/Africa/Monrovia.rb", "lib/tzinfo/definitions/Africa/Nairobi.rb", "lib/tzinfo/definitions/Africa/Ndjamena.rb", "lib/tzinfo/definitions/Africa/Niamey.rb", "lib/tzinfo/definitions/Africa/Nouakchott.rb", "lib/tzinfo/definitions/Africa/Ouagadougou.rb", "lib/tzinfo/definitions/Africa/Porto__m__Novo.rb", "lib/tzinfo/definitions/Africa/Sao_Tome.rb", "lib/tzinfo/definitions/Africa/Timbuktu.rb", "lib/tzinfo/definitions/Africa/Tripoli.rb", "lib/tzinfo/definitions/Africa/Tunis.rb", "lib/tzinfo/definitions/Africa/Windhoek.rb", "lib/tzinfo/definitions/America/Adak.rb", "lib/tzinfo/definitions/America/Anchorage.rb", "lib/tzinfo/definitions/America/Anguilla.rb", "lib/tzinfo/definitions/America/Antigua.rb", "lib/tzinfo/definitions/America/Araguaina.rb", "lib/tzinfo/definitions/America/Argentina/Buenos_Aires.rb", "lib/tzinfo/definitions/America/Argentina/Catamarca.rb", "lib/tzinfo/definitions/America/Argentina/ComodRivadavia.rb", "lib/tzinfo/definitions/America/Argentina/Cordoba.rb", "lib/tzinfo/definitions/America/Argentina/Jujuy.rb", "lib/tzinfo/definitions/America/Argentina/La_Rioja.rb", "lib/tzinfo/definitions/America/Argentina/Mendoza.rb", "lib/tzinfo/definitions/America/Argentina/Rio_Gallegos.rb", "lib/tzinfo/definitions/America/Argentina/Salta.rb", "lib/tzinfo/definitions/America/Argentina/San_Juan.rb", "lib/tzinfo/definitions/America/Argentina/San_Luis.rb", "lib/tzinfo/definitions/America/Argentina/Tucuman.rb", "lib/tzinfo/definitions/America/Argentina/Ushuaia.rb", "lib/tzinfo/definitions/America/Aruba.rb", "lib/tzinfo/definitions/America/Asuncion.rb", "lib/tzinfo/definitions/America/Atikokan.rb", "lib/tzinfo/definitions/America/Atka.rb", "lib/tzinfo/definitions/America/Bahia.rb", "lib/tzinfo/definitions/America/Barbados.rb", "lib/tzinfo/definitions/America/Belem.rb", "lib/tzinfo/definitions/America/Belize.rb", "lib/tzinfo/definitions/America/Blanc__m__Sablon.rb", "lib/tzinfo/definitions/America/Boa_Vista.rb", "lib/tzinfo/definitions/America/Bogota.rb", "lib/tzinfo/definitions/America/Boise.rb", "lib/tzinfo/definitions/America/Buenos_Aires.rb", "lib/tzinfo/definitions/America/Cambridge_Bay.rb", "lib/tzinfo/definitions/America/Campo_Grande.rb", "lib/tzinfo/definitions/America/Cancun.rb", "lib/tzinfo/definitions/America/Caracas.rb", "lib/tzinfo/definitions/America/Catamarca.rb", "lib/tzinfo/definitions/America/Cayenne.rb", "lib/tzinfo/definitions/America/Cayman.rb", "lib/tzinfo/definitions/America/Chicago.rb", "lib/tzinfo/definitions/America/Chihuahua.rb", "lib/tzinfo/definitions/America/Coral_Harbour.rb", "lib/tzinfo/definitions/America/Cordoba.rb", "lib/tzinfo/definitions/America/Costa_Rica.rb", "lib/tzinfo/definitions/America/Cuiaba.rb", "lib/tzinfo/definitions/America/Curacao.rb", "lib/tzinfo/definitions/America/Danmarkshavn.rb", "lib/tzinfo/definitions/America/Dawson.rb", "lib/tzinfo/definitions/America/Dawson_Creek.rb", "lib/tzinfo/definitions/America/Denver.rb", "lib/tzinfo/definitions/America/Detroit.rb", "lib/tzinfo/definitions/America/Dominica.rb", "lib/tzinfo/definitions/America/Edmonton.rb", "lib/tzinfo/definitions/America/Eirunepe.rb", "lib/tzinfo/definitions/America/El_Salvador.rb", "lib/tzinfo/definitions/America/Ensenada.rb", "lib/tzinfo/definitions/America/Fortaleza.rb", "lib/tzinfo/definitions/America/Fort_Wayne.rb", "lib/tzinfo/definitions/America/Glace_Bay.rb", "lib/tzinfo/definitions/America/Godthab.rb", "lib/tzinfo/definitions/America/Goose_Bay.rb", "lib/tzinfo/definitions/America/Grand_Turk.rb", "lib/tzinfo/definitions/America/Grenada.rb", "lib/tzinfo/definitions/America/Guadeloupe.rb", "lib/tzinfo/definitions/America/Guatemala.rb", "lib/tzinfo/definitions/America/Guayaquil.rb", "lib/tzinfo/definitions/America/Guyana.rb", "lib/tzinfo/definitions/America/Halifax.rb", "lib/tzinfo/definitions/America/Havana.rb", "lib/tzinfo/definitions/America/Hermosillo.rb", "lib/tzinfo/definitions/America/Indiana/Indianapolis.rb", "lib/tzinfo/definitions/America/Indiana/Knox.rb", "lib/tzinfo/definitions/America/Indiana/Marengo.rb", "lib/tzinfo/definitions/America/Indiana/Petersburg.rb", "lib/tzinfo/definitions/America/Indiana/Tell_City.rb", "lib/tzinfo/definitions/America/Indiana/Vevay.rb", "lib/tzinfo/definitions/America/Indiana/Vincennes.rb", "lib/tzinfo/definitions/America/Indiana/Winamac.rb", "lib/tzinfo/definitions/America/Indianapolis.rb", "lib/tzinfo/definitions/America/Inuvik.rb", "lib/tzinfo/definitions/America/Iqaluit.rb", "lib/tzinfo/definitions/America/Jamaica.rb", "lib/tzinfo/definitions/America/Jujuy.rb", "lib/tzinfo/definitions/America/Juneau.rb", "lib/tzinfo/definitions/America/Kentucky/Louisville.rb", "lib/tzinfo/definitions/America/Kentucky/Monticello.rb", "lib/tzinfo/definitions/America/Knox_IN.rb", "lib/tzinfo/definitions/America/La_Paz.rb", "lib/tzinfo/definitions/America/Lima.rb", "lib/tzinfo/definitions/America/Los_Angeles.rb", "lib/tzinfo/definitions/America/Louisville.rb", "lib/tzinfo/definitions/America/Maceio.rb", "lib/tzinfo/definitions/America/Managua.rb", "lib/tzinfo/definitions/America/Manaus.rb", "lib/tzinfo/definitions/America/Marigot.rb", "lib/tzinfo/definitions/America/Martinique.rb", "lib/tzinfo/definitions/America/Mazatlan.rb", "lib/tzinfo/definitions/America/Mendoza.rb", "lib/tzinfo/definitions/America/Menominee.rb", "lib/tzinfo/definitions/America/Merida.rb", "lib/tzinfo/definitions/America/Mexico_City.rb", "lib/tzinfo/definitions/America/Miquelon.rb", "lib/tzinfo/definitions/America/Moncton.rb", "lib/tzinfo/definitions/America/Monterrey.rb", "lib/tzinfo/definitions/America/Montevideo.rb", "lib/tzinfo/definitions/America/Montreal.rb", "lib/tzinfo/definitions/America/Montserrat.rb", "lib/tzinfo/definitions/America/Nassau.rb", "lib/tzinfo/definitions/America/New_York.rb", "lib/tzinfo/definitions/America/Nipigon.rb", "lib/tzinfo/definitions/America/Nome.rb", "lib/tzinfo/definitions/America/Noronha.rb", "lib/tzinfo/definitions/America/North_Dakota/Center.rb", "lib/tzinfo/definitions/America/North_Dakota/New_Salem.rb", "lib/tzinfo/definitions/America/Panama.rb", "lib/tzinfo/definitions/America/Pangnirtung.rb", "lib/tzinfo/definitions/America/Paramaribo.rb", "lib/tzinfo/definitions/America/Phoenix.rb", "lib/tzinfo/definitions/America/Porto_Acre.rb", "lib/tzinfo/definitions/America/Porto_Velho.rb", "lib/tzinfo/definitions/America/Port_of_Spain.rb", "lib/tzinfo/definitions/America/Port__m__au__m__Prince.rb", "lib/tzinfo/definitions/America/Puerto_Rico.rb", "lib/tzinfo/definitions/America/Rainy_River.rb", "lib/tzinfo/definitions/America/Rankin_Inlet.rb", "lib/tzinfo/definitions/America/Recife.rb", "lib/tzinfo/definitions/America/Regina.rb", "lib/tzinfo/definitions/America/Resolute.rb", "lib/tzinfo/definitions/America/Rio_Branco.rb", "lib/tzinfo/definitions/America/Rosario.rb", "lib/tzinfo/definitions/America/Santarem.rb", "lib/tzinfo/definitions/America/Santiago.rb", "lib/tzinfo/definitions/America/Santo_Domingo.rb", "lib/tzinfo/definitions/America/Sao_Paulo.rb", "lib/tzinfo/definitions/America/Scoresbysund.rb", "lib/tzinfo/definitions/America/Shiprock.rb", "lib/tzinfo/definitions/America/St_Barthelemy.rb", "lib/tzinfo/definitions/America/St_Johns.rb", "lib/tzinfo/definitions/America/St_Kitts.rb", "lib/tzinfo/definitions/America/St_Lucia.rb", "lib/tzinfo/definitions/America/St_Thomas.rb", "lib/tzinfo/definitions/America/St_Vincent.rb", "lib/tzinfo/definitions/America/Swift_Current.rb", "lib/tzinfo/definitions/America/Tegucigalpa.rb", "lib/tzinfo/definitions/America/Thule.rb", "lib/tzinfo/definitions/America/Thunder_Bay.rb", "lib/tzinfo/definitions/America/Tijuana.rb", "lib/tzinfo/definitions/America/Toronto.rb", "lib/tzinfo/definitions/America/Tortola.rb", "lib/tzinfo/definitions/America/Vancouver.rb", "lib/tzinfo/definitions/America/Virgin.rb", "lib/tzinfo/definitions/America/Whitehorse.rb", "lib/tzinfo/definitions/America/Winnipeg.rb", "lib/tzinfo/definitions/America/Yakutat.rb", "lib/tzinfo/definitions/America/Yellowknife.rb", "lib/tzinfo/definitions/Antarctica/Casey.rb", "lib/tzinfo/definitions/Antarctica/Davis.rb", "lib/tzinfo/definitions/Antarctica/DumontDUrville.rb", "lib/tzinfo/definitions/Antarctica/Mawson.rb", "lib/tzinfo/definitions/Antarctica/McMurdo.rb", "lib/tzinfo/definitions/Antarctica/Palmer.rb", "lib/tzinfo/definitions/Antarctica/Rothera.rb", "lib/tzinfo/definitions/Antarctica/South_Pole.rb", "lib/tzinfo/definitions/Antarctica/Syowa.rb", "lib/tzinfo/definitions/Antarctica/Vostok.rb", "lib/tzinfo/definitions/Arctic/Longyearbyen.rb", "lib/tzinfo/definitions/Asia/Aden.rb", "lib/tzinfo/definitions/Asia/Almaty.rb", "lib/tzinfo/definitions/Asia/Amman.rb", "lib/tzinfo/definitions/Asia/Anadyr.rb", "lib/tzinfo/definitions/Asia/Aqtau.rb", "lib/tzinfo/definitions/Asia/Aqtobe.rb", "lib/tzinfo/definitions/Asia/Ashgabat.rb", "lib/tzinfo/definitions/Asia/Ashkhabad.rb", "lib/tzinfo/definitions/Asia/Baghdad.rb", "lib/tzinfo/definitions/Asia/Bahrain.rb", "lib/tzinfo/definitions/Asia/Baku.rb", "lib/tzinfo/definitions/Asia/Bangkok.rb", "lib/tzinfo/definitions/Asia/Beirut.rb", "lib/tzinfo/definitions/Asia/Bishkek.rb", "lib/tzinfo/definitions/Asia/Brunei.rb", "lib/tzinfo/definitions/Asia/Calcutta.rb", "lib/tzinfo/definitions/Asia/Choibalsan.rb", "lib/tzinfo/definitions/Asia/Chongqing.rb", "lib/tzinfo/definitions/Asia/Chungking.rb", "lib/tzinfo/definitions/Asia/Colombo.rb", "lib/tzinfo/definitions/Asia/Dacca.rb", "lib/tzinfo/definitions/Asia/Damascus.rb", "lib/tzinfo/definitions/Asia/Dhaka.rb", "lib/tzinfo/definitions/Asia/Dili.rb", "lib/tzinfo/definitions/Asia/Dubai.rb", "lib/tzinfo/definitions/Asia/Dushanbe.rb", "lib/tzinfo/definitions/Asia/Gaza.rb", "lib/tzinfo/definitions/Asia/Harbin.rb", "lib/tzinfo/definitions/Asia/Hong_Kong.rb", "lib/tzinfo/definitions/Asia/Hovd.rb", "lib/tzinfo/definitions/Asia/Ho_Chi_Minh.rb", "lib/tzinfo/definitions/Asia/Irkutsk.rb", "lib/tzinfo/definitions/Asia/Istanbul.rb", "lib/tzinfo/definitions/Asia/Jakarta.rb", "lib/tzinfo/definitions/Asia/Jayapura.rb", "lib/tzinfo/definitions/Asia/Jerusalem.rb", "lib/tzinfo/definitions/Asia/Kabul.rb", "lib/tzinfo/definitions/Asia/Kamchatka.rb", "lib/tzinfo/definitions/Asia/Karachi.rb", "lib/tzinfo/definitions/Asia/Kashgar.rb", "lib/tzinfo/definitions/Asia/Kathmandu.rb", "lib/tzinfo/definitions/Asia/Katmandu.rb", "lib/tzinfo/definitions/Asia/Kolkata.rb", "lib/tzinfo/definitions/Asia/Krasnoyarsk.rb", "lib/tzinfo/definitions/Asia/Kuala_Lumpur.rb", "lib/tzinfo/definitions/Asia/Kuching.rb", "lib/tzinfo/definitions/Asia/Kuwait.rb", "lib/tzinfo/definitions/Asia/Macao.rb", "lib/tzinfo/definitions/Asia/Macau.rb", "lib/tzinfo/definitions/Asia/Magadan.rb", "lib/tzinfo/definitions/Asia/Makassar.rb", "lib/tzinfo/definitions/Asia/Manila.rb", "lib/tzinfo/definitions/Asia/Muscat.rb", "lib/tzinfo/definitions/Asia/Nicosia.rb", "lib/tzinfo/definitions/Asia/Novosibirsk.rb", "lib/tzinfo/definitions/Asia/Omsk.rb", "lib/tzinfo/definitions/Asia/Oral.rb", "lib/tzinfo/definitions/Asia/Phnom_Penh.rb", "lib/tzinfo/definitions/Asia/Pontianak.rb", "lib/tzinfo/definitions/Asia/Pyongyang.rb", "lib/tzinfo/definitions/Asia/Qatar.rb", "lib/tzinfo/definitions/Asia/Qyzylorda.rb", "lib/tzinfo/definitions/Asia/Rangoon.rb", "lib/tzinfo/definitions/Asia/Riyadh.rb", "lib/tzinfo/definitions/Asia/Riyadh87.rb", "lib/tzinfo/definitions/Asia/Riyadh88.rb", "lib/tzinfo/definitions/Asia/Riyadh89.rb", "lib/tzinfo/definitions/Asia/Saigon.rb", "lib/tzinfo/definitions/Asia/Sakhalin.rb", "lib/tzinfo/definitions/Asia/Samarkand.rb", "lib/tzinfo/definitions/Asia/Seoul.rb", "lib/tzinfo/definitions/Asia/Shanghai.rb", "lib/tzinfo/definitions/Asia/Singapore.rb", "lib/tzinfo/definitions/Asia/Taipei.rb", "lib/tzinfo/definitions/Asia/Tashkent.rb", "lib/tzinfo/definitions/Asia/Tbilisi.rb", "lib/tzinfo/definitions/Asia/Tehran.rb", "lib/tzinfo/definitions/Asia/Tel_Aviv.rb", "lib/tzinfo/definitions/Asia/Thimbu.rb", "lib/tzinfo/definitions/Asia/Thimphu.rb", "lib/tzinfo/definitions/Asia/Tokyo.rb", "lib/tzinfo/definitions/Asia/Ujung_Pandang.rb", "lib/tzinfo/definitions/Asia/Ulaanbaatar.rb", "lib/tzinfo/definitions/Asia/Ulan_Bator.rb", "lib/tzinfo/definitions/Asia/Urumqi.rb", "lib/tzinfo/definitions/Asia/Vientiane.rb", "lib/tzinfo/definitions/Asia/Vladivostok.rb", "lib/tzinfo/definitions/Asia/Yakutsk.rb", "lib/tzinfo/definitions/Asia/Yekaterinburg.rb", "lib/tzinfo/definitions/Asia/Yerevan.rb", "lib/tzinfo/definitions/Atlantic/Azores.rb", "lib/tzinfo/definitions/Atlantic/Bermuda.rb", "lib/tzinfo/definitions/Atlantic/Canary.rb", "lib/tzinfo/definitions/Atlantic/Cape_Verde.rb", "lib/tzinfo/definitions/Atlantic/Faeroe.rb", "lib/tzinfo/definitions/Atlantic/Faroe.rb", "lib/tzinfo/definitions/Atlantic/Jan_Mayen.rb", "lib/tzinfo/definitions/Atlantic/Madeira.rb", "lib/tzinfo/definitions/Atlantic/Reykjavik.rb", "lib/tzinfo/definitions/Atlantic/South_Georgia.rb", "lib/tzinfo/definitions/Atlantic/Stanley.rb", "lib/tzinfo/definitions/Atlantic/St_Helena.rb", "lib/tzinfo/definitions/Australia/ACT.rb", "lib/tzinfo/definitions/Australia/Adelaide.rb", "lib/tzinfo/definitions/Australia/Brisbane.rb", "lib/tzinfo/definitions/Australia/Broken_Hill.rb", "lib/tzinfo/definitions/Australia/Canberra.rb", "lib/tzinfo/definitions/Australia/Currie.rb", "lib/tzinfo/definitions/Australia/Darwin.rb", "lib/tzinfo/definitions/Australia/Eucla.rb", "lib/tzinfo/definitions/Australia/Hobart.rb", "lib/tzinfo/definitions/Australia/LHI.rb", "lib/tzinfo/definitions/Australia/Lindeman.rb", "lib/tzinfo/definitions/Australia/Lord_Howe.rb", "lib/tzinfo/definitions/Australia/Melbourne.rb", "lib/tzinfo/definitions/Australia/North.rb", "lib/tzinfo/definitions/Australia/NSW.rb", "lib/tzinfo/definitions/Australia/Perth.rb", "lib/tzinfo/definitions/Australia/Queensland.rb", "lib/tzinfo/definitions/Australia/South.rb", "lib/tzinfo/definitions/Australia/Sydney.rb", "lib/tzinfo/definitions/Australia/Tasmania.rb", "lib/tzinfo/definitions/Australia/Victoria.rb", "lib/tzinfo/definitions/Australia/West.rb", "lib/tzinfo/definitions/Australia/Yancowinna.rb", "lib/tzinfo/definitions/Brazil/Acre.rb", "lib/tzinfo/definitions/Brazil/DeNoronha.rb", "lib/tzinfo/definitions/Brazil/East.rb", "lib/tzinfo/definitions/Brazil/West.rb", "lib/tzinfo/definitions/Canada/Atlantic.rb", "lib/tzinfo/definitions/Canada/Central.rb", "lib/tzinfo/definitions/Canada/Eastern.rb", "lib/tzinfo/definitions/Canada/East__m__Saskatchewan.rb", "lib/tzinfo/definitions/Canada/Mountain.rb", "lib/tzinfo/definitions/Canada/Newfoundland.rb", "lib/tzinfo/definitions/Canada/Pacific.rb", "lib/tzinfo/definitions/Canada/Saskatchewan.rb", "lib/tzinfo/definitions/Canada/Yukon.rb", "lib/tzinfo/definitions/CET.rb", "lib/tzinfo/definitions/Chile/Continental.rb", "lib/tzinfo/definitions/Chile/EasterIsland.rb", "lib/tzinfo/definitions/CST6CDT.rb", "lib/tzinfo/definitions/Cuba.rb", "lib/tzinfo/definitions/EET.rb", "lib/tzinfo/definitions/Egypt.rb", "lib/tzinfo/definitions/Eire.rb", "lib/tzinfo/definitions/EST.rb", "lib/tzinfo/definitions/EST5EDT.rb", "lib/tzinfo/definitions/Etc/GMT.rb", "lib/tzinfo/definitions/Etc/GMT0.rb", "lib/tzinfo/definitions/Etc/GMT__m__0.rb", "lib/tzinfo/definitions/Etc/GMT__m__1.rb", "lib/tzinfo/definitions/Etc/GMT__m__10.rb", "lib/tzinfo/definitions/Etc/GMT__m__11.rb", "lib/tzinfo/definitions/Etc/GMT__m__12.rb", "lib/tzinfo/definitions/Etc/GMT__m__13.rb", "lib/tzinfo/definitions/Etc/GMT__m__14.rb", "lib/tzinfo/definitions/Etc/GMT__m__2.rb", "lib/tzinfo/definitions/Etc/GMT__m__3.rb", "lib/tzinfo/definitions/Etc/GMT__m__4.rb", "lib/tzinfo/definitions/Etc/GMT__m__5.rb", "lib/tzinfo/definitions/Etc/GMT__m__6.rb", "lib/tzinfo/definitions/Etc/GMT__m__7.rb", "lib/tzinfo/definitions/Etc/GMT__m__8.rb", "lib/tzinfo/definitions/Etc/GMT__m__9.rb", "lib/tzinfo/definitions/Etc/GMT__p__0.rb", "lib/tzinfo/definitions/Etc/GMT__p__1.rb", "lib/tzinfo/definitions/Etc/GMT__p__10.rb", "lib/tzinfo/definitions/Etc/GMT__p__11.rb", "lib/tzinfo/definitions/Etc/GMT__p__12.rb", "lib/tzinfo/definitions/Etc/GMT__p__2.rb", "lib/tzinfo/definitions/Etc/GMT__p__3.rb", "lib/tzinfo/definitions/Etc/GMT__p__4.rb", "lib/tzinfo/definitions/Etc/GMT__p__5.rb", "lib/tzinfo/definitions/Etc/GMT__p__6.rb", "lib/tzinfo/definitions/Etc/GMT__p__7.rb", "lib/tzinfo/definitions/Etc/GMT__p__8.rb", "lib/tzinfo/definitions/Etc/GMT__p__9.rb", "lib/tzinfo/definitions/Etc/Greenwich.rb", "lib/tzinfo/definitions/Etc/UCT.rb", "lib/tzinfo/definitions/Etc/Universal.rb", "lib/tzinfo/definitions/Etc/UTC.rb", "lib/tzinfo/definitions/Etc/Zulu.rb", "lib/tzinfo/definitions/Europe/Amsterdam.rb", "lib/tzinfo/definitions/Europe/Andorra.rb", "lib/tzinfo/definitions/Europe/Athens.rb", "lib/tzinfo/definitions/Europe/Belfast.rb", "lib/tzinfo/definitions/Europe/Belgrade.rb", "lib/tzinfo/definitions/Europe/Berlin.rb", "lib/tzinfo/definitions/Europe/Bratislava.rb", "lib/tzinfo/definitions/Europe/Brussels.rb", "lib/tzinfo/definitions/Europe/Bucharest.rb", "lib/tzinfo/definitions/Europe/Budapest.rb", "lib/tzinfo/definitions/Europe/Chisinau.rb", "lib/tzinfo/definitions/Europe/Copenhagen.rb", "lib/tzinfo/definitions/Europe/Dublin.rb", "lib/tzinfo/definitions/Europe/Gibraltar.rb", "lib/tzinfo/definitions/Europe/Guernsey.rb", "lib/tzinfo/definitions/Europe/Helsinki.rb", "lib/tzinfo/definitions/Europe/Isle_of_Man.rb", "lib/tzinfo/definitions/Europe/Istanbul.rb", "lib/tzinfo/definitions/Europe/Jersey.rb", "lib/tzinfo/definitions/Europe/Kaliningrad.rb", "lib/tzinfo/definitions/Europe/Kiev.rb", "lib/tzinfo/definitions/Europe/Lisbon.rb", "lib/tzinfo/definitions/Europe/Ljubljana.rb", "lib/tzinfo/definitions/Europe/London.rb", "lib/tzinfo/definitions/Europe/Luxembourg.rb", "lib/tzinfo/definitions/Europe/Madrid.rb", "lib/tzinfo/definitions/Europe/Malta.rb", "lib/tzinfo/definitions/Europe/Mariehamn.rb", "lib/tzinfo/definitions/Europe/Minsk.rb", "lib/tzinfo/definitions/Europe/Monaco.rb", "lib/tzinfo/definitions/Europe/Moscow.rb", "lib/tzinfo/definitions/Europe/Nicosia.rb", "lib/tzinfo/definitions/Europe/Oslo.rb", "lib/tzinfo/definitions/Europe/Paris.rb", "lib/tzinfo/definitions/Europe/Podgorica.rb", "lib/tzinfo/definitions/Europe/Prague.rb", "lib/tzinfo/definitions/Europe/Riga.rb", "lib/tzinfo/definitions/Europe/Rome.rb", "lib/tzinfo/definitions/Europe/Samara.rb", "lib/tzinfo/definitions/Europe/San_Marino.rb", "lib/tzinfo/definitions/Europe/Sarajevo.rb", "lib/tzinfo/definitions/Europe/Simferopol.rb", "lib/tzinfo/definitions/Europe/Skopje.rb", "lib/tzinfo/definitions/Europe/Sofia.rb", "lib/tzinfo/definitions/Europe/Stockholm.rb", "lib/tzinfo/definitions/Europe/Tallinn.rb", "lib/tzinfo/definitions/Europe/Tirane.rb", "lib/tzinfo/definitions/Europe/Tiraspol.rb", "lib/tzinfo/definitions/Europe/Uzhgorod.rb", "lib/tzinfo/definitions/Europe/Vaduz.rb", "lib/tzinfo/definitions/Europe/Vatican.rb", "lib/tzinfo/definitions/Europe/Vienna.rb", "lib/tzinfo/definitions/Europe/Vilnius.rb", "lib/tzinfo/definitions/Europe/Volgograd.rb", "lib/tzinfo/definitions/Europe/Warsaw.rb", "lib/tzinfo/definitions/Europe/Zagreb.rb", "lib/tzinfo/definitions/Europe/Zaporozhye.rb", "lib/tzinfo/definitions/Europe/Zurich.rb", "lib/tzinfo/definitions/GB.rb", "lib/tzinfo/definitions/GB__m__Eire.rb", "lib/tzinfo/definitions/GMT.rb", "lib/tzinfo/definitions/GMT0.rb", "lib/tzinfo/definitions/GMT__m__0.rb", "lib/tzinfo/definitions/GMT__p__0.rb", "lib/tzinfo/definitions/Greenwich.rb", "lib/tzinfo/definitions/Hongkong.rb", "lib/tzinfo/definitions/HST.rb", "lib/tzinfo/definitions/Iceland.rb", "lib/tzinfo/definitions/Indian/Antananarivo.rb", "lib/tzinfo/definitions/Indian/Chagos.rb", "lib/tzinfo/definitions/Indian/Christmas.rb", "lib/tzinfo/definitions/Indian/Cocos.rb", "lib/tzinfo/definitions/Indian/Comoro.rb", "lib/tzinfo/definitions/Indian/Kerguelen.rb", "lib/tzinfo/definitions/Indian/Mahe.rb", "lib/tzinfo/definitions/Indian/Maldives.rb", "lib/tzinfo/definitions/Indian/Mauritius.rb", "lib/tzinfo/definitions/Indian/Mayotte.rb", "lib/tzinfo/definitions/Indian/Reunion.rb", "lib/tzinfo/definitions/Iran.rb", "lib/tzinfo/definitions/Israel.rb", "lib/tzinfo/definitions/Jamaica.rb", "lib/tzinfo/definitions/Japan.rb", "lib/tzinfo/definitions/Kwajalein.rb", "lib/tzinfo/definitions/Libya.rb", "lib/tzinfo/definitions/MET.rb", "lib/tzinfo/definitions/Mexico/BajaNorte.rb", "lib/tzinfo/definitions/Mexico/BajaSur.rb", "lib/tzinfo/definitions/Mexico/General.rb", "lib/tzinfo/definitions/Mideast/Riyadh87.rb", "lib/tzinfo/definitions/Mideast/Riyadh88.rb", "lib/tzinfo/definitions/Mideast/Riyadh89.rb", "lib/tzinfo/definitions/MST.rb", "lib/tzinfo/definitions/MST7MDT.rb", "lib/tzinfo/definitions/Navajo.rb", "lib/tzinfo/definitions/NZ.rb", "lib/tzinfo/definitions/NZ__m__CHAT.rb", "lib/tzinfo/definitions/Pacific/Apia.rb", "lib/tzinfo/definitions/Pacific/Auckland.rb", "lib/tzinfo/definitions/Pacific/Chatham.rb", "lib/tzinfo/definitions/Pacific/Easter.rb", "lib/tzinfo/definitions/Pacific/Efate.rb", "lib/tzinfo/definitions/Pacific/Enderbury.rb", "lib/tzinfo/definitions/Pacific/Fakaofo.rb", "lib/tzinfo/definitions/Pacific/Fiji.rb", "lib/tzinfo/definitions/Pacific/Funafuti.rb", "lib/tzinfo/definitions/Pacific/Galapagos.rb", "lib/tzinfo/definitions/Pacific/Gambier.rb", "lib/tzinfo/definitions/Pacific/Guadalcanal.rb", "lib/tzinfo/definitions/Pacific/Guam.rb", "lib/tzinfo/definitions/Pacific/Honolulu.rb", "lib/tzinfo/definitions/Pacific/Johnston.rb", "lib/tzinfo/definitions/Pacific/Kiritimati.rb", "lib/tzinfo/definitions/Pacific/Kosrae.rb", "lib/tzinfo/definitions/Pacific/Kwajalein.rb", "lib/tzinfo/definitions/Pacific/Majuro.rb", "lib/tzinfo/definitions/Pacific/Marquesas.rb", "lib/tzinfo/definitions/Pacific/Midway.rb", "lib/tzinfo/definitions/Pacific/Nauru.rb", "lib/tzinfo/definitions/Pacific/Niue.rb", "lib/tzinfo/definitions/Pacific/Norfolk.rb", "lib/tzinfo/definitions/Pacific/Noumea.rb", "lib/tzinfo/definitions/Pacific/Pago_Pago.rb", "lib/tzinfo/definitions/Pacific/Palau.rb", "lib/tzinfo/definitions/Pacific/Pitcairn.rb", "lib/tzinfo/definitions/Pacific/Ponape.rb", "lib/tzinfo/definitions/Pacific/Port_Moresby.rb", "lib/tzinfo/definitions/Pacific/Rarotonga.rb", "lib/tzinfo/definitions/Pacific/Saipan.rb", "lib/tzinfo/definitions/Pacific/Samoa.rb", "lib/tzinfo/definitions/Pacific/Tahiti.rb", "lib/tzinfo/definitions/Pacific/Tarawa.rb", "lib/tzinfo/definitions/Pacific/Tongatapu.rb", "lib/tzinfo/definitions/Pacific/Truk.rb", "lib/tzinfo/definitions/Pacific/Wake.rb", "lib/tzinfo/definitions/Pacific/Wallis.rb", "lib/tzinfo/definitions/Pacific/Yap.rb", "lib/tzinfo/definitions/Poland.rb", "lib/tzinfo/definitions/Portugal.rb", "lib/tzinfo/definitions/PRC.rb", "lib/tzinfo/definitions/PST8PDT.rb", "lib/tzinfo/definitions/ROC.rb", "lib/tzinfo/definitions/ROK.rb", "lib/tzinfo/definitions/Singapore.rb", "lib/tzinfo/definitions/Turkey.rb", "lib/tzinfo/definitions/UCT.rb", "lib/tzinfo/definitions/Universal.rb", "lib/tzinfo/definitions/US/Alaska.rb", "lib/tzinfo/definitions/US/Aleutian.rb", "lib/tzinfo/definitions/US/Arizona.rb", "lib/tzinfo/definitions/US/Central.rb", "lib/tzinfo/definitions/US/Eastern.rb", "lib/tzinfo/definitions/US/East__m__Indiana.rb", "lib/tzinfo/definitions/US/Hawaii.rb", "lib/tzinfo/definitions/US/Indiana__m__Starke.rb", "lib/tzinfo/definitions/US/Michigan.rb", "lib/tzinfo/definitions/US/Mountain.rb", "lib/tzinfo/definitions/US/Pacific.rb", "lib/tzinfo/definitions/US/Pacific__m__New.rb", "lib/tzinfo/definitions/US/Samoa.rb", "lib/tzinfo/definitions/UTC.rb", "lib/tzinfo/definitions/WET.rb", "lib/tzinfo/definitions/W__m__SU.rb", "lib/tzinfo/definitions/Zulu.rb", "lib/tzinfo/indexes/countries.rb", "lib/tzinfo/indexes/timezones.rb", "lib/tzinfo/info_timezone.rb", "lib/tzinfo/linked_timezone.rb", "lib/tzinfo/linked_timezone_info.rb", "lib/tzinfo/offset_rationals.rb", "lib/tzinfo/ruby_core_support.rb", "lib/tzinfo/timezone.rb", "lib/tzinfo/timezone_definition.rb", "lib/tzinfo/timezone_index_definition.rb", "lib/tzinfo/timezone_info.rb", "lib/tzinfo/timezone_offset_info.rb", "lib/tzinfo/timezone_period.rb", "lib/tzinfo/timezone_proxy.rb", "lib/tzinfo/timezone_transition_info.rb", "lib/tzinfo/time_or_datetime.rb", "lib/tzinfo/tzdataparser.rb", "lib/tzinfo.rb", "test/tc_country.rb", "test/tc_country_index_definition.rb", "test/tc_country_info.rb", "test/tc_country_timezone.rb", "test/tc_data_timezone.rb", "test/tc_data_timezone_info.rb", "test/tc_info_timezone.rb", "test/tc_linked_timezone.rb", "test/tc_linked_timezone_info.rb", "test/tc_offset_rationals.rb", "test/tc_ruby_core_support.rb", "test/tc_timezone.rb", "test/tc_timezone_definition.rb", "test/tc_timezone_index_definition.rb", "test/tc_timezone_info.rb", "test/tc_timezone_london.rb", "test/tc_timezone_melbourne.rb", "test/tc_timezone_new_york.rb", "test/tc_timezone_offset_info.rb", "test/tc_timezone_period.rb", "test/tc_timezone_proxy.rb", "test/tc_timezone_transition_info.rb", "test/tc_timezone_utc.rb", "test/tc_time_or_datetime.rb", "test/test_utils.rb", "test/ts_all.rb"] + s.homepage = %q{http://tzinfo.rubyforge.org/} + s.rdoc_options = ["--exclude", "definitions", "--exclude", "indexes"] + s.require_paths = ["lib"] + s.rubyforge_project = %q{tzinfo} + s.rubygems_version = %q{1.3.5} + s.summary = %q{Daylight-savings aware timezone library} + s.test_files = ["test/tc_country.rb", "test/tc_country_index_definition.rb", "test/tc_country_info.rb", "test/tc_country_timezone.rb", "test/tc_data_timezone.rb", "test/tc_data_timezone_info.rb", "test/tc_info_timezone.rb", "test/tc_linked_timezone.rb", "test/tc_linked_timezone_info.rb", "test/tc_offset_rationals.rb", "test/tc_ruby_core_support.rb", "test/tc_timezone.rb", "test/tc_timezone_definition.rb", "test/tc_timezone_index_definition.rb", "test/tc_timezone_info.rb", "test/tc_timezone_london.rb", "test/tc_timezone_melbourne.rb", "test/tc_timezone_new_york.rb", "test/tc_timezone_offset_info.rb", "test/tc_timezone_period.rb", "test/tc_timezone_proxy.rb", "test/tc_timezone_transition_info.rb", "test/tc_timezone_utc.rb", "test/tc_time_or_datetime.rb", "test/test_utils.rb", "test/ts_all.rb"] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 3 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + else + end + else + end +end ===================================================================