Sindbad~EG File Manager

Current Path : /opt/nginxhttpd_/src/Service/Lsws/
Upload File :
Current File : //opt/nginxhttpd_/src/Service/Lsws/LswsVhostManager.php

<?php

namespace App\Service\Lsws;

use Exception;
use Symfony\Component\Filesystem\Filesystem;
use Twig\Environment;

class LswsVhostManager
{
    /**
     * @var string
     */
    protected $lswsHttpdConf;

    /**
     * @var string
     */
    protected $lswsRoot;

    /**
     * @var Filesystem
     */
    protected $fs;

    /**
     * @var array
     */
    private $updates;

    /**
     * @var array
     */
    private $data;
    /**
     * @var Environment
     */
    private $twig;

    /**
     * @var LswsListenerManager
     */
    protected $lswsListenerManager;

    const GLOBAL_VHOST_KEYS = ['vhRoot', 'configFile', 'allowSymbolLink', 'enableScript', 'restrained', 'setUIDMode', 'user', 'group'];
    const CONFIG_VHOST_KEYS = ['docRoot', 'vhDomain', 'vhAliases', 'adminEmails', 'enableGzip', 'enableBr', 'enableIpGeo', 'index', 'vhssl', 'module cache'];
    private const VIRTUALHOST_REGEX = '#virtualhost\s+([\w.-]+)\s+{([^}]*)}#s';


    public function __construct(string $lswsHttpdConf, string $lswsRoot, Environment $twig, LswsListenerManager $lswsListenerManager){
        $this->lswsHttpdConf = $lswsHttpdConf;
        $this->lswsRoot = $lswsRoot;
        $this->lswsListenerManager = $lswsListenerManager;
        $this->twig = $twig;
        $this->updates = [];
        $this->data = null;
        $this->fs = new Filesystem();
    }

    /**
     * @throws Exception
     */
    public function load()
    {
        if(!$this->fs->exists($this->lswsHttpdConf)){
            throw new Exception("The OpenLitespeed Httpd configuration file doesn't exists.");
        }

        $this->data = [];

        $httpd = file_get_contents($this->lswsHttpdConf);

        preg_match_all(self::VIRTUALHOST_REGEX, $httpd, $vhost_matches, PREG_PATTERN_ORDER);

        $this->lswsListenerManager->load();

        foreach ($vhost_matches[2] as $k => $rawVhost) {
            if (strpos($rawVhost, 'aforem-edu.net')) {
                continue;
            }

            $vhostName = $vhost_matches[1][$k];

            $vhost = [];

            $vhost['o2listeners'] = [];

            foreach ($this->lswsListenerManager->getListeners() as $listenerName => $listener){
                if(array_key_exists('map', $listener) && array_key_exists($vhostName, $listener['map'])){
                    $vhost['o2listeners'][] = $listenerName;
                }
            }

            foreach (explode("\n", trim($rawVhost)) as $line) {
                $line = explode('#', $line, 2)[0];

                if (trim($line) === '') {
                    continue;
                }

                if (preg_match('/(\S+)\s+(.+)$/', $line, $matches)) {
                    $vhost[$matches[1]] = $matches[2];
                }
            }

            if(array_key_exists('configFile', $vhost)){
                $configFile = $this->lswsRoot . $vhost['configFile'];

                if(!$this->fs->exists($configFile)){
                    $this->data[$vhostName] = $vhost;
                    continue;
                }

                $configFileContent = file_get_contents($configFile);

                $templates = ['apache', 'default'];

                foreach($templates as $template){
                    try{
                        $templateData = preg_quote($this->twig->render('vhconf/' . $template . '.conf.twig', ['serverIp' => '%ip']));
                        $templateData = preg_replace("/[\t ]/", '[\t ]*', $templateData);
                        $templateData = preg_replace("/\n/", '\n*', $templateData);
                        $templateData = str_replace('/', '\/', $templateData);

                        $hasReplaced = 0;
                        $templateData = preg_replace_callback('/%ip/', function () use (&$hasReplaced) {
                                $data = "(?<ip" . $hasReplaced . ">((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))";
                                $hasReplaced++;
                                return $data;
                        }, $templateData);

                    }catch (\Throwable $e){
                        continue;
                    }

                    if(preg_match("/.*".$templateData."/", $configFileContent, $matches)){
                        $vhost['o2template'] = $template;

                        if(array_key_exists('ip0', $matches)){
                            $vhost['o2serverIp'] = $matches['ip0'];
                        }

                        $configFileContent = preg_replace('/' . $templateData . '/', '', $configFileContent);
                    }
                }

                $configFileContent = trim($configFileContent);

                $configData = preg_split("/\n/", $configFileContent);

                $parent = null;
                foreach($configData as $line){
                    $lineData = explode("\t", preg_replace("/[\t ,]+/", "\t", trim($line)));
                    $key = $lineData[0];
                    unset($lineData[0]);

                    $lineData = array_values($lineData);

                    if(count($lineData) === 1){
                        $lineData = $lineData[0];
                    }

                    if((empty($lineData) && $lineData !== "0" && $lineData !== 0) || empty($key)){
                        continue;
                    }

                    if($lineData === '{'){
                        $parent = $key;
                    }elseif($key === '}'){
                        $parent = null;
                    }else{
                        if($parent){
                            if(!array_key_exists($parent, $vhost)){
                                $vhost[$parent] = [];
                            }

                            $vhost[$parent][$key] = $lineData;
                        }else{

                            $vhost[$key] = $lineData;
                        }
                    }
                }
            }

            $this->data[$vhostName] = $vhost;
        }
    }

