module Rex::Ui::Text::DispatcherShell

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.

Public Class Methods

new(prompt, prompt_char = '>', histfile = nil, framework = nil) click to toggle source

Initialize the dispatcher shell.

Calls superclass method Rex::Ui::Text::Shell::new
# File lib/rex/ui/text/dispatcher_shell.rb, line 257
def initialize(prompt, prompt_char = '>', histfile = nil, framework = nil)
  super

  # Initialze the dispatcher array
  self.dispatcher_stack = []

  # Initialize the tab completion array
  self.tab_words = []
  self.on_command_proc = nil
end

Public Instance Methods

append_dispatcher(dispatcher) click to toggle source

Adds the supplied dispatcher to the end of the dispatcher stack so that it doesn't affect any enstack'd dispatchers.

# File lib/rex/ui/text/dispatcher_shell.rb, line 459
def append_dispatcher(dispatcher)
  inst = dispatcher.new(self)
  self.dispatcher_stack.each { |disp|
    if (disp.name == inst.name)
      raise RuntimeError.new("Attempting to load already loaded dispatcher #{disp.name}")
    end
  }
  self.dispatcher_stack.push(inst)

  inst
end
block_command(cmd) click to toggle source

Block a specific command

# File lib/rex/ui/text/dispatcher_shell.rb, line 515
def block_command(cmd)
  self.blocked ||= {}
  self.blocked[cmd] = true
end
blocked_command?(cmd) click to toggle source

Returns nil for an empty set of blocked commands.

# File lib/rex/ui/text/dispatcher_shell.rb, line 507
def blocked_command?(cmd)
  return false if not self.blocked
  self.blocked.has_key?(cmd)
end
current_dispatcher() click to toggle source

Returns the current active dispatcher

# File lib/rex/ui/text/dispatcher_shell.rb, line 483
def current_dispatcher
  self.dispatcher_stack[0]
end
destack_dispatcher() click to toggle source

Pop a dispatcher from the front of the stacker.

# File lib/rex/ui/text/dispatcher_shell.rb, line 451
def destack_dispatcher
  self.dispatcher_stack.shift
end
enstack_dispatcher(dispatcher) click to toggle source

Push a dispatcher to the front of the stack.

# File lib/rex/ui/text/dispatcher_shell.rb, line 442
def enstack_dispatcher(dispatcher)
  self.dispatcher_stack.unshift(inst = dispatcher.new(self))

  inst
end
help_to_s(opts = {}) click to toggle source

Return a readable version of a help banner for all of the enstacked dispatchers.

See +CommandDispatcher#help_to_s+

# File lib/rex/ui/text/dispatcher_shell.rb, line 493
def help_to_s(opts = {})
  str = ''

  dispatcher_stack.reverse.each { |dispatcher|
    str << dispatcher.help_to_s
  }

  return str
end
remove_dispatcher(name) click to toggle source

Removes the supplied dispatcher instance.

# File lib/rex/ui/text/dispatcher_shell.rb, line 474
def remove_dispatcher(name)
  self.dispatcher_stack.delete_if { |inst|
    (inst.name == name)
  }
end
run_command(dispatcher, method, arguments) click to toggle source

Runs the supplied command on the given dispatcher.

# File lib/rex/ui/text/dispatcher_shell.rb, line 420
def run_command(dispatcher, method, arguments)
  self.busy = true

  if(blocked_command?(method))
    print_error("The #{method} command has been disabled.")
  else
    dispatcher.send('cmd_' + method, *arguments)
  end
ensure
  self.busy = false
end
run_single(line) click to toggle source

Run a single command line.

# File lib/rex/ui/text/dispatcher_shell.rb, line 370
def run_single(line)
  arguments = parse_line(line)
  method    = arguments.shift
  found     = false
  error     = false

  # If output is disabled output will be nil
  output.reset_color if (output)

  if (method)
    entries = dispatcher_stack.length

    dispatcher_stack.each { |dispatcher|
      next if not dispatcher.respond_to?('commands')

      begin
        if (dispatcher.commands.has_key?(method) or dispatcher.deprecated_commands.include?(method))
          self.on_command_proc.call(line.strip) if self.on_command_proc
          run_command(dispatcher, method, arguments)
          found = true
        end
      rescue
        error = $!

        print_error(
          "Error while running command #{method}: #{$!}" +
          "\n\nCall stack:\n#{$@.join("\n")}")
      rescue ::Exception
        error = $!

        print_error(
          "Error while running command #{method}: #{$!}")
      end

      # If the dispatcher stack changed as a result of this command,
      # break out
      break if (dispatcher_stack.length != entries)
    }

    if (found == false and error == false)
      unknown_command(method, line)
    end
  end

  return found
