<?php
	@set_time_limit(0);
	error_reporting(E_ALL & ~E_NOTICE);
    ini_set('magic_quotes_runtime', false);
	ignore_user_abort( true );
	clearstatcache();
	ini_set('output_buffering', 0);
	ini_set('implicit_flush', 1);

	if (isset($argv)) {
		foreach ($argv as $str) {
			$str = trim($str);
			if (!$str) continue;
			$tmp = explode("=", $str);
			$params[trim($tmp[0])] = isset($tmp[1]) ? trim($tmp[1]) : '';
		}
	}

	
	if (!in_array('ionCube Loader', get_loaded_extensions()) or !function_exists('ioncube_loader_version')) die("This script requires ionCube php extension\n");

	$ioncubeVersion = ioncube_loader_version();

	$ioncubeMajorVersion = (int)substr($ioncubeVersion, 0, strpos($ioncubeVersion, '.'));
	$ioncubeMinorVersion = (int)substr($ioncubeVersion, strpos($ioncubeVersion, '.')+1);
	if ($ioncubeMajorVersion < 4 || ($ioncubeMajorVersion == 4 && $ioncubeMinorVersion < 3)) {
		die("ionCube Loader '.$ioncubeVersion.' - old, required is a minimum of ionCube extension version 4.3 \n");
	}
	
	$install_source_url = 'https://smartcj.com/updates2/1/';
	$cdn_install_source_url = 'https://cdn.smartcj.com/updates2/1/';

	if (isset($_SERVER['HTTP_USER_AGENT'])) {
		$path = dirname(__FILE__);
		die ("SmartCJ Installation. Run from SSH: <br> <b> cd ".dirname(__FILE__)."; php install_scj.php");
	}

	$cmdline = new prompt();

	$tmp = phpversion();
	$tmp = explode('.', $tmp);
	// 5.6 and 7 = 5.6
	// 7.1 up to 7.4 = 7.1
	// 8.1 and up
	/*
	if ($tmp[0] == '5') {
		$phpversion = 7;
	} elseif ($tmp[0] == '7' and $tmp[1] == '0') {
		$phpversion = 7;
	} elseif ($tmp[0] == '7' and $tmp[1] > 1) {
		$phpversion = 71;
	} elseif ($tmp[0] == '8' ) {
		$phpversion = 81;
	} else die();
	*/


	if (PHP_MAJOR_VERSION == 5 or (PHP_MAJOR_VERSION == 7 and PHP_MINOR_VERSION == 0) ) {
		$phpversion = 7;
	} elseif (PHP_MAJOR_VERSION == 7 and PHP_MINOR_VERSION >= 1) {
		$phpversion = 71;
	} elseif (PHP_MAJOR_VERSION == 8 and PHP_MINOR_VERSION == 1) {
		$phpversion = 81;
	} elseif (PHP_MAJOR_VERSION == 8 and PHP_MINOR_VERSION == 2) {
		$phpversion = 82;
	} elseif (PHP_MAJOR_VERSION == 8 and PHP_MINOR_VERSION == 3) {
		$phpversion = 83;
	} elseif (PHP_MAJOR_VERSION == 8 and PHP_MINOR_VERSION == 4) {
		$phpversion = 84;
	} elseif (PHP_MAJOR_VERSION == 8) {
		$phpversion = 81;
	} else {
		die("Unknown version of PHP, please, contact support " . PHP_MAJOR_VERSION . '.' . PHP_MAJOR_VERSION);
	}


	if (version_compare(PHP_VERSION, '5.3.0', '<')) {
		die("This version of PHP (".PHP_VERSION.") is too old. The script requires PHP 5.3 and up.");
	}

	if (!function_exists("imagecreatefromjpeg") or !function_exists("imagecreatetruecolor") or !function_exists( "imagejpeg" ) || !function_exists( "imagedestroy" ) ) die("PHP GD lib is NOT installed. Ask admin to install it.");
	// if (!function_exists("ftp_connect")) die("PHP FTP lib is NOT installed. Ask admin to install it.");
	if (!function_exists("mysqli_connect")) die("PHP MySQL lib is NOT installed. Ask admin to install it.");
	if (!function_exists("fsockopen")) die("PHP net functions (fsockopen) is NOT installed. Ask admin to install it.");
	if (!function_exists("session_start")) die("PHP Session support (session_start) is NOT installed. Ask admin to install it.");
	if (!function_exists("exec")) die("PHP exec() function is NOT supported. Ask admin to install it.");
	if (!function_exists("curl_init")) die("PHP curl_init() function is NOT supported. Ask admin to install it.");



	$user = @exec('whoami');
	if ($user == 'root') {
		die("Please, DO NOT run install using root account. Install aborted.");
	}


	$scj_folder = (isset($params['scj_folder'])) ? $params['scj_folder'] : $cmdline->get("Folder name for script (default is scj): ");
	if (!$scj_folder) $scj_folder = 'scj';

	if (is_dir('./' . $scj_folder)) {
		die("Folder {$scj_folder} already exists \n");
	}


    $test_eval = false;
    @eval("\$test_eval=true;");
    if (!$test_eval) die("PHP eval() function is NOT installed. Ask admin to install it.");

	clearstatcache();
	
	// todo
	// if (is_dir('./' . $scj_folder)) die("Folder {$scj_folder} already exists. Rename or remove it and run install again.");

	@mkdir('./' . $scj_folder);
	if (!is_dir("./" . $scj_folder)) {
		die ("Can not create {$scj_folder} folder, contact support \n");
	}

	
	$tmp = curl_get_file($install_source_url . "/filelist.php?php_version={$phpversion}");
	if($tmp['info']['http_code'] != 200) {
		die("Can not get filelist\n");	
	}
	$files = unserialize($tmp['body']);


	if (!is_array($files)) {
		print_r($tmp);
		die("Can not get filelist ({$tmp['info']['http_code']} {$turl}). Please, contact script author. ");
	}

	$scj_path = getcwd();
	if (!$scj_path) {
		die ("Can not get path to current folder __FILE__");
	}
	$scj_path .= '/'.$scj_folder.'/';

	$scj_domain = (isset($params['domain'])) ? $params['domain'] : $cmdline->get("Type your domain (only domain ie site.com  - NO WWW, NO HTTP) : ");
	$admin_email = (isset($params['admin_email'])) ? $params['admin_email'] : $cmdline->get("Type your email (will be used as script admin email) : ");

	if (isset($params['php_path'])) {
		$php_path = $params['php_path'];
	} elseif (PHP_BINARY) {
		$php_path = PHP_BINARY;
	} else $php_path = $cmdline->get("Type PATH to php (ask admin if you don't know) : ");
	


	// locate mysql 
	if (@file_exists("/usr/bin/mysql")) {
		$mysql_path = '/usr/bin/mysql';
	} elseif (@file_exists("/usr/local/bin/mysql")) {
		$mysql_path = '/usr/local/bin/mysql';
	} else {
		$mysql_path = ($params['mysql_path']) ? $params['mysql_path'] : $cmdline->get("Type PATH to Mysql binany (ask admin if you don't know) (for example /usr/bin/mysql) : ");
	}

	$tr = array();
	$tr['htaccess'] = '.htaccess';
	$tr['htpasswd'] = '.htpasswd';

	echo "Downloading script files \n";
	$rc = new RollingCurl("request_callback");
	$rc->window_size = 50;

	$update_file_folder = ($phpversion == 5) ? 'files' : ('files' . $phpversion);

	$count = 0;
	foreach ($files as $name => $size) {
		$url = "{$cdn_install_source_url}/{$update_file_folder}/{$name}";
		$GLOBALS['scj_update_files'][] = $url;
    	$request = new RollingCurlRequest($url, 'GET', '', '', array(), array('target_file' => './' . $scj_folder . '/' . $name));
	    $rc->add($request);
	    if (isset($params['debug'])) {
	    	echo "req: $url \n";
	    }
	    $count++;
	    //if ($count > 1) break;
	}
	$rc->execute();

	echo "\n Done \n";


	$htaccess = 'DirectoryIndex index.php
