# Copyright (c) Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

import os
from distutils import version


def myExec(str):
    handle = os.popen(str)
    return handle.read()


class LiteSpeed:
    exist = False
    version = 'Unknown'
    suexec = False
    lve = False
    correctArch = False

    def __init__(self):
        self.exist = os.path.isfile('/usr/local/lsws')
        if os.path.isfile('/usr/local/lsws/bin/litespeed') and len(myExec('ps aux|grep litespeed').split('\n')) > 1:
            self.exist = True
            self.lve = '1' in myExec('grep enableLVE /usr/local/lsws/conf/httpd_config.xml')
            self.suexec = not ('0' in myExec('grep -i phpSuexec\> httpd_config.xml'))
            arch = myExec('file -L /usr/local/lsws/bin/litespeed')


class ApacheInfo:
    exist = False
    version = 'Unknown'
    mpm = 'Unknown'
    hostinglimits = 'Unknown'
    fcgid = 'Unknown'
    cgi = 'Unknown'
    php_dso = 'Unknown'
    cgid = 'Unknown'
    suPHP = 'Unknown'
    fastcgi = 'Unknown'
    disable_suexec = 'Unknown'
    suexec = 'Unknown'
    moduleInfo = False
    modules = 'Unknown'

    def __init__(self, path):
        self.detect(path)

    def check_version(self):
        if version.LooseVersion(self.version) >= version.LooseVersion("2.2.0"):
            return True
        else:
            print_warning(3001, "Unable to determine list of loaded modules, apache version %s", self.apache.version)
            return False

    def isModule(self, name):
        return str(self.modules.find(" "+name+"_module") != -1)

    def parseModules(self):
        if self.moduleInfo:
            self.hostinglimits = self.isModule('hostinglimits')
            self.fcgid = self.isModule('fcgid')
            self.php_dso = self.isModule('php5')
            self.cgi = self.isModule('cgi')
            self.cgid = self.isModule('cgid')
            self.suPHP = self.isModule('suPHP')
            self.fastcgi = self.isModule('fastcgi')
            self.disable_suexec = self.isModule('disable_suexec')
            self.suexec = self.isModule('suexec')

    def detect(self, path):
        self.exist = os.path.isfile(path)
        if self.exist:
            tmp = myExec(path + " -V 2>&1")
            if tmp.find('Syntax error') >= 0:
                lsapi_conf = None
                for s in tmp.split(' '):
                    if s.find('lsapi.conf') >= 0:
                        lsapi_conf = s.replace(':', '')
                        break
                if lsapi_conf is not None:
                    myExec('mv ' + lsapi_conf + ' ' + lsapi_conf + '.SAVE')
                tmp = myExec(path + " -V 2>&1")
                if lsapi_conf is not None:
                    myExec('mv ' + lsapi_conf + '.SAVE ' + lsapi_conf)
            lines = tmp.split('\n')
            for line in lines:
                if line.find('Server version:') != -1:
                    self.version = line[line.find('/')+1:]
                if line.find('Server MPM:') != -1:
                    self.mpm = line[line.rfind(' ')+1:]
            if version.LooseVersion(self.version) > version.LooseVersion('2.2'):
                self.moduleInfo = True
                self.modules = myExec(path + " -M 2>&1")
                self.parseModules()

    def str(self):
        if self.exist:
            str = "Apache verion: "+self.version+", mpm="+self.mpm+ \
                  ", hostinglimits="+self.hostinglimits+ \
                  ", cgi="+self.cgi+", cgid="+self.cgid+ \
                  ", fcgi="+self.fcgid+ ", fastcgi="+self.fastcgi+ \
                  ", php DSO="+self.php_dso+", suphp="+self.suPHP+ \
                  ", suexec="+self.suexec+", disable_suexec="+self.disable_suexec

            return str

        else:
            return None


def print_error(code, error_str, error_list, solution):
    print("CODE: ", code)
    print(error_str % error_list)
    print("Solution: ", solution)