end
tab_complete(str) click to toggle source

This method accepts the entire line of text from the Readline routine, stores all completed words, and passes the partial word to the real tab completion function. This works around a design problem in the Readline module and depends on the Readline.basic_word_break_characters variable being set to x00

# File lib/rex/ui/text/dispatcher_shell.rb, line 275
def tab_complete(str)
  # Check trailing whitespace so we can tell 'x' from 'x '
  str_match = str.match(/\s+$/)
  str_trail = (str_match.nil?) ? '' : str_match[0]

  # Split the line up by whitespace into words
  str_words = str.split(/[\s\t\n]+/)

  # Append an empty word if we had trailing whitespace
  str_words << '' if str_trail.length > 0

  # Place the word list into an instance variable
  self.tab_words = str_words

  # Pop the last word and pass it to the real method
  tab_complete_stub(self.tab_words.pop)
end
tab_complete_helper(dispatcher, str, words) click to toggle source

Provide command-specific tab completion

# File lib/rex/ui/text/dispatcher_shell.rb, line 350
def tab_complete_helper(dispatcher, str, words)
  items = []

  tabs_meth = "cmd_#{words[0]}_tabs"
  # Is the user trying to tab complete one of our commands?
  if (dispatcher.commands.include?(words[0]) and dispatcher.respond_to?(tabs_meth))
    res = dispatcher.send(tabs_meth, str, words)
    return [] if res.nil?
    items.concat(res)
  else
    # Avoid the default completion list for known commands
    return []
  end

  return items
end
tab_complete_stub(str) click to toggle source

Performs tab completion of a command, if supported Current words can be found in self.tab_words

# File lib/rex/ui/text/dispatcher_shell.rb, line 296
def tab_complete_stub(str)
  items = []

  return nil if not str

  # puts "Words(#{tab_words.join(", ")}) Partial='#{str}'"

  # Next, try to match internal command or value completion
  # Enumerate each entry in the dispatcher stack
  dispatcher_stack.each { |dispatcher|

    # If no command is set and it supports commands, add them all
    if (tab_words.empty? and dispatcher.respond_to?('commands'))
      items.concat(dispatcher.commands.keys)
    end

    # If the dispatcher exports a tab completion function, use it
    if(dispatcher.respond_to?('tab_complete_helper'))
      res = dispatcher.tab_complete_helper(str, tab_words)
    else
      res = tab_complete_helper(dispatcher, str, tab_words)
    end

    if (res.nil?)
      # A nil response indicates no optional arguments
      return [''] if items.empty?
    else
      # Otherwise we add the completion items to the list
      items.concat(res)
    end
  }

  # Verify that our search string is a valid regex
  begin
    Regexp.compile(str)
  rescue RegexpError
    str = Regexp.escape(str)
  end

  # @todo - This still doesn't fix some Regexp warnings:
  # ./lib/rex/ui/text/dispatcher_shell.rb:171: warning: regexp has `]' without escape

  # Match based on the partial word
  items.find_all { |e|
    e =~ /^#{str}/
  # Prepend the rest of the command (or it all gets replaced!)
  }.map { |e|
    tab_words.dup.push(e).join(' ')
  }
end
unblock_command(cmd) click to toggle source

Unblock a specific command

# File lib/rex/ui/text/dispatcher_shell.rb, line 523
def unblock_command(cmd)
  self.blocked || return
  self.blocked.delete(cmd)
end
unknown_command(method, line) click to toggle source

If the command is unknown…

# File lib/rex/ui/text/dispatcher_shell.rb, line 435
def unknown_command(method, line)
  print_error("Unknown command: #{method}.")
end