#=============================================================================
# Code-signing Information
#=============================================================================

# After we create the installer's .exe, we'll determine whether or not we can
# sign it.  This is done by detecting the presence of a single file in this
# directly matching the iglob '*.pfx' (more than one match results in an error
# being printed and no code-signing attempt).  See CODESIGN.txt for the steps
# I used to generate CA and SPC certificates that can be used during testing/
# development in lieu of the proper Thawte ones.

# signtool.exe ships with the Windows SDK.  On my box, `where signtool`
# returns:
#   C:\src\anaconda\ibuild>where signtool
#   C:\Program Files (x86)\Windows Kits\8.0\bin\x64\signtool.exe
#   C:\Program Files (x86)\Windows Kits\8.0\bin\x86\signtool.exe
#   C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe
# For the sake of simplicity, just make sure it's on your PATH.
SIGNTOOL = r'C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe'

# Other timestamp URLs that can be used if VeriSign is playing up:
# TIMESTAMP_URL = 'http://timestamp.globalsign.com/scripts/timstamp.dll'
# TIMESTAMP_URL = 'http://timestamp.comodoca.com/authenticode'
SIGN_TIMESTAMP_URL = 'http://timestamp.verisign.com/scripts/timstamp.dll'

SIGN_DESC_URL = "http://www.continuum.io"

#=============================================================================
import os
import time
from subprocess import Popen, PIPE
from glob import glob
import re

import click


def try_sign(path, sign_desc):
    pfx = glob('*.pfx')
    if len(pfx) > 1:
        print("Error: more than one code-signing certificate found (*.pfx), "
              "not attempting to digitally sign installer.")
        return

    if not pfx:
        print("No code-signing certificate found, "
              "not attempting to sign installer.")
        return

    pfx = pfx[0]
    print("Attempting to sign installer with certificate: '%s'..." % pfx)
    args = [
        SIGNTOOL, 'sign',
        '/f',  pfx,
        '/d',  sign_desc,
        '/du', SIGN_DESC_URL,
        '/t',  SIGN_TIMESTAMP_URL,
    ]

    # The official Thawte certificate is protected by a password, which needs
    # to be provided to signtool.exe via the /p argument.  The password should
    # be stored in a plain text file with the name of the certificate and the
    # suffix '.passwd' (i.e. 'continuum.pfx.passwd').
    pfx_passwd = pfx + '.passwd'
    if os.path.exists(pfx_passwd):
        with open(pfx_passwd, 'r') as f:
            passwd = f.read().rstrip()
        args += ['/p', passwd]

    args.append(path)

    # Attempting to sign the installer here appears to be very racey; signtool
    # complains that the file can't be signed because it's being used by
    # another process.  Adding a `time.sleep(1)` worked a few times, until it
    # didn't.  Then `time.sleep(3)` worked.  Then it didn't.  So, let's just
    # brute force it!
    attempt = 0
    success = True
    out = ''
    err = ''
    while True:
        attempt += 1
        print("Signing attempt %d..." % attempt)

        p = Popen(args, stdout=PIPE, stderr=PIPE)
        while p.poll() is None:
            out, err = p.communicate()
            if 'file is being used by another process' in err.decode():
                success = False

        if success and p.returncode != 0:
            msg = "signtool.exe failed %d:\nstdout: %s\nstderr: %s\n"
            raise RuntimeError(msg % (p.returncode, out, err))

        if success:
            if out:
                print(out)
            break
        else:
            time.sleep(0.1)

@click.command()
@click.argument('installer_pattern')
def main(installer_pattern):
    installers = glob(installer_pattern)
    for installer in installers:
        match = re.match('(\w+)[23]\-(.*?)\-', installer)
        sign_desc = ' '.join((match.group(1), match.group(2)))
        try_sign(installer, sign_desc)

if __name__ == '__main__':
    main()