#!/usr/bin/python
"""Import the various leases, delegations and keys.

It expects a directory with one or more of

 - lease.sig : JSON encoded "root" leases)
 - server.pri and server.pub : provided server keys
 - d-lease.sig : delegated leases in del01 format
 - d-oats.sig : delegated OAT keys in del02 format

And will install them under

  /library/xs-activation/

"""

import sys, os, re, tempfile
import shutil
import syslog

import bitfrost.util.json as json

BASE_DIR = '/library/xs-activation'
TMP_DIR  = '/library/xs-activation/tmp'

def parse_leases_with_json(contents):
    """Use a proper canonical json parser."""
    version, leases = json.read(contents)
    if version != 1:
        raise NotImplementedError("We only know version 1")
    return leases

def import_leases(fn):
    """Interpret the contents of a named file as JSON leases, and
    write the leases to disk."""

    f = open(fn)
    contents = f.read()
    f.close()
    leases = parse_leases_with_json(contents)

    destdir = os.path.join(BASE_DIR, 'leases')
    for serial, lease in leases.iteritems():
        write_hashedname_file(destdir, serial, lease)

def write_hashedname_file(basedir, serial, buf):
    target_dir = os.path.join(basedir, serial[-2:])
    if not os.path.exists(target_dir):
        os.makedirs(target_dir, 0755)

    # write new file atomically
    fpath = os.path.join(target_dir, serial)
    write_file(fpath, buf, 0644)

def write_file(fpath, buf, mode):
    """Atomically in the /var/lib/xs-activation partition.
       Mode must be octal."""
    (fh, tmpfpath) = tempfile.mkstemp(dir=TMP_DIR)
    os.write(fh, buf)
    os.close(fh)
    os.chmod(tmpfpath, mode)
    os.rename(tmpfpath, fpath)

def copyfile(src, dest, mode):
    # shutil.copyfile made atomic
    # mode (octal) is mandatory because the use of a tmpfile
    # means the dest is automatically 0600
    (fh, tmpfpath) = tempfile.mkstemp(dir=os.path.dirname(dest))
    os.close(fh)
    shutil.copyfile(src,tmpfpath)
    os.rename(tmpfpath, dest)
    os.chmod(dest, mode)

def import_delegations(srcfpath, type):
    """"Handles types 'lease-delegations' and 'oats-delegations' """
    destdir = os.path.join(BASE_DIR, type)
    fh = open(srcfpath)
    for line in fh:
        if line != '':
            tokens = line.split(' ')
            sn = tokens[1]
            write_hashedname_file(destdir, sn, line)
    fh.close()

def import_keys(srcdir):
    sprivpath = os.path.join(srcdir, 'server.pri')
    spubpath  = os.path.join(srcdir, 'server.pub')
    dprivpath = os.path.join(BASE_DIR, 'keys', 'server.private')
    dpubpath = os.path.join(BASE_DIR, 'keys', 'server.public')
    # will do the copy if
    # - the src files exist
    # - the privkey is missing (which is the last thing we copy)
    if os.path.exists(sprivpath) \
            and os.path.exists(spubpath) \
            and not os.path.exists(dprivpath):
        # If this sounds sooper secret, remember 
        # we're copying from a world-readable FAT partition.
        # The tight modes still protect the key from random
        # attackers in the field.
        copyfile(spubpath,  dpubpath,  0444)
        copyfile(sprivpath, dprivpath, 0400)

def import_all_files(indir):
    """Read all the files in a given directory and import their lease
    information"""
    for fn in os.listdir(indir):
        # Please use lease.sig - leases.dat is for ParaguayEduca compat
        # and deprecated.
        if fn == 'lease.sig' or fn == 'leases.dat':
            try:
                import_leases(os.path.join(indir, fn))
                log("imported leases from %s" % fn)
            except Exception, e:
                log("Failed to import %s (Error: %s)" % (fn, e))

        elif fn == 'd-lease.sig':
            try:
                import_delegations(os.path.join(indir, fn), 'lease-delegations')
                log("imported lease delegations from %s" % fn)
            except Exception, e:
                log("Failed to import %s (Error: %s)" % (fn, e))

        elif fn == 'd-oats.sig':
            try:
                import_delegations(os.path.join(indir, fn), 'oats-delegations')
                log("imported oats delegations from %s" % fn)
            except Exception, e:
                log("Failed to import %s (Error: %s)" % (fn, e))

        elif fn == 'server.pri':
            try:
                import_keys(indir)
                log("imported server key from %s" % fn)
            except Exception, e:
                log("Failed to import %s (Error: %s)" % (fn, e))

        elif fn == 'server.pub':
            # ignore - handled with priv key
            pass

        else:
            log("Unknown file %s" % fn)

syslog.openlog( 'xs-activation', 0, syslog.LOG_USER )
def log(msg, level=syslog.LOG_NOTICE):
    syslog.syslog(level, msg)


import_all_files(sys.argv[1])

syslog.closelog()
