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

Includes:
Shell
Included in:
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:



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

def blocked
  @blocked
end

#busyObject

:nodoc:



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

def busy
  @busy
end

#dispatcher_stackObject

:nodoc:



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

def dispatcher_stack
  @dispatcher_stack
end

#tab_wordsObject

:nodoc:



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

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.



460
461
462
463
464
465
466
467
468
469
470
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 460

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



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

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

#blocked_command?(cmd) ⇒ Boolean

Returns nil for an empty set of blocked commands.

Returns:

  • (Boolean)


508
509
510
511
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 508

def blocked_command?(cmd)
  return false if not self.blocked
  self.blocked.has_key?(cmd)
end

#current_dispatcherObject

Returns the current active dispatcher



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

def current_dispatcher
  self.dispatcher_stack[0]
end

#destack_dispatcherObject

Pop a dispatcher from the front of the stacker.



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

def destack_dispatcher
  self.dispatcher_stack.shift
end

#enstack_dispatcher(dispatcher) ⇒ Object

Push a dispatcher to the front of the stack.



443
444
445
446
447
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 443

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



494
495
496
497
498
499
500
501
502
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 494

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.



475
476
477
478
479
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 475

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
431
# 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
ensure
  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#{$@.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



524
525
526
527
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 524

def unblock_command(cmd)
  self.blocked || return
  self.blocked.delete(cmd)
end

#unknown_command(method, line) ⇒ Object

If the command is unknown…



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

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