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


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

def shell
  @shell
end

#tab_complete_itemsObject

No tab completion items by default


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

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.


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
191
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 150

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


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

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.


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

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.


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

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).


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

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.


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

def deprecated_commands
  []
end

#deprecated_help(method = nil) ⇒ Object


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

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


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

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.


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
240
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 214

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.


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

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

Wraps shell.print


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

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

Wraps shell.print_error


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

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

Wraps shell.print_good


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

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

Wraps shell.print_line


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

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

Wraps shell.print_status


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

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

Wraps shell.print_warning


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

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

#tab_complete_directory(str, words) ⇒ Object

Return a list of possible directory for tab completion.


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

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.


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

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
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.

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

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.


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

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

#update_prompt(*args) ⇒ Object

Wraps shell.update_prompt


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

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