    public function clear()
    {
        if($this->data){
            foreach($this->data as $key => $value){
                $this->remove($key);
            }
        }
    }

    public function setRule(string $vhostName, string $name, $value)
    {
        if(!array_key_exists($vhostName, $this->updates) || !is_array($this->updates[$vhostName])){
            $this->updates[$vhostName] = [];
        }

        if($value == null){
            if(array_key_exists($name, $this->updates[$vhostName])){
                unset($this->updates[$vhostName][$name]);
            }
        }else{
            $this->updates[$vhostName][$name] = $value;
        }

        if(!is_array($this->updates[$vhostName]) || (is_array($this->updates[$vhostName]) && count($this->updates[$vhostName]) === 0)){
            $this->remove($vhostName);
        }
    }

    public function remove(string $vhostName){
        $this->updates[$vhostName] = null;
    }

    /**
     * @throws Exception
     */
    public function persistAll()
    {
        $updates = $this->updates;

        $errors = [];

        foreach ($updates as $vhostName => $vhostData){
            try{
                if($vhostData !== null){
                    $this->persist($vhostName);
                }else{
                    $this->drop($vhostName);
                }
            }catch(\Throwable $e){
                $errors[$vhostName] = $e;
            }
        }

        if(count($errors) > 0){
            $message = "There is many errors detected\n";

            foreach ($errors as $v => $e){
                $message .= "\033[0;31m" . $v . " : " . $e->getMessage() . "\033[0m\n";
            }

            throw new Exception($message);
        }
    }

    /**
     * @throws Exception
     */
    private function drop(string $vhostName)
    {
        $pattern = '#virtualhost\s+(' . $vhostName . ')\s+{([^}]*)}#s';
        $configContent = file_get_contents($this->lswsHttpdConf);

        if(!preg_match($pattern, $configContent, $matches)){
            return;
        }

        $conf = explode("\n", trim($matches[2]));

        $vhRoot = null;
        $configFile = null;

        foreach ($conf as $line){
            $data = explode("\t", trim($line));
            if(count($data) === 2 && $data[0] === "vhRoot" && preg_match('/^conf\/vhosts/', $data[1])){
                $vhRoot = $this->lswsRoot . $data[1];
            }

            if (count($data) === 2 && $data[0] === 'configFile' && preg_match('/^conf\/vhosts/', $data[1])) {
                $configFile = $this->lswsRoot . $data[1];
            }
        }

        $this->fs->dumpFile($this->lswsHttpdConf, preg_replace($pattern, '', $configContent));

        $filesToRemove = [
            $configFile,
            $configFile . ".txt",
            $configFile . "0",
            $configFile . ".bak",
        ];

        $this->fs->remove($filesToRemove);

        $filesInRoot = scandir($vhRoot);

        if($filesInRoot){
            $filesInRoot = array_diff($filesInRoot, ['.', '..']);

            if(count($filesInRoot) === 0){
                $this->fs->remove($vhRoot);
            }
        }

        $this->lswsListenerManager->load();
        $listeners = array_keys($this->lswsListenerManager->getListeners());

        foreach($listeners as $listener){
            $this->lswsListenerManager->unmapVhost($listener, $vhostName);
        }

        $this->lswsListenerManager->persist();
    }

    /**
     * @throws Exception
     */
    private function persist(string $vhostName)
    {
        // Remove old vhost config to erase it
        $this->drop($vhostName);

        // Generate the vhost in the global config and create root folder and config file
        $this->generate($vhostName);

        // Persist data in the config file
        $this->write($vhostName);

        // Add the vhost to listeners
        $this->persistOnListeners($vhostName);

        unset($this->updates[$vhostName]);
    }

    /**
     * @throws Exception
     */
    private function generate(string $vhostName)
    {
        if(!$this->fs->exists($this->lswsHttpdConf)){
            throw new Exception("OpenLitespeed configuration file doesn't exists.");
        }

        $config = $this->updates[$vhostName];

        $rawConfig = "virtualhost $vhostName {";

        foreach ($config as $name => $value){
            if(!in_array($name, self::GLOBAL_VHOST_KEYS)){
                continue;
            }

            $rawConfig = $this->addRawRule($rawConfig, $name, $value, 1);
        }

        $rawConfig .= "\n}";

        $rawFileContent = file_get_contents($this->lswsHttpdConf);
        $this->fs->dumpFile($this->lswsHttpdConf, preg_replace('/\n\n+/', "\n\n", $rawFileContent . "\n\n" . $rawConfig));
        $this->fs->chown($this->lswsHttpdConf, 'lsadm');
        $this->fs->chgrp($this->lswsHttpdConf, 'nobody');

        $this->generateVhostFiles($vhostName);
    }

