#! /usr/bin/env python

import sys
import os
import stat
import argparse
import yaml
import subprocess
import shutil

def run_openssl(args):
    p = subprocess.Popen(['openssl'] + args, stderr=subprocess.PIPE)
    (_, out_stderr) = p.communicate()
    if p.returncode == 0:
        return True
    print >>sys.stderr, "openssl %s failure: %d: %s" % \
        (args, p.returncode, out_stderr)
    return False

def make_eckey(name):
    privkey = '%s-private.pem' % name
    pubkey = '%s.pem' % name

    if os.access(privkey, os.R_OK) and os.access(pubkey, os.R_OK):
        return True
    print "creating EC key \"%s\"" % name

    ecparam_args =  ['ecparam', '-name', 'prime256v1', '-genkey', '-noout',
                     '-out', privkey]
    if not run_openssl(ecparam_args):
        return False
    os.chmod(privkey, stat.S_IRUSR | stat.S_IWUSR)

    ec_args = ['ec', '-in', privkey, '-pubout', '-out', pubkey]
    if not run_openssl(ec_args):
        return False

    return True

def make_ca(logname, cakey, cacert):
    os.makedirs('./demoCA/newcerts', 0700)

    f = open('./demoCA/index.txt', 'w')
    f.close()

    f = open('./demoCA/serial', 'w')
    f.write('00\n')
    f.close()

    f = open('caconfig.txt', 'w')
    f.write('[ req ]\n')
    f.write('distinguished_name = req_distinguished_name\n')
    f.write('x509_extensions = v3_ca\n')
    f.write('string_mask = utf8only\n')
    f.write('[ req_distinguished_name ]\n')
    f.write('[ v3_ca ]\n')
    f.write('basicConstraints=CA:true\n')
    f.close()
    
    subject = '/countryName=II/stateOrProvinceName=internets/organizationName=%s/commonName=ca' % logname
    req_args = ['req', '-newkey', 'rsa:2048', '-keyout', cakey, '-out',
                'req.csr', '-nodes', '-subj', subject, '-config', 'caconfig.txt']
    if not run_openssl(req_args):
        return False
    os.chmod(cakey, stat.S_IRUSR)

    ca_args = ['ca', '-in', 'req.csr', '-selfsign', '-keyfile', cakey,
               '-out', cacert, '-batch']
    if not run_openssl(ca_args):
        return False

    return True

def make_certs(logname, nodenames):
    wdir = './httpscerts'
    if not os.access(wdir, os.F_OK):
        os.mkdir(wdir)
    os.chdir(wdir)

    ca_key = './cakey.pem'
    ca_cert = './demoCA/cacert.pem'
    if not os.access(ca_key, os.R_OK) or not os.access(ca_cert, os.R_OK):
        if not make_ca(logname, ca_key, ca_cert):
            return False

    for nodename in nodenames:
        key = './%s-key.pem' % nodename
        csr = './%s.csr' % nodename
        cert = './%s.pem' % nodename
        subject = '/countryName=II/stateOrProvinceName=internets/organizationName=%s/CN=%s' % (logname, nodename)

        if os.access(key, os.R_OK) and os.access(cert, os.R_OK):
            continue
        print "creating cert for node %s" % nodename

        req_args = ['req', '-new', '-newkey', 'rsa:2048', '-keyout', key,
                    '-out', csr, '-nodes', '-subj', subject]
        if not run_openssl(req_args):
            return False
        ca_args = ['ca', '-in', csr, '-keyfile', ca_key, '-out', cert, '-batch']
        if not run_openssl(ca_args):
            return False

        shutil.copy(ca_cert, '../nodes/%s/cacert.pem' % nodename)
        shutil.copy(cert, '../nodes/%s/webcert-%s.pem' % (nodename, nodename))
        shutil.copy(key, '../nodes/%s/webkey-%s.pem' % (nodename, nodename))
        os.chmod('../nodes/%s/webkey-%s.pem' % (nodename, nodename),
                 stat.S_IRUSR | stat.S_IWUSR)

    os.chdir('..')
    return True

def make_authkeys(nodenames):
    wdir = './publickeys'
    if not os.access(wdir, os.F_OK):
        os.mkdir(wdir)
    os.chdir(wdir)

    for nodename in nodenames:
        priv_dst = '../nodes/%s/%s-private.pem' % (nodename, nodename)
        if os.access(priv_dst, os.R_OK):
            continue
        if not make_eckey(nodename):
            return False
        if os.access(priv_dst, os.F_OK) and not os.access(priv_dst, os.W_OK):
            os.chmod(priv_dst, stat.S_IWUSR)
        shutil.move('%s-private.pem' % nodename, priv_dst)
    for nodename in nodenames:
        pub_dst = '../nodes/%s/publickeys' % nodename
        shutil.rmtree(pub_dst, ignore_errors=True)
        shutil.copytree('.', pub_dst)

    os.chdir('..')
    return True

def copy_logkey(logname, nodenames):
    for nodename in nodenames:
        shutil.copy('%s.pem' % logname, 'nodes/%s/' % nodename)

def create_destdirs(logname, nodenames):
    for nodename in nodenames:
        d = 'nodes/%s' % nodename
        if not os.access(d, os.F_OK):
            os.makedirs(d)

def copy_cacert(nodenames):
    ca_cert = './httpscerts/demoCA/cacert.pem'
    for nodename in nodenames:
        shutil.copy(ca_cert, './nodes/%s/cacert.pem' % nodename)

def main():
    parser = argparse.ArgumentParser(description="")
    parser.add_argument('config', help="System configuration")
    parser.add_argument('--logname', help="Log name", default="mylog")
    args = parser.parse_args()

    logname = args.logname
    config = yaml.load(open(args.config))
    nodenames = [node["name"] for node in
                 config["frontendnodes"] +
                 config["storagenodes"] +
                 config["signingnodes"]]
    mergenodenames = [node["name"] for node in config["mergenodes"]]

    create_destdirs(logname, nodenames + mergenodenames)
    make_eckey(logname)
    copy_logkey(logname, nodenames + mergenodenames)
    make_certs(logname, nodenames)
    make_authkeys(nodenames + mergenodenames)
    copy_cacert(mergenodenames)

main()