Skip to content
nginx_plugin.inc.php 174 KiB
Newer Older
		$custom_sendmail_path = false;
		if($custom_php_ini_settings != ''){
			// Make sure we only have Unix linebreaks
			$custom_php_ini_settings = str_replace("\r\n", "\n", $custom_php_ini_settings);
			$custom_php_ini_settings = str_replace("\r", "\n", $custom_php_ini_settings);
			$ini_settings = explode("\n", $custom_php_ini_settings);
			if(is_array($ini_settings) && !empty($ini_settings)){
				$ini_settings = str_replace('{WEBROOT}', $data['new']['document_root'].'/web', $ini_settings);
				foreach($ini_settings as $ini_setting){
					$ini_setting = trim($ini_setting);
					if(substr($ini_setting, 0, 1) == ';') continue;
					if(substr($ini_setting, 0, 1) == '#') continue;
					if(substr($ini_setting, 0, 2) == '//') continue;
					list($key, $value) = explode('=', $ini_setting, 2);
						if($key == 'session.save_path') $custom_session_save_path = true;
						if($key == 'sendmail_path') $custom_sendmail_path = true;
						case '0':
							// PHP-FPM might complain about invalid boolean value if you use 0
							$value = 'off';
						case '1':
						case 'on':
						case 'off':
						case 'true':
						case 'false':
						case 'yes':
						case 'no':
							$final_php_ini_settings[] = array('ini_setting' => 'php_admin_flag['.$key.'] = '.$value);
							break;
						default:
							$final_php_ini_settings[] = array('ini_setting' => 'php_admin_value['.$key.'] = '.$value);
		$tpl->setVar('custom_session_save_path', ($custom_session_save_path ? 'y' : 'n'));
		$tpl->setVar('custom_sendmail_path', ($custom_sendmail_path ? 'y' : 'n'));
		$tpl->setLoop('custom_php_ini_settings', $final_php_ini_settings);

		$app->system->file_put_contents($pool_dir.$pool_name.'.conf', $tpl->grab());
		$app->log('Writing the PHP-FPM config file: '.$pool_dir.$pool_name.'.conf', LOGLEVEL_DEBUG);
		// delete pool in all other PHP versions
		$default_pool_dir = trim($web_config['php_fpm_pool_dir']);
		if(substr($default_pool_dir, -1) != '/') $default_pool_dir .= '/';
		if($default_pool_dir != $pool_dir){
			if ( @is_file($default_pool_dir.$pool_name.'.conf') ) {
				$app->system->unlink($default_pool_dir.$pool_name.'.conf');
				$app->log('Removed PHP-FPM config file: '.$default_pool_dir.$pool_name.'.conf', LOGLEVEL_DEBUG);
				$app->services->restartService('php-fpm', 'reload:'.$conf['init_scripts'].'/'.$web_config['php_fpm_init_script']);
		$php_versions = $app->db->queryAllRecords("SELECT * FROM server_php WHERE php_fpm_init_script != '' AND php_fpm_ini_dir != '' AND php_fpm_pool_dir != '' AND server_id = ?", $conf["server_id"]);
		if(is_array($php_versions) && !empty($php_versions)){
			foreach($php_versions as $php_version){
Marius Cramer's avatar
Marius Cramer committed
				$php_version['php_fpm_pool_dir'] = trim($php_version['php_fpm_pool_dir']);
				if(substr($php_version['php_fpm_pool_dir'], -1) != '/') $php_version['php_fpm_pool_dir'] .= '/';
				if($php_version['php_fpm_pool_dir'] != $pool_dir){
					if ( @is_file($php_version['php_fpm_pool_dir'].$pool_name.'.conf') ) {
						$app->system->unlink($php_version['php_fpm_pool_dir'].$pool_name.'.conf');
						$app->log('Removed PHP-FPM config file: '.$php_version['php_fpm_pool_dir'].$pool_name.'.conf', LOGLEVEL_DEBUG);
						$app->services->restartService('php-fpm', 'reload:'.$php_version['php_fpm_init_script']);
					}
				}
			}
		}
		// Reload current PHP-FPM after all others
		sleep(1);
		if(!$default_php_fpm){
			$app->services->restartService('php-fpm', 'reload:'.$custom_php_fpm_init_script);
			$app->services->restartService('php-fpm', 'reload:'.$conf['init_scripts'].'/'.$web_config['php_fpm_init_script']);
	//* Delete the PHP-FPM pool configuration file
	private function php_fpm_pool_delete ($data, $web_config) {
		$default_php_fpm = true;

		if($data['old']['server_php_id'] != 0 && $data['old']['php'] != 'no'){
			$tmp_php = $app->db->queryOneRecord('SELECT * FROM server_php WHERE server_php_id = ?', $data['old']['server_php_id']);
			if($tmp_php) {
				$default_php_fpm = false;
				$custom_php_fpm_ini_dir = $tmp_php['php_fpm_ini_dir'];
				$custom_php_fpm_init_script = $tmp_php['php_fpm_init_script'];
				$custom_php_fpm_pool_dir = $tmp_php['php_fpm_pool_dir'];
				if(substr($custom_php_fpm_ini_dir, -1) != '/') $custom_php_fpm_ini_dir .= '/';
			}
			$pool_dir = $web_config['php_fpm_pool_dir'];
		} else {
			$pool_dir = $custom_php_fpm_pool_dir;
		}
Marius Cramer's avatar
Marius Cramer committed
		$pool_dir = trim($pool_dir);

		if(substr($pool_dir, -1) != '/') $pool_dir .= '/';
		$pool_name = 'web'.$data['old']['domain_id'];
		if ( @is_file($pool_dir.$pool_name.'.conf') ) {
			$app->system->unlink($pool_dir.$pool_name.'.conf');
			$app->log('Removed PHP-FPM config file: '.$pool_dir.$pool_name.'.conf', LOGLEVEL_DEBUG);
		// delete pool in all other PHP versions
		$default_pool_dir = trim($web_config['php_fpm_pool_dir']);
		if(substr($default_pool_dir, -1) != '/') $default_pool_dir .= '/';
		if($default_pool_dir != $pool_dir){
			if ( @is_file($default_pool_dir.$pool_name.'.conf') ) {
				$app->system->unlink($default_pool_dir.$pool_name.'.conf');
				$app->log('Removed PHP-FPM config file: '.$default_pool_dir.$pool_name.'.conf', LOGLEVEL_DEBUG);
				$app->services->restartService('php-fpm', 'reload:'.$conf['init_scripts'].'/'.$web_config['php_fpm_init_script']);
		$php_versions = $app->db->queryAllRecords("SELECT * FROM server_php WHERE php_fpm_init_script != '' AND php_fpm_ini_dir != '' AND php_fpm_pool_dir != '' AND server_id = ?", $data['old']['server_id']);
		if(is_array($php_versions) && !empty($php_versions)){
			foreach($php_versions as $php_version){
Marius Cramer's avatar
Marius Cramer committed
				$php_version['php_fpm_pool_dir'] = trim($php_version['php_fpm_pool_dir']);
				if(substr($php_version['php_fpm_pool_dir'], -1) != '/') $php_version['php_fpm_pool_dir'] .= '/';
				if($php_version['php_fpm_pool_dir'] != $pool_dir){
					if ( @is_file($php_version['php_fpm_pool_dir'].$pool_name.'.conf') ) {
						$app->system->unlink($php_version['php_fpm_pool_dir'].$pool_name.'.conf');
						$app->log('Removed PHP-FPM config file: '.$php_version['php_fpm_pool_dir'].$pool_name.'.conf', LOGLEVEL_DEBUG);
						$app->services->restartService('php-fpm', 'reload:'.$php_version['php_fpm_init_script']);
		// Reload current PHP-FPM after all others
		sleep(1);
		if(!$default_php_fpm){
			$app->services->restartService('php-fpm', 'reload:'.$custom_php_fpm_init_script);
			$app->services->restartService('php-fpm', 'reload:'.$conf['init_scripts'].'/'.$web_config['php_fpm_init_script']);
	private function nginx_replace($matches){
		$location = 'location'.($matches[1] != '' ? ' '.$matches[1] : '').' '.$matches[2].' '.$matches[3];
		if($matches[4] == '##merge##' || $matches[7] == '##merge##') $location .= ' ##merge##';
		if($matches[4] == '##delete##' || $matches[7] == '##delete##') $location .= ' ##delete##';
		$location .= "\n";
		$location .= $matches[5]."\n";
		$location .= $matches[6];
		return $location;
	}
	private function nginx_merge_locations($vhost_conf) {
		global $app, $conf;
        if(preg_match('/##subroot (.+?)\s*##/', $vhost_conf, $subroot)) {
            if(!preg_match('/^(?:[a-z0-9\/_-]|\.(?!\.))+$/iD', $subroot[1])) {
                $app->log('Token ##subroot is unsecure (server ID: '.$conf['server_id'].').', LOGLEVEL_WARN);
            } else {
                $insert_pos = strpos($vhost_conf, ';', strpos($vhost_conf, 'root '));
                $vhost_conf = substr_replace($vhost_conf, ltrim($subroot[1], '/'), $insert_pos, 0);
            }
        }
		// if whole location block is in one line, split it up into multiple lines
		if(is_array($lines) && !empty($lines)){
			$linecount = sizeof($lines);
			for($h=0;$h<$linecount;$h++){
				// remove comments
				if(substr(trim($lines[$h]), 0, 1) == '#'){
				$lines[$h] = rtrim($lines[$h]);
				/*
				if(substr(ltrim($lines[$h]), 0, 8) == 'location' && strpos($lines[$h], '{') !== false && strpos($lines[$h], ';') !== false){
					$lines[$h] = str_replace("{", "{\n", $lines[$h]);
					$lines[$h] = str_replace(";", ";\n", $lines[$h]);
					if(strpos($lines[$h], '##merge##') !== false){
						$lines[$h] = str_replace('##merge##', '', $lines[$h]);
						$lines[$h] = substr($lines[$h],0,strpos($lines[$h], '{')).' ##merge##'.substr($lines[$h],strpos($lines[$h], '{')+1);
					}
				}
				if(substr(ltrim($lines[$h]), 0, 8) == 'location' && strpos($lines[$h], '{') !== false && strpos($lines[$h], '}') !== false && strpos($lines[$h], ';') === false){
					$lines[$h] = str_replace("{", "{\n", $lines[$h]);
					if(strpos($lines[$h], '##merge##') !== false){
						$lines[$h] = str_replace('##merge##', '', $lines[$h]);
						$lines[$h] = substr($lines[$h],0,strpos($lines[$h], '{')).' ##merge##'.substr($lines[$h],strpos($lines[$h], '{')+1);
					}
				}
				*/
				$pattern = '/^[^\S\n]*location[^\S\n]+(?:(.+)[^\S\n]+)?(.+)[^\S\n]*(\{)[^\S\n]*(##merge##|##delete##)?[^\S\n]*(.+)[^\S\n]*(\})[^\S\n]*(##merge##|##delete##)?[^\S\n]*$/';
				$lines[$h] = preg_replace_callback($pattern, array($this, 'nginx_replace') , $lines[$h]);
			}
		}
		$vhost_conf = implode("\n", $lines);
		unset($lines);
		unset($linecount);
		$lines = explode("\n", $vhost_conf);

		if(is_array($lines) && !empty($lines)){
			$locations = array();
			$locations_to_delete = array();
			$islocation = false;
			$linecount = sizeof($lines);
			$server_count = 0;

			for($i=0;$i<$linecount;$i++){
				$l = trim($lines[$i]);
				if(substr($l, 0, 8) == 'server {') $server_count += 1;
				if($server_count > 1) break;
				if(substr($l, 0, 8) == 'location' && !$islocation){
					// Remove unnecessary whitespace
					$l = preg_replace('/\s\s+/', ' ', $l);
					$loc_parts = explode(' ', $l);
					// see http://wiki.nginx.org/HttpCoreModule#location
					if($loc_parts[1] == '=' || $loc_parts[1] == '~' || $loc_parts[1] == '~*' || $loc_parts[1] == '^~'){
						$location = $loc_parts[1].' '.$loc_parts[2];
					} else {
						$location = $loc_parts[1];
					}
					unset($loc_parts);
					if(!isset($locations[$location]['action'])) $locations[$location]['action'] = 'replace';
					if(substr($l, -9) == '##merge##') $locations[$location]['action'] = 'merge';
					if(substr($l, -10) == '##delete##') $locations[$location]['action'] = 'delete';
					if(!isset($locations[$location]['open_tag'])) $locations[$location]['open_tag'] = '        location '.$location.' {';
					if(!isset($locations[$location]['location']) || $locations[$location]['action'] == 'replace') $locations[$location]['location'] = '';
					if($locations[$location]['action'] == 'delete') $locations_to_delete[] = $location;
					if(!isset($locations[$location]['end_tag'])) $locations[$location]['end_tag'] = '        }';
					if(!isset($locations[$location]['start_line'])) $locations[$location]['start_line'] = $i;

					unset($lines[$i]);
						$openingbracketpos = strrpos($l, '{');
						if($openingbracketpos !== false){
						$closingbracketpos = strrpos($l, '}');
						if($closingbracketpos !== false && $level > 0 && $closingbracketpos >= intval($openingbracketpos)){
							$level -= 1;
							$locations[$location]['location'] .= $lines[$i]."\n";
						} elseif($closingbracketpos !== false && $level == 0 && $closingbracketpos >= intval($openingbracketpos)){
							$islocation = false;
						} else {
							$locations[$location]['location'] .= $lines[$i]."\n";
						}
						unset($lines[$i]);
					}
			if(is_array($locations) && !empty($locations)){
				if(is_array($locations_to_delete) && !empty($locations_to_delete)){
					foreach($locations_to_delete as $location_to_delete){
						if(isset($locations[$location_to_delete])) unset($locations[$location_to_delete]);
					}
				}
				foreach($locations as $key => $val){
					$new_location = $val['open_tag']."\n".$val['location'].$val['end_tag'];
					$lines[$val['start_line']] = $new_location;
				}
			}
			ksort($lines);
			$vhost_conf = implode("\n", $lines);
		}

	function client_delete($event_name, $data) {
		$app->uses("getconf");
		$web_config = $app->getconf->get_server_config($conf["server_id"], 'web');
		$client_id = intval($data['old']['client_id']);
		if($client_id > 0) {
			$client_dir = $web_config['website_basedir'].'/clients/client'.$client_id;
			if(is_dir($client_dir) && !stristr($client_dir, '..')) {
				// remove symlinks from $client_dir
				$files = array_diff(scandir($client_dir), array('.', '..'));
				if(is_array($files) && !empty($files)){
					foreach($files as $file){
						if(is_link($client_dir.'/'.$file)){
							unlink($client_dir.'/'.$file);
							$app->log('Removed symlink: '.$client_dir.'/'.$file, LOGLEVEL_DEBUG);
				$app->log('Removed client directory: '.$client_dir, LOGLEVEL_DEBUG);
			if($app->system->is_group('client'.$client_id)){
				$app->system->exec_safe('groupdel ?', 'client'.$client_id);
				$app->log('Removed group client'.$client_id, LOGLEVEL_DEBUG);
	private function _checkTcp ($host, $port) {

		$fp = @fsockopen($host, $port, $errno, $errstr, 2);

	private function _rewrite_quote($string) {
		return str_replace(array('.', '*', '?', '+'), array('\\.', '\\*', '\\?', '\\+'), $string);
	}

	private function url_is_local($hostname, $domain_id){
		global $app;

		// ORDER BY clause makes sure wildcard subdomains (*) are listed last in the result array so that we can find direct matches first
		$webs = $app->db->queryAllRecords("SELECT * FROM web_domain WHERE active = 'y' ORDER BY subdomain ASC");
		if(is_array($webs) && !empty($webs)){
			foreach($webs as $web){
				// web domain doesn't match hostname
				if(substr($hostname, -strlen($web['domain'])) != $web['domain']) continue;
				// own vhost and therefore server {} container of its own
				//if($web['type'] == 'vhostsubdomain' || $web['type'] == 'vhostalias') continue;
				// alias domains/subdomains using rewrites and therefore a server {} container of their own
				//if(($web['type'] == 'alias' || $web['type'] == 'subdomain') && $web['redirect_type'] != '' && $web['redirect_path'] != '') continue;
				if($web['subdomain'] == '*'){
					$pattern = '/\.?'.str_replace('.', '\.', $web['domain']).'$/i';
				}
				if($web['subdomain'] == 'none'){
					if($web['domain'] == $hostname){
						if($web['domain_id'] == $domain_id || $web['parent_domain_id'] == $domain_id){
							// own vhost and therefore server {} container of its own
							if($web['type'] == 'vhostsubdomain' || $web['type'] == 'vhostalias') return false;
							// alias domains/subdomains using rewrites and therefore a server {} container of their own
							if(($web['type'] == 'alias' || $web['type'] == 'subdomain') && $web['redirect_type'] != '' && $web['redirect_path'] != '') return false;
							return true;
						} else {
							return false;
						}
					}
					$pattern = '/^'.str_replace('.', '\.', $web['domain']).'$/i';
				}
				if($web['subdomain'] == 'www'){
					if($web['domain'] == $hostname || $web['subdomain'].'.'.$web['domain'] == $hostname){
						if($web['domain_id'] == $domain_id || $web['parent_domain_id'] == $domain_id){
							// own vhost and therefore server {} container of its own
							if($web['type'] == 'vhostsubdomain' || $web['type'] == 'vhostalias') return false;
							// alias domains/subdomains using rewrites and therefore a server {} container of their own
							if(($web['type'] == 'alias' || $web['type'] == 'subdomain') && $web['redirect_type'] != '' && $web['redirect_path'] != '') return false;
							return true;
						} else {
							return false;
						}
					}
					$pattern = '/^(www\.)?'.str_replace('.', '\.', $web['domain']).'$/i';
				}
				if(preg_match($pattern, $hostname)){
					if($web['domain_id'] == $domain_id || $web['parent_domain_id'] == $domain_id){
						// own vhost and therefore server {} container of its own
						if($web['type'] == 'vhostsubdomain' || $web['type'] == 'vhostalias') return false;
						// alias domains/subdomains using rewrites and therefore a server {} container of their own
						if(($web['type'] == 'alias' || $web['type'] == 'subdomain') && $web['redirect_type'] != '' && $web['redirect_path'] != '') return false;
						return true;
					} else {
						return false;
					}
				}
			}
		}
	private function get_seo_redirects($web, $prefix = '', $force_subdomain = false){
		// $force_subdomain = 'none|www'
		$seo_redirects = array();
		if(substr($web['domain'], 0, 2) === '*.') $web['subdomain'] = '*';
		if(($web['subdomain'] == 'www' || $web['subdomain'] == '*') && $force_subdomain != 'www'){
			if($web['seo_redirect'] == 'non_www_to_www'){
				$seo_redirects[$prefix.'seo_redirect_origin_domain'] = $web['domain'];
				$seo_redirects[$prefix.'seo_redirect_target_domain'] = 'www.'.$web['domain'];
				$seo_redirects[$prefix.'seo_redirect_operator'] = '=';
			}
			if($web['seo_redirect'] == '*_domain_tld_to_www_domain_tld'){
				// ^(example\.com|(?!\bwww\b)\.example\.com)$
				// ^(example\.com|((?:\w+(?:-\w+)*\.)*)((?!www\.)\w+(?:-\w+)*)(\.example\.com))$
				$seo_redirects[$prefix.'seo_redirect_origin_domain'] = '^('.str_replace('.', '\.', $web['domain']).'|((?:\w+(?:-\w+)*\.)*)((?!www\.)\w+(?:-\w+)*)(\.'.str_replace('.', '\.', $web['domain']).'))$';
				$seo_redirects[$prefix.'seo_redirect_target_domain'] = 'www.'.$web['domain'];
				$seo_redirects[$prefix.'seo_redirect_operator'] = '~*';
			}
			if($web['seo_redirect'] == '*_to_www_domain_tld'){
				$seo_redirects[$prefix.'seo_redirect_origin_domain'] = 'www.'.$web['domain'];
				$seo_redirects[$prefix.'seo_redirect_target_domain'] = 'www.'.$web['domain'];
				$seo_redirects[$prefix.'seo_redirect_operator'] = '!=';
			}
		}
		if($force_subdomain != 'none'){
			if($web['seo_redirect'] == 'www_to_non_www'){
				$seo_redirects[$prefix.'seo_redirect_origin_domain'] = 'www.'.$web['domain'];
				$seo_redirects[$prefix.'seo_redirect_target_domain'] = $web['domain'];
				$seo_redirects[$prefix.'seo_redirect_operator'] = '=';
			}
			if($web['seo_redirect'] == '*_domain_tld_to_domain_tld'){
				// ^(.+)\.example\.com$
				$seo_redirects[$prefix.'seo_redirect_origin_domain'] = '^(.+)\.'.str_replace('.', '\.', $web['domain']).'$';
				$seo_redirects[$prefix.'seo_redirect_target_domain'] = $web['domain'];
				$seo_redirects[$prefix.'seo_redirect_operator'] = '~*';
			}
			if($web['seo_redirect'] == '*_to_domain_tld'){
				$seo_redirects[$prefix.'seo_redirect_origin_domain'] = $web['domain'];
				$seo_redirects[$prefix.'seo_redirect_target_domain'] = $web['domain'];
				$seo_redirects[$prefix.'seo_redirect_operator'] = '!=';
			}
		}
		return $seo_redirects;
	}


		$app->uses('system');

		if (isset($this->jailkit_config) && isset($this->jailkit_config['jailkit_hardlinks'])) {
			if ($this->jailkit_config['jailkit_hardlinks'] == 'yes') {
Jesse Norell's avatar
Jesse Norell committed
				$options = array('hardlink');
			} elseif ($this->jailkit_config['jailkit_hardlinks'] == 'no') {
				$options = array();
			}
		} else {
Jesse Norell's avatar
Jesse Norell committed
			$options = array('allow_hardlink');
		}

		// should move return here if $this->website['new_jailkit_hash'] == $this->website['last_jailkit_hash'] ?

		// check if the chroot environment is created yet if not create it with a list of program sections from the config
		if (!is_dir($this->website['document_root'].'/etc/jailkit'))
		{
			$app->system->create_jailkit_chroot($this->website['document_root'], $this->jailkit_config['jailkit_chroot_app_sections'], $options);
			$app->log("Added jailkit chroot", LOGLEVEL_DEBUG);
			$app->load('tpl');

			$tpl = new tpl();
			$tpl->newTemplate("bash.bashrc.master");

			$tpl->setVar('jailkit_chroot', true);
			$tpl->setVar('domain', $this->website['domain']);
			$tpl->setVar('home_dir', $this->_get_home_dir(""));

			$bashrc = $this->website['document_root'].'/etc/bash.bashrc';
			if(@is_file($bashrc) || @is_link($bashrc)) unlink($bashrc);

			file_put_contents($bashrc, $tpl->grab());
			unset($tpl);

			$app->log("Added bashrc script: ".$bashrc, LOGLEVEL_DEBUG);

			$tpl = new tpl();
			$tpl->newTemplate("motd.master");

			$tpl->setVar('domain', $this->website['domain']);

			$motd = $this->website['document_root'].'/var/run/motd';
			if(@is_file($motd) || @is_link($motd)) unlink($motd);

			$app->system->file_put_contents($motd, $tpl->grab());

		} else {
			// force update existing jails
			$options[] = 'force';

			$sections = $this->jailkit_config['jailkit_chroot_app_sections'];
			$programs = $this->jailkit_config['jailkit_chroot_app_programs'] . ' '
				  . $this->jailkit_config['jailkit_chroot_cron_programs'];

			if ($this->website['new_jailkit_hash'] == $this->website['last_jailkit_hash']) {
				return;
			}

			$records = $app->db->queryAllRecords('SELECT web_folder FROM `web_domain` WHERE `parent_domain_id` = ? AND `document_root` = ? AND web_folder != \'\' AND web_folder IS NOT NULL AND `server_id` = ?', $this->website['domain_id'], $this->website['document_root'], $conf['server_id']);
			foreach ($records as $record) {
				$options[] = 'skip='.$record['web_folder'];
			}

			$app->system->update_jailkit_chroot($this->website['document_root'], $sections, $programs, $options);
		}

		// this gets last_jailkit_update out of sync with master db, but that is ok,
		// as it is only used as a timestamp to moderate the frequency of updating on the slaves
		$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW(), `last_jailkit_hash` = ? WHERE `document_root` = ?", $this->website['new_jailkit_hash'], $this->website['document_root']);
	}

	function _add_jailkit_programs($opts=array())
	{
		global $app;

		$app->uses('system');

		//copy over further programs and its libraries
		$app->system->create_jailkit_programs($this->website['document_root'], $this->jailkit_config['jailkit_chroot_app_programs'], $opts);
		$app->log("Added app programs to jailkit chroot", LOGLEVEL_DEBUG);

		$app->system->create_jailkit_programs($this->website['document_root'], $this->jailkit_config['jailkit_chroot_cron_programs'], $opts);
		$app->log("Added cron programs to jailkit chroot", LOGLEVEL_DEBUG);
	}

	function _get_home_dir($username)
	{
		return str_replace("[username]", $username, $this->jailkit_config['jailkit_chroot_home']);
	}

	function _add_jailkit_user()
	{
		global $app;

		// add the user to the chroot
		$jailkit_chroot_userhome = $this->_get_home_dir($this->website['system_user']);

		if(!is_dir($this->website['document_root'].'/etc')) $app->system->mkdir($this->website['document_root'].'/etc', 0755, true);
		if(!is_file($this->website['document_root'].'/etc/passwd')) $app->system->exec_safe('touch ?', $this->website['document_root'].'/etc/passwd');

		// IMPORTANT!
		// ALWAYS create the user. Even if the user was created before
		// if we check if the user exists, then a update (no shell -> jailkit) will not work
		// and the user has FULL ACCESS to the root of the server!
		$app->system->create_jailkit_user($this->website['system_user'], $this->website['document_root'], $jailkit_chroot_userhome);

		if(!is_dir($this->website['document_root'].$jailkit_chroot_userhome)) {
			$app->system->mkdir($this->website['document_root'].$jailkit_chroot_userhome, 0750, true);
			$app->system->chown($this->website['document_root'].$jailkit_chroot_userhome, $this->website['system_user']);
			$app->system->chgrp($this->website['document_root'].$jailkit_chroot_userhome, $this->website['system_group']);
		}
		$app->log("Added created jailkit user home in : ".$this->website['document_root'].$jailkit_chroot_userhome, LOGLEVEL_DEBUG);
	}

	private function _delete_jailkit_if_unused($parent_domain_id) {
		global $app, $conf;

		// get jail directory
		$parent_domain = $app->db->queryOneRecord("SELECT * FROM `web_domain` WHERE `domain_id` = ? OR `parent_domain_id` = ? AND `document_root` IS NOT NULL", $parent_domain_id, $parent_domain_id);
		if (!is_dir($parent_domain['document_root'])) {
			return;
		}

		// chroot is used by php-fpm
		if (isset($parent_domain['php_fpm_chroot']) && $parent_domain['php_fpm_chroot'] == 'y' && $parent_domain['php'] != 'no') {
			return;
		}

		// check for any shell_user using this jail
		$inuse = $app->db->queryOneRecord('SELECT shell_user_id FROM `shell_user` WHERE `parent_domain_id` = ? AND `chroot` = ?', $parent_domain_id, 'jailkit');
		if($inuse) {
			return;
		}

		// check for any cron job using this jail
		$inuse = $app->db->queryOneRecord('SELECT id FROM `cron` WHERE `parent_domain_id` = ? AND `type` = ?', $parent_domain_id, 'chrooted');
		if($inuse) {
			return;
		}

		$options = array();
		$records = $app->db->queryAllRecords('SELECT web_folder FROM `web_domain` WHERE `parent_domain_id` = ? AND `document_root` = ? AND web_folder != \'\' AND web_folder IS NOT NULL AND `server_id` = ?', $parent_domain_id, $parent_domain['document_root'], $conf['server_id']);
		foreach ($records as $record) {
			$options[] = 'skip='.$record['web_folder'];
		}

		$app->system->delete_jailkit_chroot($parent_domain['document_root'], $options);

		// this gets last_jailkit_update out of sync with master db, but that is ok,
		// as it is only used as a timestamp to moderate the frequency of updating on the slaves
		$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW(), `last_jailkit_hash` = NULL WHERE `document_root` = ?", $parent_domain['document_root']);
	}