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, #reset_ui, #run, #set_log_source, #stop, #stopped?, #unset_log_source, #update_prompt

Instance Attribute Details

#blockedObject

:nodoc:



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

def blocked
  @blocked
end

#busyObject

:nodoc:



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

def busy
  @busy
end

#dispatcher_stackObject

:nodoc:



522
523
524
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 522

def dispatcher_stack
  @dispatcher_stack
end

#tab_wordsObject

:nodoc:



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

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.



452
453
454
455
456
457
458
459
460
461
462
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 452

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



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

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)


500
501
502
503
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 500

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

#current_dispatcherObject

Returns the current active dispatcher



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

def current_dispatcher
	self.dispatcher_stack[0]
end

#destack_dispatcherObject

Pop a dispatcher from the front of the stacker.



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

def destack_dispatcher
	self.dispatcher_stack.shift
end

#enstack_dispatcher(dispatcher) ⇒ Object

Push a dispatcher to the front of the stack.



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

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



486
487
488
489
490
491
492
493
494
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 486

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.



251
252
253
254
255
256
257
258
259
260
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 251

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.



467
468
469
470
471
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 467

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.



414
415
416
417
418
419
420
421
422
423
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 414

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.



364
365
366
367
368
369
370
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
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 364

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



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 269

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



344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 344

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



290
291
292
293
294
295
296
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
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 290

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

	# XXX - 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 gets replaced!)
	}.map { |e|
		tab_words.dup.push(e).join(' ')
	}
end

#unblock_command(cmd) ⇒ Object

Unblock a specific command



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

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

#unknown_command(method, line) ⇒ Object

If the command is unknown…



428
429
430
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 428

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