#!/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'

include EM::Ssh::Log


def bufferio( enable, io = $stdin )
  raise "Termios library not found" unless defined?(::Termios)
  attr = Termios::getattr( io )
  enable ? (attr.c_lflag |= Termios::ICANON | Termios::ECHO) : (attr.c_lflag &= ~(Termios::ICANON|Termios::ECHO))
  Termios::setattr( io, Termios::TCSANOW, attr )
end # def bufferio( enable, io = $stdin )

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

options = {}
opts    = OptionParser.new
opts.banner += " [user:[password]@]host[:port]"
options[:port] = 22
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('-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]
connected = false


module CInput
  def shell=(shell)
    @shell = shell
  end # shell=(shell)
  def initialize
    bufferio(false, $stdin)
  end # initialize
  def unbind
    bufferio(true, $stdin)
  end # unbind
  def notify_readable
    @shell.send_data($stdin.read(1))
  end
end



EM.run do
  EM::Ssh.start(host, options[:user], options) do |ssh|
    ssh.errback do |err|
      puts "error: #{err} (#{err.class})"
      EM.stop
    end 

    ssh.callback do |connection|
      debug "**** connected: #{connection}"
      connection.open_channel do |channel|
        debug "**** channel: #{channel}"
        channel.request_pty(options[:pty] || {}) do |pty,suc|
          debug "***** pty: #{pty}; suc: #{suc}"
          pty.send_channel_request("shell") do |shell,success|
            raise ConnectionError, "Failed to create shell." unless success
            debug "***** shell: #{shell}"
            connected = true

            shell.on_data { |c,d| $stdout.print d } 
            shell.on_extended_data { |c,data| $STDERR.print data }
            shell.on_eof do
              shell.close
              EM.stop
            end # 

            trap("SIGINT") { shell.send_data("\C-c") }
            trap("SIGEXIT") do
              shell.close
              trap("SIGINT", "SIG_DFL")
            end # 

            conn        = EM.watch($stdin, CInput)
            conn.shell  = shell
            conn.notify_readable = true

          end # |shell,success|
        end # |pty,suc|
      end # |channel|
    end

  end #  |connection|  
end # EM.start

