Module: Rex::Ui::Text::DispatcherShell::CommandDispatcher

Overview

Empty template base class for command dispatchers.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#shellObject

No tab completion items by default


255
256
257
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 255

def shell
  @shell
end

#tab_complete_itemsObject

No tab completion items by default


255
256
257
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 255

def tab_complete_items
  @tab_complete_items
end

Instance Method Details

#cmd_help(cmd = nil, *ignored) ⇒ Object Also known as: cmd_?

Displays the help banner. With no arguments, this is just a list of all commands grouped by dispatcher. Otherwise, tries to use a method named cmd_#<code>cmd</code>_help for the first dispatcher that has a command named cmd. If no such method exists, uses cmd as a regex to compare against each enstacked dispatcher's name and dumps commands of any that match.


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 149

def cmd_help(cmd=nil, *ignored)
  if cmd
    help_found = false
    cmd_found = false
    shell.dispatcher_stack.each do |dispatcher|
      next unless dispatcher.respond_to?(:commands)
      next if (dispatcher.commands.nil?)
      next if (dispatcher.commands.length == 0)

      if dispatcher.respond_to?("cmd_#{cmd}", true)
        cmd_found = true
        break unless dispatcher.respond_to?("cmd_#{cmd}_help", true)
        dispatcher.send("cmd_#{cmd}_help")
        help_found = true
        break
      end
    end

    unless cmd_found
      # We didn't find a cmd, try it as a dispatcher name
      shell.dispatcher_stack.each do |dispatcher|
        if dispatcher.name =~ /#{cmd}/i
          print_line(dispatcher.help_to_s)
          cmd_found = help_found = true
        end
      end
    end

    if docs_dir && File.exist?(File.join(docs_dir, cmd + '.md'))
      print_line
      print(File.read(File.join(docs_dir, cmd + '.md')))
    end
    print_error("No help for #{cmd}, try -h") if cmd_found and not help_found
    print_error("No such command") if not cmd_found
  else
    print(shell.help_to_s)
    if docs_dir && File.exist?(File.join(docs_dir + '.md'))
      print_line
      print(File.read(File.join(docs_dir + '.md')))
    end
  end
end

#cmd_help_helpObject


137
138
139
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 137

def cmd_help_help
  print_line "There's only so much I can do"
end

#cmd_help_tabs(str, words) ⇒ Object

Tab completion for the help command

By default just returns a list of all commands in all dispatchers.


197
198
199
200
201
202
203
204
205
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 197

def cmd_help_tabs(str, words)
  return [] if words.length > 1

  tabs = []
  shell.dispatcher_stack.each { |dispatcher|
    tabs += dispatcher.commands.keys
  }
  return tabs
end

#commandsObject

Returns nil for an empty set of commands.

This method should be overridden to return a Hash with command names for keys and brief help text for values.


45
46
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 45

def commands
end

#deprecated_cmd(method = nil, *args) ⇒ Object

Print a warning that the called command is deprecated and optionally forward to the replacement method (useful for when commands are renamed).


108
109
110
111
112
113
114
115
116
117
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 108

