#!/usr/bin/env ruby
# This is really nothing more than a utility to test the em-ssh adapter.
# It's not meant to be used for anything else.
# It probably requires ruby 1.9.2-p180 as p190 tends to segfault when using Fibers.
require 'bundler/setup'
require 'termios'
require 'highline'
require 'em-ssh'
require 'em-ssh/shell'

include EM::Ssh::Log

def abort(msg)
  puts msg
  Process.exit
end # abort(msg)

options     = {:auth_methods => ['publickey', 'password'], :port => 22}
opts        = OptionParser.new
opts.banner += " [user:[password]@]host[:port] wait_string command [command command ...]"

opts.on('-u', '--user String', String) { |u| options[:user] = u }
opts.on('-p', '--password [String]', String) do |p| 
  options[:password] = p.nil? ? HighLine.new.ask("password: "){|q| q.echo = "*" } : p
end
opts.on('-t', '--timeout Integer', Integer) { |t| options[:timeout] = t }
opts.on('--[no-]publickey', "don't attempt public key auth") do |pk|
  options[:auth_methods] = ['password'] unless pk
end 
opts.on('-v', '--verbose') do 
  EM::Ssh.logger.level = EM::Ssh.logger.level - 1 unless EM::Ssh.logger.level == 0 
  options[:verbose] = EM::Ssh.logger.level
end
opts.parse!

host = ARGV.shift
if host.nil?
  host,options[:password] = options[:password], HighLine.new.ask("#{options[:password]}'s password: "){|q| q.echo = "*" }
end # host.nil?
abort("a host is required") if host.nil?

options[:user], host = *host.split('@') if host.include?('@')
options[:user], options[:password] = *options[:user].split(':') if options[:user] && options[:user].include?(':')
host, options[:port] = *host.split(':') if host.include?(':')
options[:user]       = ENV['USER'] unless options[:user]
options[:password]   = HighLine.new.ask("#{options[:user]}'s password: "){|q| q.echo = "*" } unless options[:password]


waitstr  = ARGV.shift
commands = ARGV
abort("wait_string is required") if waitstr.nil?
abort("command is required") if commands.empty?
waitstr = Regexp.escape(waitstr)


EM.run do
  EM::Ssh::Shell.new(host, options[:user], options[:password], :timeout => options[:timeout], :net_ssh => options) do |shell|
    shell.errback do |err|
      puts "error: #{err} (#{err.class})"
      EM.stop
    end 

    shell.callback do 
      commands.clone.each do |command|

        mys = shell.split
        mys.errback do |err|
          $stderr.puts "subshell error: #{err} (#{err.class})"
          mys.close
        end

        mys.on(:closed) do
          commands.delete(command)
          EM.stop if commands.empty?
        end

        puts("waiting for: #{waitstr.inspect}")
        mys.callback do
          mys.expect(waitstr) do 
            puts "sending #{command.inspect} and waiting for #{waitstr.inspect}"
            mys.expect(waitstr, command) do |result|
              puts "#{mys} result: '#{result}'"
              mys.close
            end 
          end 
        end

      end 
    end 
  end 
end 
