class MigrateCredData < ActiveRecord::Migration

	def self.up
		begin # Wrap the whole thing in a giant rescue.
		skipped_notes = []
		new_creds = []
		Msf::DBManager::Note.find(:all).each do |note|
			next unless note.ntype[/^auth\.(.*)/]
			service_name = $1
			if !service_name
				skipped_notes << note
				next
			end
			if note.host and note.host.respond_to?(:address)
				if note.service
					svc_id = note.service.id
				else
					candidate_services = []
					note.host.services.each do |service|
						if service.name == service_name
							candidate_services << service
						end
					end
					# Use the default port, or the first port that matches the protocol name.
					default_port = case service_name.downcase
						when 'ftp'; 21
						when /^smb/; 445
						when /^imap/; 143
						when 'telnet'; 23
						when 'pop3'; 110
						when 'http','domino','axis','wordpress','tomcat'; 80
						when 'tns'; 1521
						when 'snmp'; 161
						when 'mssql'; 1433
						when 'ssh'; 22
						when 'https'; 443
						when 'mysql'; 3306
						when 'db2'; 50000
						when 'postgres'; 5432
						else nil
					end
					if !default_port
						skipped_notes << note
						next
					end
					if candidate_services.size == 1
						svc_id = candidate_services.first.id
					elsif candidate_services.empty?
						Msf::DBManager::Service.new do |svc|
							svc.host_id = note.host.id
							svc.port = default_port
							svc.proto = 'tcp'
							svc.state = 'open'
							svc.name = service_name.downcase
							svc.save!
							svc_id = svc.id
						end
					elsif candidate_services.size > 1
						svc_ports = candidate_services.map{|s| s.port}
						if svc_ports.index(default_port)
							svc_id = candidate_services[svc_ports.index(default_port)].id
						else
							svc_id = candidate_services.first.id
						end
					end
				end
			else
				skipped_notes << note
				next
			end
			if note.data[:hash]
				ptype = 'smb_hash'
				pass = note.data[:hash]
			elsif note.data[:ssh_key]
				ptype = 'ssh_key'
				pass = note.data[:extra]
			else
				ptype = 'password'
				pass = note.data[:pass]
			end
			# Format domains and databases into the usernames.
			if note.ntype == "auth.smb_challenge"
				domain = note.data[:extra].match(/DOMAIN=([^\s]+)/)[1]
				if domain
					user = [domain, note.data[:user]].join("/")
				else
					user = note.data[:user]
				end
			elsif note.ntype =~ /auth\.(postgres|db2)/
				if note.data[:database]
					user = [note.data[:database], note.data[:user]].join("/")
				else
					user = note.data[:user]
				end
			else
				user = note.data[:user]
			end
			# Not actually a credentials, convert to migrated notes
			if service_name == 'smb' && note.data[:token]
				skipped_notes << note
				next
			end
			if service_name == 'tns' && note.data[:type] == "bruteforced_sid"
				skipped_notes << note
				next
			end
			# Special case for the bizarre reporting for aux/admin/oracle/oracle_login
			if service_name == 'tns' && note.data[:type] == "bruteforced_account"
				note.data[:data] =~ /([^\x2f]+)\x2f([^\s]+).*with sid (.*)/
				user = "#{$3}/#{$1}"
				pass = $2
			end
			new_creds << [svc_id, ptype, user, pass]
		end

		say "Migrating #{new_creds.size} credentials."
		new_creds.uniq.each do |note|
			Msf::DBManager::Cred.new do |cred|
				cred.service_id = note[0]
				cred.user = note[2]
				cred.pass = note[3]
				cred.ptype = note[1]
				cred.save!
			end
		end

		say "Migrating #{skipped_notes.size} notes."
		skipped_notes.uniq.each do |note|
			Msf::DBManager::Note.new do |new_note|
				new_note.host_id = note.host_id
				new_note.ntype = "migrated_auth"
				new_note.data = note.data.merge(:migrated_auth_type => note.ntype)
				new_note.save!
			end
		end

		say "Deleting migrated auth notes."
		Msf::DBManager::Note.find(:all).each do |note|
			next unless note.ntype[/^auth\.(.*)/]
			note.delete
		end
		rescue 
			say "There was a problem migrating auth credentials. Skipping."
			return true # Never fail!
		end
	end


	def self.down
		raise ActiveRecord::IrreversibleMigration
	end

end