def deprecated_cmd(method=nil, *args)
  cmd = caller[0].match(/`cmd_(.*)'/)[1]
  print_error "The #{cmd} command is DEPRECATED"
  if cmd == "db_autopwn"
    print_error "See http://r-7.co/xY65Zr instead"
  elsif method and self.respond_to?("cmd_#{method}", true)
    print_error "Use #{method} instead"
    self.send("cmd_#{method}", *args)
  end
end

#deprecated_commandsObject

Returns an empty set of commands.

This method should be overridden if the dispatcher has commands that should be treated as deprecated. Deprecated commands will not show up in help and will not tab-complete, but will still be callable.


55
56
57
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 55

def deprecated_commands
  []
end

#deprecated_help(method = nil) ⇒ Object


119
120
121
122
123
124
125
126
127
128
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 119

def deprecated_help(method=nil)
  cmd = caller[0].match(/`cmd_(.*)_help'/)[1]
  print_error "The #{cmd} command is DEPRECATED"
  if cmd == "db_autopwn"
    print_error "See http://r-7.co/xY65Zr instead"
  elsif method and self.respond_to?("cmd_#{method}_help", true)
    print_error "Use 'help #{method}' instead"
    self.send("cmd_#{method}_help")
  end
end

#docs_dirObject

Return the subdir of the `documentation/` directory that should be used to find usage documentation

TODO: get this value from somewhere that doesn't invert a bunch of dependencies


248
249
250
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 248

def docs_dir
  File.expand_path(File.join(__FILE__, '..', '..', '..', '..', '..', 'documentation', 'cli'))
end

#help_to_s(opts = {}) ⇒ Object

Return a pretty, user-readable table of commands provided by this dispatcher.


213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 213

def help_to_s(opts={})
  # If this dispatcher has no commands, we can't do anything useful.
  return "" if commands.nil? or commands.length == 0

  # Display the commands
  tbl = Rex::Text::Table.new(
    'Header'  => "#{self.name} Commands",
    'Indent'  => opts['Indent'] || 4,
    'Columns' =>
      [
        'Command',
        'Description'
      ],
    'ColProps' =>
      {
        'Command' =>
          {
            'MaxWidth' => 12
          }
      })

  commands.sort.each { |c|
    tbl << c
  }

  return "\n" + tbl.to_s + "\n"
end

#initialize(shell) ⇒ Object

Initializes the command dispatcher mixin.


34
35
36
37
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 34

def initialize(shell)
  self.shell = shell
  self.tab_complete_items = []
end

Wraps shell.print


99
100
101
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 99

def print(msg = '')
  shell.print(msg)
end

Wraps shell.print_error


62
63
64
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 62

def print_error(msg = '')
  shell.print_error(msg)
end

Wraps shell.print_good


85
86
87
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 85

def print_good(msg = '')
  shell.print_good(msg)
end

Wraps shell.print_line


78
79
80
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 78

def print_line(msg = '')
  shell.print_line(msg)
end

Wraps shell.print_status


71
72
73
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 71

def print_status(msg = '')
  shell.print_status(msg)
end

Wraps shell.print_warning


92
93
94
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 92

def print_warning(msg = '')
  shell.print_warning(msg)
end

#tab_complete_directory(str, words) ⇒ Object

Return a list of possible directory for tab completion.


276
277
278
279
280
281
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 276

def tab_complete_directory(str, words)
  str = '.' + ::File::SEPARATOR if str.empty?
  dirs = Dir.glob(str.concat('*'), File::FNM_CASEFOLD).select { |x| File.directory?(x) }

  dirs
end

#tab_complete_filenames(str, words) ⇒ Object

Provide a generic tab completion for file names.

If the only completion is a directory, this descends into that directory and continues completions with filenames contained within.


263
264
265
266
267
268
269
270
271
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 263

def tab_complete_filenames(str, words)
  matches = ::Readline::FILENAME_COMPLETION_PROC.call(str)
  if matches and matches.length == 1 and File.directory?(matches[0])
    dir = matches[0]
    dir += File::SEPARATOR if dir[-1,1] != File::SEPARATOR
    matches = ::Readline::FILENAME_COMPLETION_PROC.call(dir)
  end
  matches.nil? ? [] : matches
end

#tab_complete_generic(fmt, str, words) ⇒ Object

Provide a generic tab completion function based on the specification pass as fmt. The fmt argument in a hash where values are an array defining how the command should be completed. The first element of the array can be one of:

nil      - This argument is a flag and takes no option.
true     - This argument takes an option with no suggestions.
:address - This option is a source address.
:bool    - This option is a boolean.
:file    - This option is a file path.
Array    - This option is an array of possible values.

295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 295

def tab_complete_generic(fmt, str, words)
  last_word = words[-1]
  fmt = fmt.select { |key, value| last_word == key || !words.include?(key) }

  val = fmt[last_word]
  return fmt.keys if !val  # the last word does not look like a fmtspec
  arg = val[0]
  return fmt.keys if !arg  # the last word is a fmtspec that takes no argument

  tabs = []
  if arg.to_s.to_sym == :address
    tabs = tab_complete_source_address
  elsif arg.to_s.to_sym == :bool
    tabs = ['true', 'false']
  elsif arg.to_s.to_sym == :file
    tabs = tab_complete_filenames(str, words)
  elsif arg.kind_of?(Array)
    tabs = arg.map {|a| a.to_s}
  end
  tabs
end

#tab_complete_source_addressObject

Return a list of possible source addresses for tab completion.


320
321
322
323
324
325
326
327
328
329
330
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 320

def tab_complete_source_address
  addresses = [Rex::Socket.source_address]
  # getifaddrs was introduced in 2.1.2
  if ::Socket.respond_to?(:getifaddrs)
    ifaddrs = ::Socket.getifaddrs.select do |ifaddr|
      ifaddr.addr && ifaddr.addr.ip?
    end
    addresses += ifaddrs.map { |ifaddr| ifaddr.addr.ip_address }
  end
  addresses
end

#unknown_command(method, line) ⇒ Symbol?

A callback that can be used to handle unknown commands. This can for example, allow a dispatcher to mark a command as being disabled.

Returns:

  • (Symbol, nil)

    Returns a symbol specifying the action that was taken by the handler or `nil` if no action was taken. The only supported action at this time is `:handled`, signifying that the unknown command was handled by this dispatcher and no additional dispatchers should receive it.


339
340
341
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 339

def unknown_command(method, line)
  nil
end

#update_prompt(*args) ⇒ Object

Wraps shell.update_prompt


133
134
135
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 133

def update_prompt(*args)
  shell.update_prompt(*args)
end