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

Includes:
Shell
Included in:
Msf::Ui::Console::Driver, Post::Meterpreter::Ui::Console
Defined in:
lib/rex/ui/text/dispatcher_shell.rb

Overview

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.

Defined Under Namespace

Modules: CommandDispatcher

Instance Attribute Summary collapse

Attributes included from Shell

#disable_output, #framework, #input, #on_command_proc, #on_print_proc, #output

Instance Method Summary collapse

Methods included from Shell

#init_tab_complete, #init_ui, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #reset_ui, #run, #set_log_source, #stop, #stopped?, #unset_log_source, #update_prompt

Instance Attribute Details

#blockedObject

:nodoc:


532
533
534
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 532

def blocked
  @blocked
end

#busyObject

:nodoc:


531
532
533
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 531

def busy
  @busy
end

#dispatcher_stackObject

:nodoc:


529
530
531
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 529

def dispatcher_stack
  @dispatcher_stack
end

#tab_wordsObject

:nodoc:


530
531
532
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 530

def tab_words
  @tab_words
end

Instance Method Details

#append_dispatcher(dispatcher) ⇒ Object

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


459
460
461
462
463
464
465
466
467
468
469
# 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) ⇒ Object

Block a specific command


515
516
517
518
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 515

def block_command(cmd)
  self.blocked ||= {}
  self.blocked[cmd] = true
end

#blocked_command?(cmd) ⇒ Boolean

Returns nil for an empty set of blocked commands.


507
508
509
510
# 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_dispatcherObject

Returns the current active dispatcher


483
484
485
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 483

def current_dispatcher
  self.dispatcher_stack[0]
end

#destack_dispatcherObject

Pop a dispatcher from the front of the stacker.


451
452
453
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 451

def destack_dispatcher
  self.dispatcher_stack.shift
end

#enstack_dispatcher(dispatcher) ⇒ Object

Push a dispatcher to the front of the stack.


442
443
444
445
446
# 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 = {}) ⇒ Object

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

See CommandDispatcher#help_to_s


493
494
495
496
497
498
499
500
501
# 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

#initialize(prompt, prompt_char = '>', histfile = nil, framework = nil) ⇒ Object

Initialize the dispatcher shell.


258
259
260
261
262
263
264
265
266
267
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 258

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

#remove_dispatcher(name) ⇒ Object

Removes the supplied dispatcher instance.


474
475
476
477
478
# 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) ⇒ Object

Runs the supplied command on the given dispatcher.


421
422
423
424
425
426
427
428
429
430
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 421

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
  self.busy = false
end

#run_single(line) ⇒ Object

Run a single command line.


371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 371

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#{[email protected].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) ⇒ Object

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


276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 276

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) ⇒ Object

Provide command-specific tab completion


351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 351

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) ⇒ Object

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


297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 297

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) ⇒ Object

Unblock a specific command


523
524
525
526
# 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) ⇒ Object

If the command is unknown…


435
436
437
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 435

def unknown_command(method, line)
  print_error("Unknown command: #{method}.")
end