#!/usr/bin/env python

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import os
import os.path
import shutil
import json
import urllib2
from subprocess import Popen
from subprocess import PIPE, STDOUT
from datetime import datetime
import re


def sha1_matches(shaFile, checkFile):
    cwd = os.getcwd()
    os.chdir(os.path.dirname(checkFile))
    proc = Popen(["shasum", "-c", shaFile, "-s"], stdout=PIPE)
    output, unused_err = proc.communicate()
    retcode = proc.poll()
    if output:
        print "shasum said [%s]" % output.strip()
    os.chdir(cwd)
    return retcode == 0


def download_url(url, toFile, sha1):
    """Download a url to a file"""
    if not os.path.exists(toFile) or not sha1_matches(sha1, toFile):
        print 'Downloading [%s]' % url
        res = urllib2.urlopen(url)
        with open(toFile, 'w') as f:
            f.write(res.read())
        print 'Downloaded [%s]' % toFile
    else:
        print 'Cached [%s]' % toFile


def extract_files(inDir, zipFile):
    cwd = os.getcwd()
    os.chdir(inDir)
    if os.path.exists(zipFile):
        proc = Popen("gunzip -c %s | tar xf -" % zipFile,
                     stdout=PIPE, shell=True)
        output, unused_err = proc.communicate()
        retcode = proc.poll()
        if retcode:
            print "Extracting [%s] failed with return code [%d]" \
                % (zipFile, retcode)
        if output:
            print "Output from extraction [%s]" % output
    else:
        print "Zip file [%s] does not exist in [%s]" % (zipFile, inDir)
    os.chdir(cwd)


def install_mod(phpDir, modType, modStr):
    cwd = os.getcwd()
    os.chdir(phpDir)
    if modType == "pear" or modType == "pecl":
        proc = Popen(['./bin/%s' % modType, "install", modStr], stdout=PIPE)
        output, unused_err = proc.communicate()
        retcode = proc.poll()
        if retcode:
            print "Install [%s, %s] return code [%d]" \
                % (modType, modStr, retcode)
        if output:
            print "Output from install [%s]" % output
    else:
        print "Invalid modType [%s]" % modType
    os.chdir(cwd)


def extract_php_info(phpInfoOuput, reStr):
    extract = None
    m = re.search(reStr, output)
    if m is not None and len(m.groups()) > 0:
        extract = m.group(1)
    return extract


def newrelic_needs_configured(php_ini_install_file):
    with open(php_ini_install_file, 'rt') as phpIni:
        for line in phpIni:
            if line.find('newrelic') >= 0:
                return False
    return True

if __name__ == '__main__':
    # Open stdout unbuffered
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)

    print ''
    print '--------------------------------------------------------'
    print '|                     WARNING                          |'
    print '--------------------------------------------------------'
    print '|                                                      |'
    print '|        THIS BUILD PACK HAS BEEN DEPRECATED.          |'
    print '|                                                      |'
    print '|             IT HAS BEEN REPLACED BY:                 |'
    print '| https://github.com/dmikusa-pivotal/cf-php-build-pack |'
    print '|                                                      |'
    print '|                 PLEASE UPGRADE!!                     |'
    print '|                                                      |'
    print '|  THIS BUILD PACK WILL CEASE TO EXISTS AFTER 6/30/14  |'
    print '|                                                      |'
    print '--------------------------------------------------------'
    print ''

    print 'Started: [%s]' % datetime.now()

    # User's application files
    BUILD_DIR = sys.argv[1]

    # Cache space for the build pack
    CACHE_DIR = sys.argv[2]

    # Temp space
    TMP_DIR = os.environ.get('TMPDIR', '/tmp')

    # Make sure cache & build directories exist
    if not os.path.exists(BUILD_DIR):
        os.makedirs(BUILD_DIR)
    if not os.path.exists(CACHE_DIR):
        os.makedirs(CACHE_DIR)

    # Build pack location
    BP_DIR = os.path.dirname(os.path.dirname(sys.argv[0]))

    # Load configuration options
    default_cfg_file = os.path.join(BP_DIR,
                                    'default',
                                    'options.json')
    default_cfg = json.load(open(default_cfg_file))
    user_config_file = os.path.join(BUILD_DIR, 'config', 'options.json')
    if os.path.exists(user_config_file):
        print 'Custom config found, overriding default options'
        user_config = json.load(open(user_config_file))
        config = dict(default_cfg.items() + user_config.items())
    else:
        config = default_cfg

    # Location of Downloads
    BIN_URL = config['DOWNLOAD_URL']

    # Location of SHA1 files
    SHA_DIR = os.path.join(BP_DIR, 'default', 'shasums')

    # Default Configuration Directory
    DEFAULT_CFG_DIR = os.path.join(BP_DIR, 'default')

    # Download HTTPD & Install
    httpd_major_version = '.'.join(config['HTTPD_VERSION'].split('.')[:-1])
    httpd_bin_file = 'httpd-%s-bin.tar.gz' % config['HTTPD_VERSION']
    print 'Configuring HTTPD Version: %s' % config['HTTPD_VERSION']
    download_url(
        os.path.join(BIN_URL, 'httpd', httpd_major_version, httpd_bin_file),
        os.path.join(CACHE_DIR, httpd_bin_file),
        os.path.join(SHA_DIR, "httpd", httpd_major_version, "%s.sha1" % httpd_bin_file))
    extract_files(CACHE_DIR, httpd_bin_file)
    shutil.move(
        os.path.join(CACHE_DIR, httpd_bin_file[:-7]),
        os.path.join(BUILD_DIR, 'httpd'))

    # Install HTTPD Configuration
    default_httpd_cfg_dir = os.path.join(DEFAULT_CFG_DIR, 'httpd',
                                         httpd_major_version)
    httpd_cfg_dir = os.path.join(BUILD_DIR, 'httpd', 'conf')
    user_cfg_dir = os.path.join(BUILD_DIR, 'config', 'httpd')
    cfg_files = (
        'httpd.conf',
        os.path.join('extra', 'httpd-default.conf'),
        os.path.join('extra', 'httpd-directories.conf'),
        os.path.join('extra', 'httpd-logging.conf'),
        os.path.join('extra', 'httpd-mime.conf'),
        os.path.join('extra', 'httpd-modules.conf'),
        os.path.join('extra', 'httpd-mpm.conf'),
        os.path.join('extra', 'httpd-php.conf')
    )
    for cfg_file in cfg_files:
        if os.path.exists(os.path.join(user_cfg_dir, cfg_file)) \
                and cfg_file != "httpd.conf":
            print 'Found custom %s file, installing.' % cfg_file
            shutil.copy(
                os.path.join(user_cfg_dir, cfg_file),
                os.path.join(httpd_cfg_dir, cfg_file))
        else:
            shutil.copy(
                os.path.join(default_httpd_cfg_dir, cfg_file),
                os.path.join(httpd_cfg_dir, cfg_file))

    # Download PHP & Install
    php_major_version = '.'.join(config['PHP_VERSION'].split('.')[:-1])
    php_bin_file = 'php-%s-bin.tar.gz' % config['PHP_VERSION']
    print 'Configuring PHP Version: %s' % config['PHP_VERSION']
    download_url(
        os.path.join(BIN_URL, 'php', php_major_version, php_bin_file),
        os.path.join(CACHE_DIR, php_bin_file),
        os.path.join(SHA_DIR, 'php', php_major_version, "%s.sha1" % php_bin_file))
    extract_files(CACHE_DIR, php_bin_file)
    php_install_dir = os.path.join(BUILD_DIR, 'php')
    shutil.move(
        os.path.join(CACHE_DIR, php_bin_file[:-7]),
        php_install_dir)

    # Setup php.ini
    default_php_cfg_dir = os.path.join(DEFAULT_CFG_DIR, 'php',
                                       php_major_version)
    php_ini_file = os.path.join(BUILD_DIR, 'config', 'php.ini')
    php_ini_install_file = os.path.join(BUILD_DIR, 'php', 'etc', 'php.ini')
    if os.path.exists(php_ini_file):
        print 'Found custom php.ini file, installing.'
        shutil.copy(php_ini_file,
                    php_ini_install_file)
    else:
        shutil.copy(os.path.join(default_php_cfg_dir, 'php.ini'),
                    php_ini_install_file)

    # Setup php-fpm.conf
    php_fpm_cnf_file = os.path.join(BUILD_DIR, 'config', 'php-fpm.conf')
    php_fpm_cnf_install_file = os.path.join(BUILD_DIR,
                                            'php', 'etc', 'php-fpm.conf')
    if os.path.exists(php_fpm_cnf_file):
        print 'Found custom php-fpm.conf file, installing.'
        shutil.copy(php_fpm_cnf_file,
                    php_fpm_cnf_install_file)
    else:
        shutil.copy(os.path.join(default_php_cfg_dir, 'php-fpm.conf'),
                    php_fpm_cnf_install_file)

    # Install PEAR Modules
    pear_mods_file = os.path.join(BUILD_DIR, 'config', 'pear-mods.txt')
    if os.path.exists(pear_mods_file):
        with open(pear_mods_file, 'rt') as pear_mods:
            for mod in pear_mods:
                mod = mod.strip()
                print "Installing PEAR mod [%s]" % mod
                install_mod(php_install_dir, "pear", mod)

    # Install New Relic
    newrelic_install_key = config.get('NR_INSTALL_KEY', None)
    if newrelic_install_key:
        print 'Installing New Relic PHP agent'
        newrelic_daemon_folder = 'newrelic'
        newrelic_daemon_name = 'newrelic-daemon'
        newrelic_url = config['NEWRELIC_DOWNLOAD_URL']
        newrelic_version = config['NEWRELIC_VERSION']
        newrelic_base_name = 'newrelic-php5-%s-linux' % newrelic_version
        newrelic_bin_file = '%s.tar.gz' % newrelic_base_name
        download_url(
            os.path.join(newrelic_url, newrelic_version, newrelic_bin_file),
            os.path.join(CACHE_DIR, newrelic_bin_file),
            os.path.join(SHA_DIR, 'newrelic', "%s.sha1" % newrelic_bin_file))
        extract_files(CACHE_DIR, newrelic_bin_file)
        newrelic_extracted_dir = os.path.join(CACHE_DIR, newrelic_base_name)
        # Extract PHP info for proper New Relic module selection
        proc = Popen([os.path.join(php_install_dir, 'bin', 'php'), '-r', 'phpinfo();'],
                     stdout=PIPE, stderr=STDOUT, shell=False)
        output, err_output = proc.communicate()
        retcode = proc.poll()
        if retcode:
            print "Executing php failed with return code [%d]" \
                % retcode
        newrelic_arch = 'x64'
        php_zts = '-zts' if extract_php_info(output, 'Thread Safety => (.*)\n') == 'enabled' else ''
        php_api = extract_php_info(output, 'PHP Extension => (.*)\n')
        php_module_dir = os.path.join(php_install_dir, 'lib', 'php', 'extensions', ('no-debug-non-zts-%s' % php_api))
        newrelic_so = os.path.join(newrelic_extracted_dir,
                                   'agent',
                                   newrelic_arch,
                                   'newrelic-%s%s.so' % (php_api, php_zts))
        newrelic_so_dest = os.path.join(php_module_dir, 'newrelic.so')
        shutil.copy(newrelic_so, newrelic_so_dest)
        newrelic_daemon_source = os.path.join(newrelic_extracted_dir, 'daemon', 'newrelic-daemon.x64')
        newrelic_daemon_dir = os.path.join(BUILD_DIR, newrelic_daemon_folder)
        newrelic_daemon_dest = os.path.join(newrelic_daemon_dir, newrelic_daemon_name)
        os.makedirs(newrelic_daemon_dir)
        shutil.copy(newrelic_daemon_source, newrelic_daemon_dest)
        # Configure New Relic
        vcap_application = json.loads(os.environ.get('VCAP_APPLICATION'))
        if newrelic_needs_configured(php_ini_install_file):
            print 'Configuring New Relic in php.ini'
            with open(php_ini_install_file, 'a') as file:
                file.write('extension=newrelic.so\n')
                file.write('[newrelic]\n')
                file.write('newrelic.license=%s\n' % newrelic_install_key)
                file.write('newrelic.appname=%s\n' % vcap_application['name'])
                file.write('newrelic.daemon.logfile=%s\n' %
                           os.path.join('${HOME}', '..', 'logs', 'newrelic-daemon.log'))
                file.write('newrelic.daemon.location=%s\n' %
                           os.path.join('${HOME}', newrelic_daemon_folder, newrelic_daemon_name))
                file.write('newrelic.daemon.port=%s\n' %
                           os.path.join('${TMPDIR}', '.newrelic.sock'))
                file.write('newrelic.daemon.pidfile=%s\n' %
                           os.path.join('${TMPDIR}', 'newrelic.pid'))
        else:
            print 'New Relic is installed, but not configured due to what' \
                  ' appears to be an existing New Relic configuration in php.ini'

    # Create a startup script
    with open(os.path.join(BUILD_DIR, 'start.sh'), 'wt') as f:
        f.write('#!/usr/bin/env bash\n')
        if httpd_major_version == '2.4':
            f.write("export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/php/lib\n")
            f.write('%s -p "%s" -y "%s"\n' % (
                os.path.join("$HOME", "php", "sbin", "php-fpm"),
                os.path.join("$HOME", "php", "etc"),
                os.path.join("$HOME", "php", "etc", "php-fpm.conf")))
        f.write('export HTTPD_SERVER_ADMIN="%s"\n' % config['ADMIN_EMAIL'])
        f.write('%s -f %s -k start\n' % (
            os.path.join("$HOME", 'httpd', 'bin', 'apachectl'),
            os.path.join("$HOME", 'httpd', 'conf', 'httpd.conf')))
        f.write('while [ 1 -eq 1 ]; do\n')
        f.write('    sleep 100000\n')
        f.write('done\n')
    os.chmod(os.path.join(BUILD_DIR, 'start.sh'), 0755)

    print 'Finished: [%s]' % datetime.now()