def print_warning(code, error_str, error_list):
    print("WARNING: ", code)
    print(error_str % error_list)


class Kernel:
    version = None
    isLVEKernel = False
    isVZKernel = False
    isLVEEnabled = False

    def __init__(self):
        self.kernelName = myExec('/bin/uname -r').rstrip('\n')
        self.isLVEKernel = self.kernelName.find('lve')
        if self.isLVEKernel != -1:
            self.version = self.kernelName[self.isLVEKernel+3:]
            self.isLVEEnabled = os.path.isfile('/proc/lve/list')
        else:
            self.isVZKernel = 'stab' in self.kernelName

    def check(self):
        if self.isLVEEnabled:
            if self.isLVEEnabled:
                if version.LooseVersion(self.version) > version.LooseVersion('0.8.28'):
                    return True
                elif version.LooseVersion(self.version) > version.LooseVersion('0.8.0'):
                    print_error(1001, "You are running bugy kernel LVE version %s", self.version,
                                "Upgrade Kernel")
                elif version.LooseVersion(self.version) > version.LooseVersion('0.7.0'):
                    print_error(1002, "You are running old kernel LVE version %s\n That version doesn't support multiple cores per LVE or memory limits", self.version, "Upgrade Kernel")
                else:
                    print_error(1003, "You are running very old, bugy kernel, LVE version %s",
                                self.version, "Upgrade Kernel")
            else:
                print_error(1004, "LVE is not enabled", (),
                            "Check /etc/sysconfig/lve file, and make sure lve rpm is installed")
        elif self.isVZKernel:
            print_error(1101, "You are running VZ or OpenVZ", (),
                        "CloudLinux is not compatible, see http://www.cloudlinux.com/vz-compat.php for more info")
        else:
            print_error(1201, "You are not running CloudLinux kernel. Your kernel is: %s",
                        self.version, "Check /boot/grub/grub.conf")

    def str(self):
        result = "Kernel: "
        if self.isLVEEnabled:
            result += "OK (" + self.version + ")"
        elif self.isVZKernel:
            result += "VZ (" + self.kernelName + ")"
        else:
            result += "Unknown (" + self.kernelName + ")"
        return result


class CP:
    name = "Unknown CP"
    version = "Unknown"
    kernel = None

    def __init__(self):
        self.apache = ApacheInfo('/usr/sbin/apachectl')
        self.apache_path = '/etc/httpd'
        self.kernel = Kernel()

    def str(self):
        return self.name + " " + self.version + " " + self.kernel.str()

    def __str__(self):
        return self.name

    def check(self):
        self.kernel.check()


class CPanel(CP):
    def __init__(self):
        super(CPanel, self).__init__()
        self.name = "cPanel"
        self.version = myExec('/usr/local/cpanel/cpanel -V')
        self.apache = ApacheInfo('/usr/local/bin/apachectl')
        if os.path.exists('/etc/cpanel/ea4/is_ea4'):
            self.ea = 'EA4'
            self.apache_path = '/etc/apache2'
        else:
            self.ea = 'EA3'
            self.apache_path = '/usr/local/apache'

    def __str__(self):
        return self.ea


class Plesk(CP):
    def __init__(self):
        super(Plesk, self).__init__()
        self.name = "Plesk"
        tmp = myExec('/bin/cat /usr/local/psa/version')
        self.version = tmp.split(' ')[0]
        self.apache = ApacheInfo('/usr/sbin/apachectl')


class DirectAdmin(CP):
    def __init__(self):
        super(DirectAdmin, self).__init__()
        self.name = "DirectAdmin"
        tmp = myExec('/usr/local/directadmin/custombuild/build versions')
        tmp = tmp.split('\n')
        self.version = 'Unknown'
        self.apache = ApacheInfo('/usr/sbin/apachectl')
        for item in tmp:
            if item.find('Installed version of DirectAdmin:') != -1:
                self.version = item.split(':')[1].strip()
                break


