#!/usr/bin/env ruby -wKU
# == Synopsis
#
# upload [-k «keyfile»] [-d «destination»] «file»
#
# == Usage
#
# --help:
#    show help.
#
# --keyfile/-k:
#    Keyfile used for signing.
#
# --destination/-d:
#    Which GitHub ‘«user»/«repository»’ we should upload to.
#
require 'getoptlong'
require "rubygems"
require "net/netrc"
require "json"
require 'base64'
require 'openssl'
require 'digest/sha1'

def sign_file(path, keyfile, password)
  # %x{openssl dgst -dss1 -sign '#{keyfile}' -passin 'pass:#{password}' '#{path}'|openssl enc -base64}.chomp

  key       = OpenSSL::PKey::DSA.new(File.read(keyfile), password) or abort "*** error reading keyfile: ‘#{keyfile}’."
  digest    = Digest::SHA1.digest(File.read(path))
  signature = key.syssign(digest)
  Base64.encode64(signature).gsub("\n", '')
end

def aws_upload(path, url, key, acl, filename, content_type, access_key, policy, signature)
  rc = %x{curl -sw'%{http_code}' --show-error -o/dev/null \
     -F "key=#{key}" \
     -F "acl=#{acl}" \
     -F "success_action_status=201" \
     -F "Filename=#{filename}" \
     -F "Content-Type=#{content_type}" \
     -F "AWSAccessKeyId=#{access_key}" \
     -F "Policy=#{policy}" \
     -F "Signature=#{signature}" \
     -F "file=@#{path}" \
     #{url}
  }
  abort "aws error: #{rc}" unless rc == '201'
end

def create_download(path, repository, name, description, content_type)
  payload = { 'name' => name || File::basename(path), 'size' => File::size(path), 'content_type' => content_type || 'application/octet-stream' }
  payload['description'] = description unless description.nil?

  open("|curl -snd '#{payload.to_json}' https://api.github.com/repos/#{repository}/downloads") do |io|
    github = JSON.parse(io.read)
    abort "github error: #{github['errors'].inspect} for #{repository}"     if github.include?('errors')
    abort "github unexpected response: #{github.inspect} for #{repository}" unless github.include?('html_url')
    aws_upload(path, github['s3_url'], github['path'], github['acl'], payload['name'], payload['content_type'], github['accesskeyid'], github['policy'], github['signature'])
    return github['html_url']
  end
  nil
end

if __FILE__ == $PROGRAM_NAME
  opts = GetoptLong.new(
    [ '--help',        '-h', GetoptLong::NO_ARGUMENT       ],
    [ '--keyfile',     '-k', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--destination', '-d', GetoptLong::REQUIRED_ARGUMENT ]
  )

  keyfile     = nil
  destination = 'textmate/textmate'
  netinfo     = Net::Netrc.locate("sign.textmate.org") or abort "*** missing passphrase in ~/.netrc."

  opts.each do |opt, arg|
    case opt
      when '--help'        then RDoc::usage
      when '--keyfile'     then keyfile     = arg
      when '--destination' then destination = arg
    end
  end

  abort 'No signing key provided' if keyfile.nil?

  if path = ARGV.shift
    if File.basename(path) =~ /(.+)_r(\d+)\.tbz$/
      base, name, version = $&, $1, $2
      description = ''

      info = {
        'url'       => create_download(path, destination, base, description, 'application/x-bzip2'),
        'version'   => version,
        'signature' => sign_file(path, keyfile, netinfo.password),
        'signee'    => netinfo.login
      }

      STDOUT << info.to_json
    end
  end
end