    /**
     * @throws Exception
     */
    private function generateVhostFiles(string $vhostName){
        $config = $this->updates[$vhostName];

        if (array_key_exists('vhRoot', $config) && !empty($config['vhRoot'])) {
            $vhRoot = $this->lswsRoot . $config['vhRoot'];

            if (!$this->fs->exists($vhRoot)) {
                $this->fs->mkdir($vhRoot, 0750);
                $this->fs->chown($vhRoot, 'lsadm');
                $this->fs->chgrp($vhRoot, 'nobody');
            }
        }

        if (array_key_exists('configFile', $config) && !empty($config['configFile'])) {
            $configFile = $this->lswsRoot . $config['configFile'];

            if (!$this->fs->exists($configFile)) {
                $this->fs->touch($configFile, 0750);
                $this->fs->chown($configFile, 'lsadm');
                $this->fs->chgrp($configFile, 'nobody');
            }
        }
    }

    /**
     * @throws Exception
     */
    private function write(string $vhostName)
    {
        $config = $this->updates[$vhostName];

        if (!array_key_exists('configFile', $config) || empty($config['configFile'])) {
            return;
        }

        if (!array_key_exists('o2serverIp', $config) || empty($config['o2serverIp'])) {
            throw new Exception("o2serverIp is not defined. You have to define it.");
        }

        $this->generateVhostFiles($vhostName);

        if(!$this->fs->exists($this->lswsRoot . $config['configFile'])){
            throw new Exception("Vhost config file doesn't exists. Impossible to create it.");
        }

        $rawVhostConfig = '';

        foreach ($config as $name => $value){
            if(!in_array($name, self::CONFIG_VHOST_KEYS)){
                continue;
            }

            $rawVhostConfig = $this->addRawRule($rawVhostConfig, $name, $value);
        }

        try{
            $res = $this->addTemplate($rawVhostConfig, $config['o2template'], $config['o2serverIp']);
        }catch (\Throwable $e){
            $res = $this->addTemplate($rawVhostConfig, "default", $config['o2serverIp']);
        }

        $rawVhostConfig = $res;

        $this->fs->dumpFile($this->lswsRoot . $config['configFile'], $rawVhostConfig);
        $this->fs->chown($this->lswsRoot . $config['configFile'], 'lsadm');
        $this->fs->chgrp($this->lswsRoot . $config['configFile'], 'nobody');
    }

    /**
     * @throws Exception
     */
    private function addTemplate(string $rawConfig, string $template, string $serverIp = '127.0.0.1') : string
    {
        $template = $this->twig->render("vhconf/$template.conf.twig", ['serverIp' => $serverIp]);
        return $rawConfig . "\n" . $template;
    }

    /**
     * @throws Exception
     */
    private function persistOnListeners(string $vhostName)
    {
        $config = $this->updates[$vhostName];

        if(!array_key_exists('o2listeners', $config) || empty($config['o2listeners'])){
            return;
        }

        if(!is_array($config['o2listeners'])){
            throw new Exception("o2listeners params is not an array");
        }

        if (!array_key_exists('vhDomain', $config) || empty($config['vhDomain'])) {
            throw new Exception('There is no vhDomain for this vhost. A vhDomain is required to map it with a listener');
        }

        $this->lswsListenerManager->load();
        $availableListeners = array_keys($this->lswsListenerManager->getListeners());

        foreach ($config['o2listeners'] as $listener){
            if(in_array($listener, $availableListeners)){
                $this->lswsListenerManager->mapVhost($listener, $vhostName, $config['vhDomain']);
            }
        }

        $this->lswsListenerManager->persist();
    }

    private function addRawRule(string $rawConfig, string $name, $value, $tabCount = 0) : string
    {
        if(empty($value) && $value !== "0" && $value !== 0){
            return $rawConfig;
        }

        $tabs = str_repeat("\t", $tabCount);

        if(is_array($value)){

            $rawConfig .= "\n" . $tabs . $name . "\t{";

            foreach ($value as $k => $v){
                if((empty($v) && $v !== '0' && $v !== 0) || is_array($v)){
                    continue;
                }

                $rawConfig .= "\n" . $tabs . "\t" . $k . "\t" . $v;
            }

            $rawConfig .= "\n" . $tabs . "}\n";
        }else{
            $rawConfig .= "\n" . $tabs . $name . "\t" . $value;
        }

        return $rawConfig;
    }
}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists