# -*- coding: utf-8 -*-
# DirectAdmin function library
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions
#are met:
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following
#    disclaimer in the documentation and/or other materials provided
#    with the distribution.
#  * The names of its contributors may not be used to endorse or
#    promote products derived from this software without specific
#    prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
#FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
#COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
#INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
#BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
#ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#POSSIBILITY OF SUCH DAMAGE.
#

# Library functionality:
#
# Automatic configuration of PHP Selector for DirectAdmin
# Create php.ini files for DA PHP version selector
# Create symlink to user data directory
#


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from future import standard_library
standard_library.install_aliases()
from builtins import *
import os
import shutil
import cagefslib
from cldetectlib import is_da, get_param_from_file
import secureio
from signals_handlers import sigterm_check
from clcommon import clcaptain
from clcommon.utils import ExternalProgramFailed
from clcommon.utils import mod_makedirs

PHP_INI_MASK = '/usr/local/directadmin/data/users/%s/php/php.ini'
ALT_PHP_INI_MASK = '/etc/cl.php.d/alt-php%s/alt_php.ini'


# Configure PHP selector for DA
# Calls from cagefsctl
def configure_selector_for_directadmin():
    # Check - if this DirectAdmin machine
    if not is_da():
        return

    # 1. check file presence
    if not os.path.isfile(cagefslib.NATIVE_CONF):
        if not os.path.isdir(cagefslib.ETC_CL_ALT_PATH):
            mod_makedirs(cagefslib.ETC_CL_ALT_PATH, 0o755)

        shutil.copyfile('/usr/share/cagefs-plugins/directadmin-cagefs/directadmin.native.conf', cagefslib.NATIVE_CONF)
        os.chmod(cagefslib.NATIVE_CONF, 0o644)

    # 2. Determine php version - read /usr/local/directadmin/custombuild/options.conf and find PHP version there
    php_ver = get_param_from_file('/usr/local/directadmin/custombuild/options.conf', 'php1_release', '=')
    php_ver = php_ver.replace('.', '')
    is_lsphp = (get_param_from_file('/usr/local/directadmin/custombuild/options.conf', 'php1_mode', '=') == 'lsphp' or \
                                    get_param_from_file('/usr/local/directadmin/custombuild/options.conf', 'php2_mode', '=') == 'lsphp')


    # 3. Read config File
    cagefslib.read_native_conf()
    if not cagefslib.config_loaded:
        return

    content = cagefslib.orig_binaries

    # 4. modify php line
    if php_ver != '':
        # php = /usr/local/phpNN/bin/php-cgiNN
        content['php'] = '/usr/local/php'+php_ver+'/bin/php-cgi'+php_ver
    else:
        content['php'] = '/usr/local/php5/bin/php-cgi'

    if is_lsphp and not os.path.exists(content['php']):
        shutil.copyfile(content['lsphp'], content['php'])

    # 5. Determine php.ini path from /etc/httpd/conf/extra/httpd-suphp.conf
    httpd_php_ini_path = get_param_from_file('/etc/httpd/conf/extra/httpd-suphp.conf', 'suPHP_ConfigPath')

    if httpd_php_ini_path != '':
        # Path of php.ini determined, insert it to dictionary with PHP version
        if not httpd_php_ini_path.endswith('/'):
            httpd_php_ini_path += '/'

        content['php.ini'] = httpd_php_ini_path + 'php.ini'
    else:
        if php_ver != '':
            content['php.ini'] = '/usr/local/php' + php_ver + '/lib/php.ini'
        else:
            content['php.ini'] = '/usr/local/etc/php5/cgi/php.ini'

    # Check paths existence
    if not os.path.exists(content['php']) or not os.path.exists(content['php.ini']):
        return

    # write to file
    lines = []
    for key in content.keys():
        lines.append(key+'='+content[key])

    cagefslib.write_file(cagefslib.NATIVE_CONF, lines, True)
    cagefslib.config_loaded = False


