Module: Rex::Ui::Text::DispatcherShell
- 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
-
#blocked ⇒ Object
:nodoc:.
-
#busy ⇒ Object
:nodoc:.
-
#dispatcher_stack ⇒ Object
:nodoc:.
Attributes included from Shell
#cont_flag, #cont_prompt, #disable_output, #framework, #hist_last_saved, #histfile, #input, #local_hostname, #local_username, #log_source, #name, #on_command_proc, #on_print_proc, #output, #prompt, #prompt_char, #stop_count, #stop_flag, #tab_complete_proc
Instance Method Summary collapse
-
#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.
-
#block_command(cmd) ⇒ Object
Block a specific command.
-
#blocked_command?(cmd) ⇒ Boolean
Returns nil for an empty set of blocked commands.
-
#current_dispatcher ⇒ Object
Returns the current active dispatcher.
-
#destack_dispatcher ⇒ Object
Pop a dispatcher from the front of the stacker.
-
#enstack_dispatcher(dispatcher) ⇒ Object
Push a dispatcher to the front of the stack.
-
#help_to_s(opts = {}) ⇒ Object
Return a readable version of a help banner for all of the enstacked dispatchers.
-
#initialize(prompt, prompt_char = '>', histfile = nil, framework = nil, name = nil) ⇒ Object
Initialize the dispatcher shell.
-
#remove_dispatcher(name) ⇒ Object
Removes the supplied dispatcher instance.
-
#run_command(dispatcher, method, arguments) ⇒ Object
Runs the supplied command on the given dispatcher.
-
#run_single(line, propagate_errors: false) ⇒ Boolean
Run a single command line.
-
#shellsplitex(line) ⇒ Object
Split a line as Shellwords.split would however instead of raising an ArgumentError on unbalanced quotes return the remainder of the string as if the last character were the closing quote.
-
#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.
-
#tab_complete_helper(dispatcher, str, words) ⇒ Object
Provide command-specific tab completion.
-
#tab_complete_stub(original_str, split_str) ⇒ Object
Performs tab completion of a command, if supported.
-
#unblock_command(cmd) ⇒ Object
Unblock a specific command.
-
#unknown_command(method, line) ⇒ Object
If the command is unknown…
Methods included from Shell
#_print_prompt, #format_prompt, #get_input_line, #init_tab_complete, #init_ui, #log_input, #log_output, #parse_line, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #prompt_yesno, #reset_ui, #run, #set_log_source, #stop, #stopped?, #supports_color?, #unset_log_source, #update_prompt
Methods included from Resource
Instance Attribute Details
#blocked ⇒ Object
:nodoc:
719 720 721 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 719 def blocked @blocked end |
#busy ⇒ Object
:nodoc:
718 719 720 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 718 def busy @busy end |
#dispatcher_stack ⇒ Object
:nodoc:
717 718 719 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 717 def dispatcher_stack @dispatcher_stack 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.
614 615 616 617 618 619 620 621 622 623 624 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 614 def append_dispatcher(dispatcher) inst = dispatcher.new(self) self.dispatcher_stack.each { |disp| if (disp.name == inst.name) raise "Attempting to load already loaded dispatcher #{disp.name}" end } self.dispatcher_stack.push(inst) inst end |
#block_command(cmd) ⇒ Object
Block a specific command
670 671 672 673 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 670 def block_command(cmd) self.blocked ||= {} self.blocked[cmd] = true end |
#blocked_command?(cmd) ⇒ Boolean
Returns nil for an empty set of blocked commands.
662 663 664 665 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 662 def blocked_command?(cmd) return false if not self.blocked self.blocked.has_key?(cmd) end |
#current_dispatcher ⇒ Object
Returns the current active dispatcher
638 639 640 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 638 def current_dispatcher self.dispatcher_stack[0] end |
#destack_dispatcher ⇒ Object
Pop a dispatcher from the front of the stacker.
606 607 608 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 606 def destack_dispatcher self.dispatcher_stack.shift end |
#enstack_dispatcher(dispatcher) ⇒ Object
Push a dispatcher to the front of the stack.
597 598 599 600 601 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 597 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
648 649 650 651 652 653 654 655 656 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 648 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, name = nil) ⇒ Object
Initialize the dispatcher shell.
393 394 395 396 397 398 399 400 401 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 393 def initialize(prompt, prompt_char = '>', histfile = nil, framework = nil, name = nil) super # Initialze the dispatcher array self.dispatcher_stack = [] # Initialize the tab completion array self.on_command_proc = nil end |
#remove_dispatcher(name) ⇒ Object
Removes the supplied dispatcher instance.
629 630 631 632 633 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 629 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.
575 576 577 578 579 580 581 582 583 584 585 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 575 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, propagate_errors: false) ⇒ Boolean
Run a single command line.
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 512 def run_single(line, propagate_errors: false) arguments = parse_line(line) method = arguments.shift cmd_status = nil # currently either nil or :handled, more statuses can be added in the future 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) cmd_status = :handled elsif cmd_status.nil? cmd_status = dispatcher.unknown_command(method, line) end rescue ::Interrupt cmd_status = :handled print_error("#{method}: Interrupted") raise if propagate_errors rescue OptionParser::ParseError => e print_error("#{method}: #{e.}") raise if propagate_errors rescue error = $! print_error( "Error while running command #{method}: #{$!}" + "\n\nCall stack:\n#{$@.join("\n")}") raise if propagate_errors rescue ::Exception => e error = $! print_error( "Error while running command #{method}: #{$!}") raise if propagate_errors end # If the dispatcher stack changed as a result of this command, # break out break if (dispatcher_stack.length != entries) } if (cmd_status.nil? && error == false) unknown_command(method, line) end end return cmd_status == :handled end |
#shellsplitex(line) ⇒ Object
Split a line as Shellwords.split would however instead of raising an ArgumentError on unbalanced quotes return the remainder of the string as if the last character were the closing quote.
This code was originally taken from github.com/ruby/ruby/blob/93420d34aaf8c30f11a66dd08eb186da922c831d/lib/shellwords.rb#L88
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 690 def shellsplitex(line) tokens = [] field_value = String.new field_begin = nil line.scan(/\G(\s*)(?>([^\s\\\'\"]+)|'([^\']*)'|"((?:[^\"\\]|\\.)*)"|(\\.?)|(\S))(\s|\z)?/m) do |preceding_whitespace, word, sq, dq, esc, garbage, sep| field_begin ||= Regexp.last_match.begin(0) + preceding_whitespace.length if garbage quote_start_begin = Regexp.last_match.begin(0) + preceding_whitespace.length field_quote = garbage field_value << line[quote_start_begin + 1..-1].gsub('\\\\', '\\') tokens << { begin: field_begin, value: field_value, quote: field_quote } break end field_value << (word || sq || (dq && dq.gsub(/\\([$`"\\\n])/, '\\1')) || esc.gsub(/\\(.)/, '\\1')) if sep tokens << { begin: field_begin, value: field_value, quote: ((sq && "'") || (dq && '"') || nil) } field_value = String.new field_begin = nil end end { tokens: tokens } 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
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 410 def tab_complete(str) ::Readline.completion_append_character = ' ' ::Readline.completion_case_fold = false # Check trailing whitespace so we can tell 'x' from 'x ' str_match = str.match(/[^\\]([\\]{2})*\s+$/) str_trail = (str_match.nil?) ? '' : str_match[0] # Split the line up by whitespace into words split_str = shellsplitex(str) # Append an empty token if we had trailing whitespace split_str[:tokens] << { begin: str.length, value: '' } if str_trail.length > 0 # Pop the last word and pass it to the real method result = tab_complete_stub(str, split_str) if result result.uniq else result end end |
#tab_complete_helper(dispatcher, str, words) ⇒ Object
Provide command-specific tab completion
491 492 493 494 495 496 497 498 499 500 501 502 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 491 def tab_complete_helper(dispatcher, str, words) 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? return res end # Avoid the default completion list for unknown commands [] end |
#tab_complete_stub(original_str, split_str) ⇒ Object
Performs tab completion of a command, if supported
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 435 def tab_complete_stub(original_str, split_str) *preceding_tokens, current_token = split_str[:tokens] return nil unless current_token items = [] current_word = current_token[:value] tab_words = preceding_tokens.map { |word| word[:value] } # Next, try to match internal command or value completion # Enumerate each entry in the dispatcher stack dispatcher_stack.each do |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(current_word, tab_words) else res = tab_complete_helper(dispatcher, current_word, tab_words) end if res.nil? # A nil response indicates no optional arguments return [''] if items.empty? else if res.second == :override_completions return res.first else # Otherwise we add the completion items to the list items.concat(res) end end end # Match based on the partial word matches = items.select do |word| word.downcase.start_with?(current_word.downcase) end # Prepend the preceding string of the command (or it all gets replaced!) preceding_str = original_str[0...current_token[:begin]] quote = current_token[:quote] matches_with_preceding_words_appended = matches.map do |word| word = quote.nil? ? word.gsub('\\') { '\\\\' }.gsub(' ', '\\ ') : "#{quote}#{word}#{quote}" preceding_str + word end matches_with_preceding_words_appended end |
#unblock_command(cmd) ⇒ Object
Unblock a specific command
678 679 680 681 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 678 def unblock_command(cmd) self.blocked || return self.blocked.delete(cmd) end |
#unknown_command(method, line) ⇒ Object
If the command is unknown…
590 591 592 |
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 590 def unknown_command(method, line) print_error("Unknown command: #{method}") end |