require "bundler/gem_tasks"
require 'openssl'
require 'metasploit-payloads/crypto'

c_source = "../c/meterpreter/"
java_source = "../java"
php_source = "../php/meterpreter/"
python_source = "../python/meterpreter/"
dest = "./data"
meterpreter_dest = "./data/meterpreter"
android_dest = "./data/android"
java_dest = "./data/java"
manifest_file = './manifest'
manifest_uuid_file = './manifest.uuid'
manifest_hash_type = 'SHA3-256'

platform_config = {
  :windows => {
    :sources => [
      "../c/meterpreter/output"
    ],
    :extensions => [
      "dll"
    ]
  },
  :java_meterpreter => {
    :sources => [
      "../java/output/data/meterpreter"
    ],
    :extensions => [
      "jar"
    ],
  },
  :java_output => {
    :sources => [
      "../java/output/data/java"
    ],
    :extensions => [
      "class"
    ]
  },
  :android => {
    :sources => [
      "../java/output/data/android"
    ],
    :extensions => [
      "jar",
      "dex",
      "xml",
      "arsc"
    ]
  },
  :php => {
    :sources => [
      php_source
    ],
    :extensions => [
      "php"
    ]
  },
  :python => {
    :sources => [
      python_source
    ],
    :extensions => [
      "py"
    ]
  }
}

def copy_files(cnf, meterpreter_dest)
  cnf[:sources].each do |f|
    cnf[:extensions].each do |ext|
      Dir.glob("#{f}/**/*.#{ext}").each do |bin|
        f_path = ::Pathname.new(f)
        bin_path = ::Pathname.new(bin)
        target = File.join(meterpreter_dest, bin_path.relative_path_from(f_path))
        print("Copying: #{bin} -> #{target}\n")
        contents = ::File.binread(bin_path)
        encrypted_contents = ::MetasploitPayloads::Crypto.encrypt(plaintext: contents)
        output = ::Pathname.new(::File.expand_path(target))
        ::FileUtils.mkdir_p(output.dirname) unless output.dirname.exist?
        ::File.binwrite(output, encrypted_contents)
      end
    end
  end
end

task :create_dir do
  Dir.mkdir(dest) unless Dir.exist?(dest)
  Dir.mkdir(meterpreter_dest) unless Dir.exist?(meterpreter_dest)
  Dir.mkdir(java_dest) unless Dir.exist?(java_dest)
  Dir.mkdir(android_dest) unless Dir.exist?(android_dest)
end

task :win_compile do
  Dir.chdir(c_source) do
    system('cmd.exe /c make.bat')
  end
end

task :java_compile do
  Dir.chdir(java_source) do
    system('mvn package -Ddeploy.path=output -Dandroid.sdk.path=$ANDROID_HOME -Dandroid.ndk.path=$ANDROID_NDK_HOME -Dandroid.release=true -q -P deploy')
  end
end

task :win_copy do
  copy_files(platform_config[:windows], meterpreter_dest)
end

task :java_copy do
  copy_files(platform_config[:java_meterpreter], meterpreter_dest)
  copy_files(platform_config[:java_output], java_dest)
  copy_files(platform_config[:android], android_dest)
end

task :php_copy do
  copy_files(platform_config[:php], meterpreter_dest)
end

task :python_copy do
  copy_files(platform_config[:python], meterpreter_dest)
end

task :create_manifest do
  all_data_files = ::Dir.glob(dest + '/**/*').select { |f| ::File.file?(f) }.sort
  manifest = all_data_files.map { |f| [f, manifest_hash_type, ::OpenSSL::Digest.new(manifest_hash_type, ::File.binread(f))].join(':') }
  ::File.binwrite(manifest_file, manifest.join("\n"))
  ::File.binwrite(manifest_uuid_file, ::OpenSSL::Digest.new(manifest_hash_type, ::File.binread(manifest_file)))
end

task :win_prep => [:create_dir, :win_compile, :win_copy, :create_manifest] do
end

task :java_prep => [:create_dir, :java_compile, :java_copy] do
end

task :php_prep => [:create_dir, :php_copy] do
end

task :python_prep => [:create_dir, :python_copy] do
end

task :default => [:python_prep, :php_prep, :java_prep, :create_manifest] do
end

# Override tag_version in bundler-#.#.#/lib/bundler/gem_helper.rb to force signed tags
module Bundler
  class GemHelper
    def tag_version
      sh "git tag -m \"Version #{version}\" -s #{version_tag}"
      Bundler.ui.confirm "Tagged #{version_tag}."
      yield if block_given?
    rescue
      Bundler.ui.error "Untagging #{version_tag} due to error."
      sh_with_code "git tag -d #{version_tag}"
      raise
    end
  end
end
