##
# Signing plugin for hoe.
#
# === Tasks Provided:
#
# generate_key::       Generate a key for signing your gems.
#
# === Extra Configuration Options:
#
# signing_key_file::    Signs your gems with this private key.
# signing_cert_file::   Signs your gem with this certificate.
#
# === Usage:
#
# Run the 'generate_key' task.  This will:
#
# 1. Configure your ~/.hoerc.
# 2. Generate a signing key and certificate.
# 3. Install the private key and public certificate files into ~/.gem.
#
# Hoe will now generate signed gems when the package task is run.  If you have
# multiple machines you build gems on, be sure to install your key and
# certificate on each machine.
#
# Keep your private key secret!  Keep your private key safe!
#
# You can provide your private key passphrase via the
# GEM_PRIVATE_KEY_PASSPHRASE environment variable.
#
# To make sure your gems are signed run:
#
#   rake package; tar tf pkg/yourproject-1.2.3.gem
#
# If your gem is signed you will see:
#
#   data.tar.gz
#   data.tar.gz.sig
#   metadata.gz
#   metadata.gz.sig

module Hoe::Signing
  Hoe::DEFAULT_CONFIG["signing_key_file"]  = "~/.gem/gem-private_key.pem"
  Hoe::DEFAULT_CONFIG["signing_cert_file"] = "~/.gem/gem-public_cert.pem"

  ##
  # Define tasks for plugin.

  def define_signing_tasks
    set_up_signing

    desc "Generate a key for signing your gems."
    task :generate_key do
      generate_key_task
    end

    desc "Check pubilc key for signing your gems."
    task :check_key do
      check_key_task
    end
  rescue NameError
    warn "Couldn't set up signing (openssl error?). Skipping."
  end

  def set_up_signing # :nodoc:
    signing_key = nil
    cert_chain = []

    with_config do |config, _path|
      break unless config["signing_key_file"] and config["signing_cert_file"]
      key_file = File.expand_path config["signing_key_file"].to_s
      signing_key = key_file if File.exist? key_file

      cert_file = File.expand_path config["signing_cert_file"].to_s
      cert_chain << cert_file if File.exist? cert_file
    end

    if signing_key and cert_chain then
      passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
      spec.signing_key = OpenSSL::PKey::RSA.new(File.read(signing_key), passphrase)
      spec.cert_chain = cert_chain
    end
  end

  def check_key_task # :nodoc:
    with_config do |config, _path|
      break unless config["signing_cert_file"]
      pub_key = File.expand_path config["signing_cert_file"].to_s

      c = OpenSSL::X509::Certificate.new File.read pub_key
      t = c.not_after

      if t < Time.now then
        warn "Gem signing certificate has expired"
      else
        warn "Gem signing certificate has NOT expired. Carry on."
      end
    end
  end

  def generate_key_task # :nodoc:
    email = Array(spec.email)
    abort "No email in your gemspec" if email.nil? or email.empty?

    key_file = with_config { |config, _| config["signing_key_file"] }
    cert_file = with_config { |config, _| config["signing_cert_file"] }

    if key_file.nil? or cert_file.nil? then
      ENV["SHOW_EDITOR"] ||= "no"
      Rake::Task["config_hoe"].invoke

      key_file = with_config { |config, _| config["signing_key_file"] }
      cert_file = with_config { |config, _| config["signing_cert_file"] }
    end

    key_file = File.expand_path key_file
    cert_file = File.expand_path cert_file

    unless File.exist? key_file then
      puts "Generating certificate"

      if File.exist? key_file then
        abort "Have #{key_file} but no #{cert_file}, aborting as a precaution"
      end

      warn "NOTICE: using #{email.first} for certificate" if email.size > 1

      sh "gem cert --build #{email.first}"
      mv "gem-private_key.pem", key_file, :verbose => true
      mv "gem-public_cert.pem", cert_file, :verbose => true

      puts "Installed key and certificate."
    end

    puts "Key file = #{key_file}"
    puts "Cert file = #{cert_file}"
    puts
    puts "Until rubygems.org has a better strategy for signing, that's"
    puts "the best we can do at this point."
  end
end
