# -*- coding: binary -*-
module Msf
module Simple

###
#
# A simplified post-exploitation module wrapper.
#
###
module Post

	include Module

	#
	# Wraps the post-exploitation module running process in a simple single
	# method.  The options hash can have the following values passed in it:
	#
	# OptionStr
	#
	# 	A string of comma separated option values that should be imported into
	# 	the datastore.
	#
	# Options
	#
	# 	A hash of values to be imported directly into the datastore.
	#
	# LocalInput
	#
	# 	The local input handle that data can be read in from.
	#
	# LocalOutput
	#
	# 	The local output through which data can be displayed.
	#
	# RunAsJob
	#
	# 	Whether or not the module should be run in the context of a background
	# 	job.
	#
	def self.run_simple(omod, opts = {}, &block)

		# Clone the module to prevent changes to the original instance
		mod = omod.replicant
		Msf::Simple::Framework.simplify_module( mod, false )
		yield(mod) if block_given?

		# Import options from the OptionStr or Option hash.
		mod._import_extra_options(opts)

		# Verify the options
		mod.options.validate(mod.datastore)

		# Initialize user interaction
		if ! opts['Quiet']
			mod.init_ui(opts['LocalInput'] || mod.user_input, opts['LocalOutput'] || mod.user_output)
		else
			mod.init_ui(nil, nil)
		end

		#
		# Disable this until we can test background stuff a little better
		#
		if(mod.passive? or opts['RunAsJob'])
			ctx = [ mod.replicant ]
			mod.job_id = mod.framework.jobs.start_bg_job(
				"Post: #{mod.refname}",
				ctx,
				Proc.new { |ctx_| self.job_run_proc(ctx_) },
				Proc.new { |ctx_| self.job_cleanup_proc(ctx_) }
			)
			# Propagate this back to the caller for console mgmt
			omod.job_id = mod.job_id
		else
			ctx = [ mod ]
			self.job_run_proc(ctx)
			self.job_cleanup_proc(ctx)
		end
	end

	#
	# Calls the class method.
	#
	def run_simple(opts = {}, &block)
		Msf::Simple::Post.run_simple(self, opts, &block)
	end

protected

	#
	# Job run proc, sets up the module and kicks it off.
	#
	# XXX: Mostly Copy/pasted from simple/auxiliarly.rb
	#
	def self.job_run_proc(ctx)
		mod = ctx[0]
		begin
			mod.setup
			mod.framework.events.on_module_run(mod)
			# Grab the session object since we need to fire an event for not
			# only the normal module_run event that all module types have to
			# report, but a specific event for sessions as well.
			s = mod.framework.sessions[mod.datastore["SESSION"]]
			mod.framework.events.on_session_module_run(s, mod)
			mod.run
		rescue ::Timeout::Error => e
			mod.error = e
			mod.print_error("Post triggered a timeout exception")
			mod.cleanup
			return
		rescue ::Interrupt => e
			mod.error = e
			mod.print_error("Post interrupted by the console user")
			mod.cleanup
			return
		rescue ::Exception => e
			mod.error = e
			mod.print_error("Post failed: #{e.class} #{e}")
			if(e.class.to_s != 'Msf::OptionValidateError')
				mod.print_error("Call stack:")
				e.backtrace.each do |line|
					break if line =~ /lib.msf.base.simple.post.rb/
					mod.print_error("  #{line}")
				end
			end

			elog("Post failed: #{e.class} #{e}", 'core', LEV_0)
			dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_3)

			mod.cleanup

			return
		end
	end

	#
	# Clean up the module after the job completes.
	#
	# Copy/pasted from simple/auxiliarly.rb
	#
	def self.job_cleanup_proc(ctx)
		mod = ctx[0]
		mod.framework.events.on_module_complete(mod)
		# Allow the exploit to cleanup after itself, that messy bugger.
		mod.cleanup
	end

end

end
end

