<?php
/**
 * Pimcore
 *
 * This source file is subject to the GNU General Public License version 3 (GPLv3)
 * For the full copyright and license information, please view the LICENSE.md and gpl-3.0.txt
 * files that are distributed with this source code.
 *
 * @copyright  Copyright (c) 2009-2016 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     GNU General Public License version 3 (GPLv3)
 */

namespace Pimcore\Tool;

use Pimcore\Config; 

class Console {
	/**
	 * @var string system environment
	 */
	private static $systemEnvironment;

    /**
     * @static
     * @return string "windows" or "unix"
     */
    public static function getSystemEnvironment(){
		if (self::$systemEnvironment == null) {
			if(stripos(php_uname("s"), "windows") !== false) {
				self::$systemEnvironment = 'windows';
			}else{
				self::$systemEnvironment = 'unix';
			}
		}
		return self::$systemEnvironment;
    }

    /**
     * @return mixed
     * @throws \Exception
     */
    public static function getPhpCli () {

        if(Config::getSystemConfig()->general->php_cli) {
            if(@is_executable(Config::getSystemConfig()->general->php_cli)) {
                return (string) Config::getSystemConfig()->general->php_cli;
            } else {
                \Logger::critical("PHP-CLI binary: " . Config::getSystemConfig()->general->php_cli . " is not executable");
            }
        }

        $paths = array(
            "/usr/bin/php",
            "/usr/local/bin/php",
            "/usr/local/zend/bin/php",
            "/bin/php",
            realpath(PIMCORE_DOCUMENT_ROOT . "/../php/php.exe") // for windows sample package (XAMPP)
        );

        foreach ($paths as $path) {
            if(@is_executable($path)) {
                return $path;
            }
        }

        throw new \Exception("No php executable found, please configure the correct path in the system settings");
    }

    public static function getTimeoutBinary () {
        $paths = array("/usr/bin/timeout","/usr/local/bin/timeout","/bin/timeout");

        foreach ($paths as $path) {
            if(@is_executable($path)) {
                return $path;
            }
        }

        return false;
    }

    /**
     * @param $cmd
     * @param null $outputFile
     * @param null $timeout
     * @return string
     */
    public static function exec ($cmd, $outputFile = null, $timeout = null) {

        if($timeout && self::getTimeoutBinary()) {

            // check if --kill-after flag is supported in timeout
            $killafter = "";
            $out = self::exec(self::getTimeoutBinary() . " --help");
            if(strpos($out, "--kill-after")) {
                $killafter = " -k 1m";
            }

            $cmd = self::getTimeoutBinary() . $killafter . " " . $timeout . "s " . $cmd;
        } else if($timeout) {
            \Logger::warn("timeout binary not found, executing command without timeout");
        }

        if($outputFile) {
            $cmd = $cmd . " > ". $outputFile ." 2>&1";
        } else {
            // send stderr to /dev/null otherwise this goes to the apache error log and can fill it up pretty quickly
            if(self::getSystemEnvironment() != 'windows') {
                $cmd .= " 2> /dev/null";
            }
        }

        \Logger::debug("Executing command `" . $cmd . "` on the current shell");
        $return = shell_exec($cmd);

        return $return;
    }

    /**
     * @static
     * @param string $cmd
     * @param null|string $outputFile
     * @return int
     */
    public static function execInBackground($cmd, $outputFile = null) {

        // windows systems
        if(self::getSystemEnvironment() == 'windows') {
            return self::execInBackgroundWindows($cmd, $outputFile);
        } else {
            return self::execInBackgroundUnix($cmd, $outputFile);
        }
    }

    /**
     * @static
     * @param string $cmd
     * @param string $outputFile
     * @return int
     */
    protected static function execInBackgroundUnix ($cmd, $outputFile) {

        if(!$outputFile) {
            $outputFile = "/dev/null";
        }

        $nice = "";
        if(@is_executable("/usr/bin/nice")) {
            $nice = "/usr/bin/nice -n 19 ";
        }

        $commandWrapped = "/usr/bin/nohup " . $nice . $cmd . " > ". $outputFile ." 2>&1 & echo $!";
        \Logger::debug("Executing command `" . $commandWrapped . "´ on the current shell in background");
        $pid = shell_exec($commandWrapped);

        \Logger::debug("Process started with PID " . $pid);

        return $pid;
    }

    /**
     * @static
     * @param string $cmd
     * @param string $outputFile
     * @return int
     */
    protected static function execInBackgroundWindows($cmd, $outputFile) {

        if(!$outputFile) {
            $outputFile = "NUL";
        }

        $commandWrapped = "cmd /c " . $cmd . " > ". $outputFile . " 2>&1";
        \Logger::debug("Executing command `" . $commandWrapped . "´ on the current shell in background");

        $WshShell = new \COM("WScript.Shell");
        $WshShell->Run($commandWrapped, 0, false);
        \Logger::debug("Process started - returning the PID is not supported on Windows Systems");

        return 0;
    }

    /**
     * Returns a hash with all options passed to a cli script
     *
     * @return array
     */
    public static function getOptions($onlyFullNotationArgs = false){
        GLOBAL $argv;
        $options = array();
        $tmpOptions = $argv;
        array_shift($tmpOptions);

        foreach($tmpOptions as $optionString){
            if($onlyFullNotationArgs && substr($optionString,0,2) != '--'){
                continue;
            }
            $exploded = explode("=",$optionString,2);
            $options[str_replace('-','',$exploded[0])] =  $exploded[1];
        }

        return $options;
    }

    /**
     * @param $options
     * @param string $concatenator
     * @param string $arrayConcatenator
     * @return string
     */
    public static function getOptionString($options,$concatenator = '=',$arrayConcatenator = ','){
        $string = '';

        foreach($options as $key => $value){
            $string .= '--' . $key;
            if($value){
                if(is_array($value)){
                    $value = implode($arrayConcatenator,$value);
                }
                $string .= $concatenator . "'" . $value . "'";
            }
            $string .= ' ';
        }

        return $string;
    }

    /**
     * @param array $allowedUsers
     * @throws \Exception
     */
    public static function checkExecutingUser($allowedUsers = array()){

        $configFile = \Pimcore\Config::locateConfigFile("system.php");
        $owner = fileowner($configFile);
        if($owner == false){
            throw new \Exception("Couldn't get user from file " . $configFile);
        }
        $userData = posix_getpwuid($owner);
        $allowedUsers[] = $userData['name'];

        $scriptExecutingUserData = posix_getpwuid(posix_geteuid());
        $scriptExecutingUser = $scriptExecutingUserData['name'];

        if(!in_array($scriptExecutingUser,$allowedUsers)){
            throw new \Exception("The current system user is not allowed to execute this script. Allowed users: '" . implode(',',$allowedUsers) ."' Executing user: '$scriptExecutingUser'.");
        }
    }

    /**
     * @throws \Exception
     */
    public static function checkCliExecution() {
        if (php_sapi_name() != 'cli') {
            throw new \Exception("Script execution is restricted to CLI");
        }
    }


}
