185 lines
3.4 KiB
Ruby
185 lines
3.4 KiB
Ruby
require 'rex/ui'
|
|
|
|
module Rex
|
|
module Ui
|
|
module Text
|
|
|
|
begin
|
|
require 'readline'
|
|
|
|
###
|
|
#
|
|
# This class implements standard input using readline against
|
|
# standard input. It supports tab completion.
|
|
#
|
|
###
|
|
class Input::Readline < Rex::Ui::Text::Input
|
|
include ::Readline
|
|
|
|
@@rl_thread = nil
|
|
@@rl_pipes = nil
|
|
@@rl_prompt = ''
|
|
@@rl_history = true
|
|
|
|
#
|
|
# Enable readline mode by setting rl_usestdio to false
|
|
# Address the following items before switching:
|
|
#
|
|
# XXX: Find a way to emulate ^Z when in readline mode
|
|
# XXX: Create histories/tab completion for shells
|
|
# XXX: Capture remote shell prompt and use for readline
|
|
#
|
|
@@rl_usestdio = true
|
|
|
|
#
|
|
# Initializes the readline-aware Input instance for text.
|
|
#
|
|
def initialize(tab_complete_proc = nil)
|
|
if (tab_complete_proc)
|
|
::Readline.basic_word_break_characters = "\x00"
|
|
::Readline.completion_proc = tab_complete_proc
|
|
end
|
|
end
|
|
|
|
#
|
|
# Start the readline thread
|
|
#
|
|
def readline_start
|
|
return if @@rl_usestdio
|
|
return if @@rl_thread
|
|
@@rl_pipes = Rex::Compat.pipe
|
|
@@rl_thread = ::Thread.new do
|
|
begin
|
|
while (line = ::Readline.readline(@@rl_prompt, @@rl_history))
|
|
@@rl_pipes[1].write(line+"\n")
|
|
@@rl_pipes[1].flush
|
|
end
|
|
rescue ::Exception => e
|
|
$stderr.puts "ERROR: readline thread: #{e.to_s} #{e.backtrace.to_s}"
|
|
end
|
|
end
|
|
end
|
|
|
|
#
|
|
# Stop the readline thread
|
|
#
|
|
def readline_stop
|
|
# Stop the reader thread
|
|
if (@@rl_thread)
|
|
begin
|
|
@@rl_thread.kill
|
|
rescue ::Exception
|
|
end
|
|
@@rl_thread = nil
|
|
end
|
|
|
|
# Close the pipes
|
|
if (@@rl_pipes)
|
|
begin
|
|
@rl_pipes[0].close
|
|
@rl_pipes[1].close
|
|
rescue ::Exception
|
|
end
|
|
@@rl_pipes = nil
|
|
end
|
|
end
|
|
|
|
#
|
|
# Status of the readline thread
|
|
#
|
|
def readline_status
|
|
@@rl_thread ? true : false
|
|
end
|
|
|
|
#
|
|
# Calls sysread on the standard input handle.
|
|
#
|
|
def sysread(len = 1)
|
|
return $stdin.sysread(len) if @@rl_usestdio
|
|
|
|
if (! readline_status)
|
|
$stderr.puts "ERROR: sysread() called outside of thread mode: " + caller(1).to_s
|
|
return ''
|
|
end
|
|
@@rl_pipes[0].sysread(len)
|
|
end
|
|
|
|
#
|
|
# Fake gets using readline
|
|
#
|
|
def gets()
|
|
return $stdin.gets() if @@rl_usestdio
|
|
|
|
if (! readline_status)
|
|
$stderr.puts "ERROR: gets() called outside of thread mode: " + caller(1).to_s
|
|
return ''
|
|
end
|
|
|
|
@@rl_pipes[0].gets
|
|
end
|
|
|
|
#
|
|
# Print a prompt and flush standard output.
|
|
#
|
|
def prompt(prompt)
|
|
_print_prompt(prompt)
|
|
return gets()
|
|
end
|
|
|
|
#
|
|
# Prompt-based getline using readline.
|
|
#
|
|
def pgets
|
|
if (readline_status)
|
|
$stderr.puts "ERROR: pgets called inside of thread mode: " + caller(1).to_s
|
|
return ''
|
|
end
|
|
|
|
if ((line = ::Readline.readline(prompt, true)))
|
|
HISTORY.pop if (line.empty?)
|
|
return line + "\n"
|
|
else
|
|
eof = true
|
|
return line
|
|
end
|
|
end
|
|
|
|
#
|
|
# Returns the output pipe handle
|
|
#
|
|
def fd
|
|
return $stdin if @@rl_usestdio
|
|
|
|
if (! readline_status)
|
|
$stderr.puts "fd called outside of thread mode: " + caller(1).to_s
|
|
return ''
|
|
end
|
|
|
|
@@rl_pipes[0]
|
|
end
|
|
|
|
#
|
|
# Indicates that this input medium as a shell builtin, no need
|
|
# to extend.
|
|
#
|
|
def intrinsic_shell?
|
|
true
|
|
end
|
|
|
|
#
|
|
# The prompt that is to be displayed.
|
|
#
|
|
attr_accessor :prompt
|
|
#
|
|
# The output handle to use when displaying the prompt.
|
|
#
|
|
attr_accessor :output
|
|
|
|
end
|
|
rescue LoadError
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|