2005-07-18 05:13:21 +00:00
|
|
|
require 'rex/ui'
|
|
|
|
|
|
|
|
module Rex
|
|
|
|
module Ui
|
|
|
|
module Text
|
|
|
|
|
|
|
|
###
|
|
|
|
#
|
|
|
|
# The dispatcher shell class is designed to provide a generic means
|
|
|
|
# of processing various shell commands that may be located in
|
|
|
|
# different modules or chunks of codes. These chunks are referred
|
|
|
|
# to as command dispatchers. The only requirement for command dispatchers is
|
|
|
|
# that they prefix every method that they wish to be mirrored as a command
|
|
|
|
# with the cmd_ prefix.
|
|
|
|
#
|
|
|
|
###
|
|
|
|
module DispatcherShell
|
|
|
|
|
|
|
|
###
|
|
|
|
#
|
2005-11-15 05:22:13 +00:00
|
|
|
# Empty template base class for command dispatchers.
|
2005-07-18 05:13:21 +00:00
|
|
|
#
|
|
|
|
###
|
|
|
|
module CommandDispatcher
|
|
|
|
|
2005-11-15 05:22:13 +00:00
|
|
|
#
|
|
|
|
# Initializes the command dispatcher mixin.
|
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def initialize(shell)
|
|
|
|
self.shell = shell
|
|
|
|
self.tab_complete_items = []
|
|
|
|
end
|
|
|
|
|
2005-11-15 05:22:13 +00:00
|
|
|
#
|
|
|
|
# Returns nil for an empty set of commands.
|
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def commands
|
|
|
|
end
|
2005-11-15 05:22:13 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# Wrapps shell.print_error
|
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def print_error(msg = '')
|
|
|
|
shell.print_error(msg)
|
|
|
|
end
|
|
|
|
|
2005-11-15 05:22:13 +00:00
|
|
|
#
|
|
|
|
# Wrapps shell.print_status
|
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def print_status(msg = '')
|
|
|
|
shell.print_status(msg)
|
|
|
|
end
|
|
|
|
|
2005-11-15 05:22:13 +00:00
|
|
|
#
|
|
|
|
# Wrapps shell.print_line
|
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def print_line(msg = '')
|
|
|
|
shell.print_line(msg)
|
|
|
|
end
|
|
|
|
|
2005-11-15 05:22:13 +00:00
|
|
|
#
|
|
|
|
# Wrapps shell.print
|
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def print(msg = '')
|
|
|
|
shell.print(msg)
|
|
|
|
end
|
|
|
|
|
2005-11-15 05:22:13 +00:00
|
|
|
#
|
|
|
|
# Wraps shell.update_prompt
|
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def update_prompt(prompt)
|
|
|
|
shell.update_prompt(prompt)
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# No tab completion items by default
|
|
|
|
#
|
|
|
|
attr_accessor :shell, :tab_complete_items
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
2005-11-15 05:22:13 +00:00
|
|
|
# DispatcherShell derives from shell.
|
2005-07-18 05:13:21 +00:00
|
|
|
#
|
|
|
|
include Shell
|
|
|
|
|
|
|
|
#
|
2005-11-15 05:22:13 +00:00
|
|
|
# Initialize the dispatcher shell.
|
2005-07-18 05:13:21 +00:00
|
|
|
#
|
|
|
|
def initialize(prompt, prompt_char = '>')
|
|
|
|
super
|
|
|
|
|
|
|
|
self.dispatcher_stack = []
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
2005-11-15 05:22:13 +00:00
|
|
|
# Performs tab completion on shell input if supported.
|
2005-07-18 05:13:21 +00:00
|
|
|
#
|
|
|
|
def tab_complete(str)
|
|
|
|
items = []
|
|
|
|
|
|
|
|
# Next, try to match internal command or value completion
|
|
|
|
# Enumerate each entry in the dispatcher stack
|
|
|
|
dispatcher_stack.each { |dispatcher|
|
|
|
|
# If it supports commands, query them all
|
|
|
|
if (dispatcher.respond_to?('commands'))
|
|
|
|
items.concat(dispatcher.commands.to_a.map { |x| x[0] })
|
|
|
|
end
|
|
|
|
|
|
|
|
# If the dispatcher has custom tab completion items, use them
|
|
|
|
items.concat(dispatcher.tab_complete_items || [])
|
|
|
|
}
|
|
|
|
|
|
|
|
items.find_all { |e|
|
|
|
|
e =~ /^#{str}/
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
2005-11-15 05:22:13 +00:00
|
|
|
# Run a single command line.
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def run_single(line)
|
|
|
|
arguments = parse_line(line)
|
|
|
|
method = arguments.shift
|
|
|
|
found = false
|
|
|
|
|
|
|
|
reset_color if (supports_color?)
|
|
|
|
|
|
|
|
if (method)
|
|
|
|
entries = dispatcher_stack.length
|
|
|
|
|
|
|
|
dispatcher_stack.each { |dispatcher|
|
|
|
|
begin
|
|
|
|
if (dispatcher.respond_to?('cmd_' + method))
|
2005-07-19 04:21:15 +00:00
|
|
|
run_command(dispatcher, method, arguments)
|
|
|
|
|
2005-07-18 07:46:54 +00:00
|
|
|
found = true
|
2005-07-18 05:13:21 +00:00
|
|
|
end
|
|
|
|
rescue
|
2005-07-18 07:46:54 +00:00
|
|
|
output.print_error(
|
|
|
|
"Error while running command #{method}: #{$!}" +
|
|
|
|
"\n\nCall stack:\n#{$@.join("\n")}")
|
2005-07-18 05:13:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# If the dispatcher stack changed as a result of this command,
|
|
|
|
# break out
|
|
|
|
break if (dispatcher_stack.length != entries)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found == false)
|
|
|
|
unknown_command(method, line)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return found
|
|
|
|
end
|
|
|
|
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
|
|
|
# Runs the supplied command on the given dispatcher.
|
|
|
|
#
|
|
|
|
def run_command(dispatcher, method, arguments)
|
|
|
|
eval("dispatcher.#{'cmd_' + method}(*arguments)")
|
|
|
|
end
|
|
|
|
|
2005-07-18 05:13:21 +00:00
|
|
|
#
|
|
|
|
# If the command is unknown...
|
|
|
|
#
|
|
|
|
def unknown_command(method, line)
|
|
|
|
output.print_error("Unknown command: #{method}.")
|
|
|
|
end
|
|
|
|
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
2005-11-15 05:22:13 +00:00
|
|
|
# Push a dispatcher to the front of the stack.
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def enstack_dispatcher(dispatcher)
|
2005-11-23 14:34:11 +00:00
|
|
|
self.dispatcher_stack.unshift(inst = dispatcher.new(self))
|
|
|
|
|
|
|
|
inst
|
2005-07-18 05:13:21 +00:00
|
|
|
end
|
|
|
|
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
2005-11-15 05:22:13 +00:00
|
|
|
# Pop a dispatcher from the front of the stacker.
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
2005-07-18 05:13:21 +00:00
|
|
|
def destack_dispatcher
|
|
|
|
self.dispatcher_stack.shift
|
|
|
|
end
|
|
|
|
|
2005-11-23 14:34:11 +00:00
|
|
|
#
|
|
|
|
# Adds the supplied dispatcher to the end of the dispatcher stack so that
|
|
|
|
# it doesn't affect any enstack'd dispatchers.
|
|
|
|
#
|
|
|
|
def append_dispatcher(dispatcher)
|
|
|
|
self.dispatcher_stack.push(inst = dispatcher.new(self))
|
|
|
|
|
|
|
|
inst
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Removes the supplied dispatcher instance.
|
|
|
|
#
|
|
|
|
def remove_dispatcher(name)
|
|
|
|
self.dispatcher_stack.delete_if { |inst|
|
|
|
|
(inst.name == name)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Returns the current active dispatcher
|
|
|
|
#
|
|
|
|
def current_dispatcher
|
|
|
|
self.dispatcher_stack[0]
|
|
|
|
end
|
|
|
|
|
2005-07-19 04:21:15 +00:00
|
|
|
#
|
|
|
|
# Return a readable version of a help banner for all of the enstacked
|
|
|
|
# dispatchers.
|
|
|
|
#
|
|
|
|
def help_to_s(opts = {})
|
|
|
|
str = ''
|
|
|
|
|
|
|
|
dispatcher_stack.reverse.each { |dispatcher|
|
|
|
|
# No commands? Suckage.
|
|
|
|
next if ((dispatcher.respond_to?('commands') == false) or
|
2005-10-17 00:25:07 +00:00
|
|
|
(dispatcher.commands == nil) or
|
2005-07-19 04:21:15 +00:00
|
|
|
(dispatcher.commands.length == 0))
|
|
|
|
|
|
|
|
# Display the commands
|
|
|
|
tbl = Table.new(
|
|
|
|
'Header' => "#{dispatcher.name} Commands",
|
|
|
|
'Indent' => opts['Indent'] || 4,
|
|
|
|
'Columns' =>
|
|
|
|
[
|
|
|
|
'Command',
|
|
|
|
'Description'
|
|
|
|
],
|
|
|
|
'ColProps' =>
|
|
|
|
{
|
|
|
|
'Command' =>
|
|
|
|
{
|
|
|
|
'MaxWidth' => 12
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
dispatcher.commands.sort.each { |c|
|
|
|
|
tbl << c
|
|
|
|
}
|
|
|
|
|
|
|
|
str += "\n" + tbl.to_s + "\n"
|
|
|
|
}
|
|
|
|
|
|
|
|
return str
|
|
|
|
end
|
2005-07-18 05:13:21 +00:00
|
|
|
|
|
|
|
|
2005-11-15 05:22:13 +00:00
|
|
|
attr_accessor :dispatcher_stack # :nodoc:
|
2005-07-18 05:13:21 +00:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|