# Create php.ini for DA
# Call from cagefs
def create_php_ini_for_DA(user_dir, user, php_vers, uid, gid):
    if not is_da():
        return

    cl_user_dir = user_dir + cagefslib.ETC_CL_ALT_PATH + "/" + user + "/php"
    link_name = cl_user_dir + '/php.ini'
    try:
        secureio.set_user_perm(uid, gid)
        if not os.path.isdir(cl_user_dir):
            mod_makedirs(cl_user_dir, 0o755)
        create_user_php_ini(user, php_vers, link_name, True)
    except (OSError, IOError) as e:
        secureio.set_root_perm()
        secureio.logging('Error: ' + str(e), secureio.SILENT_FLAG, True)
        return True

    secureio.set_root_perm()
    return False


# Create symlink to valid php.ini in DirectAdmin user dir
def create_symlink_to_php_ini_for_DA(skeleton_dir):
    sigterm_check()
    if not is_da():
        return

    dest_dir = skeleton_dir + '/usr/local/directadmin/data'
    try:
        if not os.path.isdir(dest_dir):
            mod_makedirs(dest_dir, 0o755)
        link_name = dest_dir + '/users'
        if not (os.path.islink(link_name) and os.readlink(link_name) == cagefslib.ETC_CL_ALT_PATH):
            try:
                os.unlink(link_name)
            except OSError:
                pass
            os.symlink(cagefslib.ETC_CL_ALT_PATH, link_name)
    except (OSError,) as e:
        secureio.logging('Error: ' + str(e), secureio.SILENT_FLAG, 1)


# Change user php.ini
# Call from cl-selector
def da_change_user_php_ini(username, version, is_write_log=False):
    if not is_da():
        return

    import pwd
    from cagefsctl import BASEDIR, get_user_prefix
    # userdir = /var/cagefs/[prefix]/[user]/etc
    prefix = get_user_prefix(username)
    link_name = BASEDIR + '/' + prefix + '/' + username + cagefslib.ETC_CL_ALT_PATH + '/' + username + '/php/php.ini'

    # drop permissions if we are running as root
    uid = None
    gid = None
    drop_perm = False
    # check if we work under the specified user
    user_data = pwd.getpwnam(username)
    if os.geteuid() != user_data.pw_uid:
        uid = user_data.pw_uid
        gid = user_data.pw_gid
        drop_perm = True

    if drop_perm:
        secureio.set_user_perm(uid, gid)

    # Create user link/file
    create_user_php_ini(username, version, link_name, is_write_log)

    if drop_perm:
        secureio.set_root_perm()


# Create user php.ini link or file dependently of PHP version
# Works only under user, not root!
def create_user_php_ini(user, php_vers, link_name, is_write_log):
    if php_vers == 'native':
        # '/usr/local/directadmin/data/users/%s/php/php.ini'
        php_ini_path = PHP_INI_MASK % user
        if os.path.isfile(php_ini_path):
            # user's php.ini present
            try:
                content = secureio.read_file_secure(php_ini_path, None, None, False, is_write_log)
            except (OSError, IOError):
                return

            if os.path.islink(link_name):
                try:
                    os.unlink(link_name)
                except OSError:
                    pass
            try:
                clcaptain.write(link_name, ''.join(content))
            except (OSError, IOError, ExternalProgramFailed) as e:
                secureio.logging("Error: Failed to write file " + link_name + ": " + str(e), secureio.SILENT_FLAG, 1, is_write_log)
        else:
            # user's php.ini absent, delete link to alt php.ini
            try:
                os.unlink(link_name)
            except OSError:
                pass
    else:
        # create symlink to php.ini for alternative php version
        link_to = ALT_PHP_INI_MASK % php_vers.replace(".", "")
        # Delete symlink if it points to wrong location, or if it is not symlink (regular file)
        if not (os.path.islink(link_name) and os.readlink(link_name) == link_to):
            try:
                os.unlink(link_name)      # delete symlink if previous version differs current
            except OSError:
                pass
            try:
                clcaptain.symlink(link_to, link_name)
            except (OSError, ExternalProgramFailed) as e:
                secureio.logging("Error: Failed to create link " + link_name + ": " + str(e), secureio.SILENT_FLAG, 1, is_write_log)
