#!/usr/bin/ruby
# coding: utf-8

require 'nokogiri'
require 'open-uri'
require 'tempfile'

Keyserver = 'pgp.gwolf.org'
# NamesURL = 'https://people.debian.org/~anibal/ksp-dc17/names.html'
NamesURL = '/home/anibal/public_html/ksp-dc17/names.html'

def unutf(str)
  str.gsub('\xc3\xa8', 'è').gsub('\xc3\xb6', 'ö').gsub('\xc3\xa1','á').
    gsub('\xc3\xb1','ñ').gsub('\xc3\xb3','ó').gsub('\xc3\xab','ë').
    gsub('\xc3\xa9','é').gsub('\xc3\xbc','ü').gsub('\xc3\xae','î').
    gsub('\xc3\x89','É').gsub('\xc3\xa5','å').gsub('\xc4\x8c','Č').
    gsub('\xc5\x99','ř').gsub('\xc3\xbd','ý').gsub('\xc5\xbe','ž')
end

# Retreive our authoritative file, to populate the keyring
html = Nokogiri(open(NamesURL))
keys = html.search('a').map {|a| a.inner_html}.select {|lin| lin=~/^[0-9A-F]{16}$/}

# Get all the keys for the KSP, output each of them individually in
# this working directory
t=Tempfile.new('gnupg')
tmpdir=t.path
t.unlink
Dir.mkdir(tmpdir, 0700)
keys.each do |key|
  filename = '0x%s' % key
  puts 'Getting key %s' % key
  next if File.exists?(filename)
  system('gpg', '--keyserver', Keyserver, '--homedir', tmpdir, '--recv-keys', key)
  system('gpg', '--homedir', tmpdir, '--armor', '--output', filename, '--export', key)
  system('ls', '-lh', key)
end
# Yes, this looks nasty and dangerous. DO NOT RUN this script under
# any computer you care about :-P
system('rm', '-rf', tmpdir)

# Constant sections for all of the text files
res_txt = { :header => "Cross-signing report\n====================\n\n",
            :signed => [],
            :sign => []
          }

# Constant header for all of the Neato files
res =  ['digraph G {
    layout = neato;
    edge[len=7.0, color=lightgray];
    node[color=lightgray, fontcolor=lightgray];
    fontsize=96;
    outputorder=edgesfirst;
']
keys = {}
sig_to = {}
sig_from = {}
# All keys are part of the Neato output
Dir.glob('0x*').select {|f| f=~/^0x[\dABCDEF]{16}$/}.each do |key|
  has = []
  pkts = `gpg --list-packets #{key}`.split(/\n/)
  name = pkts.select{|l| l=~/^:user ID/}
  name = name.first
  # Some name corrections that we have to care for manually (due to us
  # picking just the first UID we find)
  name.gsub!(/Oriole/, 'Didier Raboud')
  name.gsub!('BDAY:1988-11-15','Stephen Paul Weber')
  name.gsub!('keybase.io/jeansch', 'Jean Schurger')

  if (name =~/^:user ID packet: "([^<"(]+)/)
    uid=unutf($1)
    res <<( '    "%s" [label="%s"];' % [key, uid])
    res_txt[:header] << ("%s (%s)\n" % [key, uid])
  end
  keys[key] = unutf($1)

  # Note which keys sign this one, which are signed by it (ignoring
  # all signatures not in this KSP's keyring)
  sig_to[key] = []
  sig_from[key] = []
  sigs = pkts.select{|l| l=~/^:signature/}.map {|l| l.gsub /.*, keyid /, ''}.
         select{ |sig| File.exists?('0x%s'%sig) }.each do |sig|
    next if key == sig
    next if has.include?(sig)
    sig_to[key] ||= []
    sig_to[key] << sig
    sig_from['0x%s'%sig] ||= []
    sig_from['0x%s'%sig] << key
    has << sig
    res << '      "%s" -> "0x%s";' % [key, sig]
#    res_txt[:sig_to] << sig
  end
end

# Build a HTML index for all keys
html = File.open('index.html', 'w')
html.puts %q(<html><head><title>Key Signing Party participants • DebConf 17</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>body {font-family: Helvetica, sans-serif, arial;}</style>
</head><body>
<h1>Key Signing Party participants • DebConf 17</h1>
<p>This shows the list of participants for the <a href="%s">DebConf17 Key
Signing Party</a>. Its utility is to find whom are <em>you</em> better
connected to, and with whom should you ask for signatures.</p>
<p>There are really no guidelines to this. But...</p>
<ul>
  <li>Is <em>Foo</em> your friend, and you haven't cross-signed? Go ahead
    and get yourself signed!</li>
  <li>Is <em>Bar</em> many <em>hops</em> away from you? Cross-sign and you
    will bring more coherence to our strong set!</li>
  <li>Is <em>Quux</em> isolated or weakly connected? Bring him in by
    cross-signing!</li>
  <li>Of course... Are you very weakly connected or disconnected? Don't be
    shy; cross-sign with as many people as you can.</li>
</ul>
<p>There is no need to be at the geographic center, only to introduce as much
redundancy (in the good sense) as possible.</p>
<ol>
)

keys.sort_by {|k,v| v.upcase}.each do |k, name|
  html.puts '<li><a href="%s.neato.svg"><tt>%s</tt> - %s</a></li>' % [k, k, unutf(name)]
  base = res.join("\n").split(/\n/)
  related = []

  puts 'Generating Neato file: %s - %s' % [k, name]
  File.open('%s.neato' % k, 'w') do |out|
    out.puts base.map { |lin|
      if lin.include?(k)
        if lin.include?('->')
          lin.gsub! /";$/, '" [color=black];'
        end
      end
      
      lin
    }.join("\n")
    out.puts '    "%s" [style=filled, color=black, fontcolor=white];' % k
    sig_to[k].each do |to|
      next if '0x%s'%to == k
      out.puts '"0x%s" [color=black, fontcolor=black];' % to
      related << to unless related.include?(to)
    end
    sig_from[k].each do |from|
      next if from == k
      out.puts '"%s" [color=black, fontcolor=black];' % from
      related << from unless related.include?(from)
    end
    out.puts 'label="%s: %d related keys (signed by←%d, signs→%d)";' % [name, related.size, sig_from[k].size, sig_to[k].size]
    out.puts '}'
  end
  puts 'Compiling...'
  system('neato', '-Tsvg', '-O', '%s.neato'%k)
end

html.puts '</ol></body></html>'