AuthType Basic
AuthName "SCJ Admin Area"
AuthUserFile __PATH__/.htpasswd
require valid-user
';

	if (!isset($params['admin_password'])) {
		$passwd = '';
		for ($i=0; $i < 10; $i++) $passwd .= chr(rand(97,122));
	} else $passwd = $params['admin_password'];
	
	$htpasswd = 'admin:' . password_hash($passwd,  PASSWORD_DEFAULT);
	
	$ht_deny = "deny from all\n";


	file_put_contents($scj_folder . '/admin/.htaccess', $htaccess);
	file_put_contents($scj_folder . '/admin/.htpasswd', $htpasswd);

	file_put_contents($scj_folder . '/admin/files/.htaccess', $ht_deny);
	file_put_contents($scj_folder . '/backup/.htaccess', $ht_deny);
	file_put_contents($scj_folder . '/data/.htaccess', $ht_deny);
	file_put_contents($scj_folder . '/logs/.htaccess', $ht_deny);
	file_put_contents($scj_folder . '/cache/.htaccess', $ht_deny);
	file_put_contents($scj_folder . '/includes/libs/.htaccess', $ht_deny);


    $ht_list = array('top', 'top/tt', 'tmp', 'thumbs', 'rot_pages');
    foreach ($ht_list as $z => $folder) {
		if (!$f = fopen('./'.$scj_folder  .'/' . $folder . '/.htaccess', 'w')) {
			die("Can not open " . $folder . '/.htaccess for writing ');
		}
    
		fputs($f, "RemoveHandler .php .phtml .php3 .php4 .php5 .htm .html .shtml\nAddType text/html .php .phtml .php3 .php4 .php5 .htm .html .shtml\n");
		fclose($f);

    }


	echo "Making config.php \n";

	$tries = 0;

	while (true) {

		$tries++;

		if ($tries > 3) die("Can not connect using this parameters " . print_r($params));

		$mysql_host = (isset($params['mysql_host']) and $params['mysql_host']) ? $params['mysql_host'] : $cmdline->get("Mysql host (usually localhost): ");
		$mysql_user = (isset($params['mysql_user']) and $params['mysql_user']) ? $params['mysql_user'] : $cmdline->get("Mysql username: ");
		$mysql_pass = (isset($params['mysql_pass']) and $params['mysql_pass']) ? $params['mysql_pass'] : $cmdline->get("Mysql password: ");
		$mysql_name = (isset($params['mysql_name']) and $params['mysql_name']) ? $params['mysql_name'] : $cmdline->get("Mysql DB name: ");

    	try {
	    	if (!$link = mysqli_connect($mysql_host, $mysql_user, $mysql_pass, $mysql_name)) {
    			echo "Can not connect to mysql using this data, please try again or contact your admin \n";
    		
    			$params['mysql_host'] = $params['mysql_user'] = $params['mysql_pass'] = $params['mysql_name'] = '';
		    	continue;
    		}

    	} catch (Exception $e) {

    		echo "Can not connect to mysql using this data, please try again or contact your admin \n";
    		$params['mysql_host'] = $params['mysql_user'] = $params['mysql_pass'] = $params['mysql_name'] = '';
    		continue;

    	}


    	break;

	}


	// create config

	$tr = array();
	$tr['__DB_HOST__'] = $mysql_host;
	$tr["__DB_USER__"] = $mysql_user;
	$tr["__DB_PASSWORD__"] = $mysql_pass;
	$tr["__DB_NAME__"] = $mysql_name;
	$tr["__PHP_PATH__"] = $php_path;
	$tr["__ADMIN_EMAIL__"] = $admin_email;
	$tr["__PATH_TO_LOG__"] = $scj_path . 'logs/';
	

	$config_text = file_get_contents("./{$scj_folder}/includes/config.php");
	$config_text = strtr($config_text, $tr);

	if (isset($mysql_charset) and $mysql_charset != '') $config_text .= "\n \$config['db_charset'] = '{$mysql_charset}'; \n\n";

	if (!$f = fopen("./{$scj_folder}/includes/config.php", 'w')) {
		die("Can not open {$scj_folder}/includes/config.php for writing, please contact support \n");
	}
	fputs($f, $config_text);
	fclose($f);


	//create htaccess
	$config_text = file_get_contents("./{$scj_folder}/admin/.htaccess");
	$config_text = str_replace('__PATH__', $scj_path . 'admin', $config_text);

	if (!$f = fopen("./{$scj_folder}/admin/.htaccess", 'w')) {
		die("Can not open {$scj_folder}/admin/.htaccess for writing, please contact support \n");
	}
	fputs($f, $config_text);
	fclose($f);


	// create common.php

	$config_text = "<?php
	if (!defined('BASE_DIR')) define('BASE_DIR', '{$scj_path}');
	";

	if (!$f = fopen("./{$scj_folder}/cgi/common.php", 'w')) {
		die("Can not open {$scj_folder}/cgi/common.php for writing, please contact support \n");
	}
	fputs($f, $config_text);
	fclose($f);

	copy("{$scj_folder}/scj2.lic", './scj2.lic');

	$text = file_get_contents("{$scj_folder}/scj2.sql");
	$text = str_replace('/scj/', "/{$scj_folder}/", $text);
	file_put_contents("{$scj_folder}/scj2.sql", $text);

   	if (!isset($params['skip_default_db_sql'])) {
	   	$cmd = "{$mysql_path} --user='{$mysql_user}' --password='{$mysql_pass}' --host='{$mysql_host}' {$mysql_name} < {$scj_folder}/scj2.sql";
		// load mysql DB
	    $out = `$cmd`;
    }

	mysqli_query($link, "update settings set value = '".addslashes($admin_email)."' 
	where name = 'system_email'");

	mysqli_query($link, "update settings set value = '".addslashes('http://' . $scj_domain .'/')."' 
	where name = 'site_url'");

	mysqli_query($link, "UPDATE settings SET value = '".addslashes($scj_path . 'tube/index.php')."' WHERE name = 'cj_pages'");

	if (@file_exists("/usr/bin/convert")) {
		$im_path = "/usr/bin/";
	} elseif (@file_exists("/usr/local/bin/convert")) {
		$im_path = "/usr/local/bin/";
	}

	if (isset($im_path)) {
		mysqli_query($link, "UPDATE rot_settings SET value = '".addslashes($im_path)."' WHERE name = 'im_path' ");
		mysqli_query($link, "UPDATE rot_settings SET value = 'im' WHERE name = 'img_lib' ");
	}


	mysqli_query($link, "UPDATE settings SET value = '".str_replace('/mysql', '/mysqldump', $mysql_path)."' WHERE name = 'mysqldump_path' ");


	$pass = '';
	for ($i=1; $i<=10; $i++) {
		$pass .= chr(rand(97,122));
	}
	mysqli_query($link, "update settings set value = '".addslashes($pass)."' where name = 'exchange_password' ");

	// changes paths
	mysqli_query($link, "UPDATE settings SET value = REPLACE(value, '/scj/', '/".addslashes($scj_folder)."/') where name IN ('trader_thumbs_url', 'top_default_image', 'top_empty_url') ");

	// chmod
	foreach (file($scj_folder . '/includes/conf/permission_list.txt') as $z => $str) {
		$str = trim($str);
		if (!$str) continue;

		$tmp = explode('=', $str);

		$file_permissions[trim($tmp[0])] = trim($tmp[1]);
	}

	if (!is_array($file_permissions)) {
		die('Can not load permission list');
	}
	
	foreach ($file_permissions as $file => $perm) {
		if (!file_exists($scj_path . $file)) continue;
		if (!chmod($scj_path . $file, octdec( (int) $perm) )) {
			die("Can not set permission on {$scj_path}{$file}");
		}
	}

	$cmd = "cd {$scj_folder}/admin; {$php_path} update.php";
	`$cmd`;

	echo "Add following to Crontab (note, if you are not going to use internal rotation you can skip line with rot.php) \n\n";

	echo "#SmartCJ Pro at domain {$scj_domain}\n";
	echo "*/1 * * * * cd {$scj_path}bin && env HTTP_HOST={$scj_domain} {$php_path} -q cron.php \n";
	echo "*/1 * * * * cd {$scj_path}bin && env HTTP_HOST={$scj_domain} {$php_path} -q rotation.php \n";


	//if (!$params['domain'])	$mysql_name = $cmdline->get("\n\n Script Installation is done. Please, open http://{$scj_domain}/scj/admin/ in your browser. Your login is 'admin' and password is '{$passwd}'. Press Enter to continue. \n\n");
	echo "\n\n Script Installation is done. Please, open http://{$scj_domain}/{$scj_folder}/admin/ in your browser. Your login is 'admin' and password is '{$passwd}'. \n\n";

	@mail($admin_email, 'SmartCJ admin password', "Thank you for installing Smartcj at {$scj_domain}. Your login is admin and password is {$passwd} ");


	echo "\rDone, everything's ok                                                                          \n\n\n";



function request_callback($response, $info, $request) {
    global $scj_update_files, $total_downloaded, $params, $rc;

    if ($info['http_code'] != 200 ) {
    	//throw new Exception("Can not download file {$info['url']}");
    	$rc->add($request);

    	//throw new Exception("Can not download file {$info['url']} (code {$info['http_code']}) ");
    	print("Can not download file {$info['url']} (code {$info['http_code']}), try again .. \n");
    	return;

    }

    $total_downloaded++;

	$percent = round($total_downloaded/count($scj_update_files)*100);
	echo "\r" . $percent . "% downloaded  ";

    $save_path = $request->user_params['target_file'];

    if (isset($params['debug'])) {
    	echo "save: {$save_path} \n";
    }
    

	$p = 0;
	while ($p = strpos($save_path, '/', $p+1)) {
		$dir = substr($save_path, 0, $p);
		if (!is_dir($dir)) {
			if (!mkdir($dir)) die("Can not create $dir");
		}
	}

	if (!$f = fopen($save_path, 'w')) {
	    echo "<font size=+1><b> NOTE !!!  UPDATE IS NOT FINISHED !!! \n Script can not open {$save_path} for writing. Please, check permissions/file owner  and run update again !!! </b></font> \n";
	    throw new Exception("Can not save file ");
	}

	fputs($f, $response);
	fclose($f);

}




class RollingCurlRequest {
    public $url = false;
    public $method = 'GET';
    public $post_data = null;
    public $headers = null;
    public $options = null;
    public $handle_id = null;
    public $user_params = array();

    /**
     * @param string $url
     * @param string $method
     * @param  $post_data
     * @param  $headers
     * @param  $options
     * @return void
     */
    function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null, $user_params = null) {
        $this->url = $url;
        $this->method = $method;
        $this->post_data = $post_data;
        $this->headers = $headers;
        $this->options = $options;
        $this->user_params = $user_params;
    }

    /**
     * @return void
     */
    public function __destruct() {
        unset($this->url, $this->method, $this->post_data, $this->headers, $this->options);
    }
}


/**
 * RollingCurl custom exception
 */
class RollingCurlException extends Exception {
}

/**
 * Class that holds a rolling queue of curl requests.
 *
 * @throws RollingCurlException
 */
class RollingCurl {
    /**
     * @var int
     *
     * Window size is the max number of simultaneous connections allowed.
     *
     * REMEMBER TO RESPECT THE SERVERS:
     * Sending too many requests at one time can easily be perceived
     * as a DOS attack. Increase this window_size if you are making requests
     * to multiple servers or have permission from the receving server admins.
     */
    private $window_size = 5;

    /**
     * @var float
     *
     * Timeout is the timeout used for curl_multi_select.
     */
    private $timeout = 10;
    
    private $max_download_size = 0;

    /**
     * @var string|array
     *
     * Callback function to be applied to each result.
     */
    private $callback;

    /**
     * @var array
     *
     * Set your base options that you want to be used with EVERY request.
     */
    protected $options = array(
        CURLOPT_SSL_VERIFYPEER => 0,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_USERAGENT => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) SmartCJ',
        CURLOPT_FOLLOWLOCATION => true,
		CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        //CURLOPT_MAXREDIRS => 1
        //CURLOPT_MAX_RECV_SPEED_LARGE  => 10000
        //CURLOPT_VERBOSE => true,
    );

    /**
     * @var array
     */
    private $headers = array();

    /**
     * @var Request[]
     *
     * The request queue
     */
    private $requests = array();

    /**
     * @var RequestMap[]
     *
     * Maps handles to request indexes
     */
    private $requestMap = array();

    private $urlMap = array();

    /**
     * @param  $callback
     * Callback function to be applied to each result.
     *
     * Can be specified as 'my_callback_function'
     * or array($object, 'my_callback_method').
     *
     * Function should take three parameters: $response, $info, $request.
     * $response is response body, $info is additional curl info.
     * $request is the original request
     *
     * @return void
     */
    function __construct($callback = null) {
        $this->callback = $callback;
    }

    
    /**
     * @param string $name
     * @return mixed
     */
    public function __get($name) {
        return (isset($this->{$name})) ? $this->{$name} : null;
    }

    /**
     * @param string $name
     * @param mixed $value
     * @return bool
     */
    public function __set($name, $value) {
        // append the base options & headers
        if ($name == "options" || $name == "headers") {
            $this->{$name} = $value + $this->{$name};
        } else {
            $this->{$name} = $value;
        }
        return true;
    }

    /**
     * Add a request to the request queue
     *
     * @param Request $request
     * @return bool
     */
    public function add($request) {
        $this->requests[] = $request;
        return true;
    }

    /**
     * Create new Request and add it to the request queue
     *
     * @param string $url
     * @param string $method
     * @param  $post_data
     * @param  $headers
     * @param  $options
     * @return bool
     */
    public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
        $this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
        return true;
    }

    /**
     * Perform GET request
     *
     * @param string $url
     * @param  $headers
     * @param  $options
     * @return bool
     */
    public function get($url, $headers = null, $options = null) {
        return $this->request($url, "GET", null, $headers, $options);
    }

    /**
     * Perform POST request
     *
     * @param string $url
     * @param  $post_data
     * @param  $headers
     * @param  $options
     * @return bool
     */
    public function post($url, $post_data = null, $headers = null, $options = null) {
        return $this->request($url, "POST", $post_data, $headers, $options);
    }

    /**
     * Execute processing
     *
     * @param int $window_size Max number of simultaneous connections
     * @return string|bool
     */
    public function execute($window_size = null) {
        // rolling curl window must always be greater than 1
        if (sizeof($this->requests) == 1) {
            return $this->single_curl();
        } else {
            // start the rolling curl. window_size is the max number of simultaneous connections
            return $this->rolling_curl($window_size);
        }
    }

    /**
     * Performs a single curl request
     *
     * @access private
     * @return string
     */
    private function single_curl() {
        $ch = curl_init();
        $request = array_shift($this->requests);
        $options = $this->get_options($request);
        curl_setopt_array($ch, $options);
        $output = curl_exec($ch);
        $info = curl_getinfo($ch);

        // it's not neccesary to set a callback for one-off requests
        if ($this->callback) {
            $callback = $this->callback;
            if (is_callable($this->callback)) {
                call_user_func($callback, $output, $info, $request);
            }
        }
        else
            return $output;
        return true;
    }


    private function progress($resource, $downloadSize, $downloaded, $uploadSize, $uploaded) {
    
    	/*
    	echo " $resource, $downloadSize, $downloaded, $uploadSize, $uploaded, {$this->max_download_size}  \n";
    	if ($downloadSize > $this->max_download_size) {
    		return 'test';
    	}
    	*/
    	if ($downloaded > $this->max_download_size) {
    		return false;
    	}

    	return true;
    }

    /**
     * Performs multiple curl requests
     *
     * @access private
     * @throws RollingCurlException
     * @param int $window_size Max number of simultaneous connections
     * @return bool
     */
    private function rolling_curl($window_size = null) {
        if ($window_size)
            $this->window_size = $window_size;

        // make sure the rolling window isn't greater than the # of urls
        if (sizeof($this->requests) < $this->window_size)
            $this->window_size = sizeof($this->requests);

        if ($this->window_size < 2) {
            throw new RollingCurlException("Window size must be greater than 1");
        }

        $master = curl_multi_init();

        // start the first batch of requests
        for ($i = 0; $i < $this->window_size; $i++) {
            $ch = curl_init();

            //var_dump($this->requests[$i]);

            $options = $this->get_options($this->requests[$i]);

            //print_r($options);
            //exit;
            if ($this->max_download_size) {

            	$options[CURLOPT_NOPROGRESS] = false;
            	//$options[CURLOPT_BUFFERSIZE] = 1024;

				curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function ($resource, $downloadSize, $downloaded, $uploadSize, $uploaded)  {
				    $this->progress($resource, $downloadSize, $downloaded, $uploadSize, $uploaded);
				});

            }

            curl_setopt_array($ch, $options);

            curl_multi_add_handle($master, $ch);

            // Add to our request Maps
            //$key = (string) serialize($ch);
            // php 8 fix
            $key = (string) $options[CURLOPT_URL];
            /*
            $key = str_replace(array('http://www.', 'http://', 'https://www.', 'https://'), '', $key);
            if ($_SERVER['TEST_ENV']) {
            	echo "set key " . $options[CURLOPT_URL] . "  ... {$key} \n";
            }
            */
            $this->requestMap[$key] = $i;
            //print_r($this->requestMap);
            //echo "<br><br><br><br>";

        }

        do {
            while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
            if ($execrun != CURLM_OK)
                break;
            
            // a request was just completed -- find out which one
            while ($done = curl_multi_info_read($master)) {

                // get the info and content returned on the request
                $info = curl_getinfo($done['handle']);
                $output = curl_multi_getcontent($done['handle']);

                // send the return values to the callback function.
                $callback = $this->callback;
                if (is_callable($callback)) {

                    //$key = (string) $done['handle'];
                    $key = $info['url'];
                    if (!$this->requests[$this->requestMap[$key]]) {

                    	//echo 'no key: ' . $key;
                    	//print_r($this->requestMap);

                    	reset($this->requestMap);

                    	foreach ($this->requestMap as $map_k => $map_v) {
                    		$k1 = str_replace(array('http://www.', 'http://', 'https://www.', 'https://'), '', $map_k);
                    		$k2 = str_replace(array('http://www.', 'http://', 'https://www.', 'https://'), '', $info['url']);
                    		if ($k1 == $k2) {
                    			$key = $map_k;
                    			//var_dump($key);
                    			//break;
                    		}
                    	}

                    }

                    $request = $this->requests[$this->requestMap[$key]];
                    $request->handle_id = $this->requestMap[$key];
                    unset($this->requestMap[$key]);
                    call_user_func($callback, $output, $info, $request);
                }

                // start a new request (it's important to do this before removing the old one)
                if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {
                    $ch = curl_init();
                    $options = $this->get_options($this->requests[$i]);
                    curl_setopt_array($ch, $options);
                    curl_multi_add_handle($master, $ch);

                    // Add to our request Maps
                    //$key = (string) $ch;
                    $key = (string) $options[CURLOPT_URL];
                    $this->requestMap[$key] = $i;
                    $i++;
                }

                // remove the curl handle that just completed
                curl_multi_remove_handle($master, $done['handle']);

            }

            // Block for data in / output; error handling is done by curl_multi_exec
            if ($running)
                curl_multi_select($master, $this->timeout);

        } while ($running);
        curl_multi_close($master);
        return true;
    }


    /**
     * Helper function to set up a new request by setting the appropriate options
     *
     * @access private
     * @param Request $request
     * @return array
     */
    private function get_options($request) {
        // options for this entire curl object
        $options = $this->__get('options');
        if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
            $options[CURLOPT_FOLLOWLOCATION] = 1;
            $options[CURLOPT_MAXREDIRS] = 5;
        }
        $headers = $this->__get('headers');

        // append custom options for this specific request
        if ($request->options) {
            $options = $request->options + $options;
        }

        // set the request URL
        $options[CURLOPT_URL] = $request->url;

        // posting data w/ this request?
        if ($request->post_data) {
            $options[CURLOPT_POST] = 1;
            $options[CURLOPT_POSTFIELDS] = $request->post_data;
        }
        if ($headers) {
            $options[CURLOPT_HEADER] = 0;
            $options[CURLOPT_HTTPHEADER] = $headers;
        }

        return $options;
    }

    /**
     * @return void
     */
    public function __destruct() {
        unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
    }
}



function curl_get_file($url) {
	$ch = curl_init();
	
	curl_setopt_array($ch, array(
        CURLOPT_SSL_VERIFYPEER => 0,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_URL => $url,
    ));

	$output = curl_exec($ch);

	return array(
		'info' => curl_getinfo($ch),
		'body' => $output
	);

}


class prompt {
  var $tty;

  function __constuct() {

   if (substr(PHP_OS, 0, 3) == "WIN") {
     $this->tty = fopen("\con", "rb");
   } else {
     if (!($this->tty = fopen("/dev/tty", "r"))) {
       $this->tty = fopen("php://stdin", "r");
     }
   }

   if (!$this->tty) die("Can not open tty for input " );

  }

  function get($string, $length = 1024) {

	if (!$this->tty) $this->__constuct();

   echo $string;
   $result = trim(fgets($this->tty, $length));
   echo "\n";
   return $result;
  }
}