class HSphere(CP):
    def __init__(self):
        super(HSphere, self).__init__()
        self.name = "H-Sphere"
        tmp = myExec('/bin/cat /hsphere/local/home/cpanel/shiva/psoft_config/HS_VERSION')
        self.version = tmp.split('\n')[1].strip()
        self.apache = self.get_apache_type()

    def get_apache_type(self):
        if os.path.isfile('/hsphere/shared/scripts/scripts.cfg'):
            f = open('/hsphere/shared/scripts/scripts.cfg')
            lines = f.readlines()
            f.close()
            for item in lines:
                key = item.split('=')[0].strip()
                value = item.split('=')[1].strip()
                if key == 'apache_version':
                    if value == '1':
                        return ApacheInfo('/hsphere/shared/apache/bin/httpd')
                    else:
                        return ApacheInfo('/hsphere/shared/apache2/bin/apachectl')
        return ApacheInfo('')


class iWorx(CP):
    def __init__(self):
        super(iWorx, self).__init__()
        self.name = "InterWorx"
        self.ini_file = '/usr/local/interworx/iworx.ini'
        self.version = self.find_version()
        self.apache = ApacheInfo('/usr/sbin/apachectl')

    def find_version(self):
        try:
            with open(self.ini_file, 'r') as ini_info:
                out = ini_info.read()
            return out.split('version')[4].replace('\n', '').replace('=', '').replace('"', '').split('[')[0]
        except Exception:
            return None


class ISPMgr(CP):
    def __init__(self):
        super(ISPMgr, self).__init__()
        self.name = "ISPManager"
        self.version = "unk"
        self.apache = ApacheInfo('/usr/sbin/apachectl')


class CustomPanel(CP):
    def __init__(self):
        super(CustomPanel, self).__init__()
        self.name = "CustomPanel"
        self.config_reader()
        self.root_dir = '/usr/share/lve/modlscapi/custom/'
        self.apache = None

    def config_reader(self):
        """Read all configurations related to Custom Panel from config.ini in the 'root_dir' folder.
        config.ini file must include GLOBAL section.
        """
        import configparser
        config = configparser.ConfigParser()
        config.read(self.root_dir + 'config.ini')
        cp_config = config['GLOBAL']
        self.check_config_ini(cp_config)

        self.version = cp_config.get('VERSION')
        self.apachectl= cp_config.get('APACHECTL_BIN_LOCATION')
        self.doc_url = cp_config.get('DOC_URL')
        self.executable= cp_config.get('EXECUTABLE_BIN')
        self.panel_name = cp_config.get('PANEL_NAME')

        if self.apachectl:
            self.apache = ApacheInfo(self.apachectl)

    def check_config_ini(self, cp_config):
        # If we add some directive to config.ini as a new feature or delete one of them, we have update this list too.
        # With this list we check if user supplied all required keys.
        required_keys = ['version', 'apachectl_bin_location', 'doc_url', 'executable_bin', 'panel_name']
        missing_keys = []

        for key in required_keys:
            if cp_config.get(key) is None:
                missing_keys.append(key)

        if missing_keys:
            print(f'Config file is missing required keys: {missing_keys}')
            exit(1)


def get_cp():
    if os.path.isfile('/usr/local/cpanel/cpanel'):
        cp = CPanel()
    elif os.path.isfile('/usr/local/psa/version'):
        cp = Plesk()
    elif os.path.isdir('/usr/local/directadmin') and os.path.isfile('/usr/local/directadmin/custombuild/build'):
        cp = DirectAdmin()
    elif os.path.isfile('/hsphere/local/home/cpanel/shiva/psoft_config/HS_VERSION'):
        cp = HSphere()
    elif os.path.isdir("/usr/local/ispmgr"):
        cp = ISPMgr()
    elif os.path.isfile('/usr/local/interworx/iworx.ini'):
        cp = iWorx()
    elif os.path.isfile('/usr/share/lve/modlscapi/custom/config.ini'):
        cp = CustomPanel()
    else:
        cp = CP()
    return cp
