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, #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:



461
462
463
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 461

def blocked
  @blocked
end

#busyObject

:nodoc:



460
461
462
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 460

def busy
  @busy
end

#dispatcher_stackObject

:nodoc:



458
459
460
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 458

def dispatcher_stack
  @dispatcher_stack
end

#tab_wordsObject

:nodoc:



459
460
461
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 459

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.



363
364
365
366
367
368
369
370
371
372
373
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 363

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



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

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)


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

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

#current_dispatcherObject

Returns the current active dispatcher



387
388
389
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 387

def current_dispatcher
	self.dispatcher_stack[0]
end

#destack_dispatcherObject

Pop a dispatcher from the front of the stacker.



355
356
357
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 355

def destack_dispatcher
	self.dispatcher_stack.shift
end

#enstack_dispatcher(dispatcher) ⇒ Object

Push a dispatcher to the front of the stack.



346
347
348
349
350
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 346

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.



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 395

def help_to_s(opts = {})
	str = ''

	dispatcher_stack.reverse.each { |dispatcher|
		# No commands?  Suckage.
		next if ((dispatcher.respond_to?('commands') == false) or
		         (dispatcher.commands == nil) or
		         (dispatcher.commands.length == 0))

		# Display the commands
		tbl = Table.new(
			'Header'  => "#{dispatcher.name} Commands",
			'Indent'  => opts['Indent'] || 4,
			'Columns' => 
				[
					'Command',
					'Description'
				],
			'ColProps' =>
				{
					'Command' =>
						{
							'MaxWidth' => 12
						}
				})

		dispatcher.commands.sort.each { |c|
			tbl << c
		}

		str << "\n" + tbl.to_s + "\n"
	}

	return str
end

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

Initialize the dispatcher shell.



162
163
164
165
166
167
168
169
170
171
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 162

def initialize(prompt, prompt_char = '>', histfile = 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.



378
379
380
381
382
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 378

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.



325
326
327
328
329
330
331
332
333
334
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 325

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.



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 275

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



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 180

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



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 255

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



201
202
203
204
205
206
207
208
209
210
211
212
213
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
241
242
243
244
245
246
247
248
249
250
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 201

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



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

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

#unknown_command(method, line) ⇒ Object

If the command is unknown…



339
340
341
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 339

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