|
@@ -4,15 +4,17 @@ declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace Hestia\System;
|
|
namespace Hestia\System;
|
|
|
|
|
|
|
|
-class HestiaApp {
|
|
|
|
|
-
|
|
|
|
|
|
|
+class HestiaApp
|
|
|
|
|
+{
|
|
|
protected const TMPDIR_DOWNLOADS="/tmp/hestia-webapp";
|
|
protected const TMPDIR_DOWNLOADS="/tmp/hestia-webapp";
|
|
|
|
|
+ protected $phpsupport = false;
|
|
|
|
|
|
|
|
- public function __construct() {
|
|
|
|
|
|
|
+ public function __construct()
|
|
|
|
|
+ {
|
|
|
@mkdir(self::TMPDIR_DOWNLOADS);
|
|
@mkdir(self::TMPDIR_DOWNLOADS);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function run(string $cmd, $args, &$cmd_result=null) : bool
|
|
|
|
|
|
|
+ public function run(string $cmd, $args, &$cmd_result=null): bool
|
|
|
{
|
|
{
|
|
|
$cli_script = HESTIA_CMD . '/' . basename($cmd);
|
|
$cli_script = HESTIA_CMD . '/' . basename($cmd);
|
|
|
$cli_arguments = '';
|
|
$cli_arguments = '';
|
|
@@ -25,19 +27,24 @@ class HestiaApp {
|
|
|
$cli_arguments = escapeshellarg($args);
|
|
$cli_arguments = escapeshellarg($args);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- exec ($cli_script . ' ' . $cli_arguments . ' 2>&1', $output, $exit_code);
|
|
|
|
|
|
|
+ exec($cli_script . ' ' . $cli_arguments . ' 2>&1', $output, $exit_code);
|
|
|
|
|
|
|
|
$result['code'] = $exit_code;
|
|
$result['code'] = $exit_code;
|
|
|
$result['args'] = $cli_arguments;
|
|
$result['args'] = $cli_arguments;
|
|
|
$result['raw'] = $output;
|
|
$result['raw'] = $output;
|
|
|
- $result['text'] = implode( PHP_EOL, $output);
|
|
|
|
|
|
|
+ $result['text'] = implode(PHP_EOL, $output);
|
|
|
$result['json'] = json_decode($result['text'], true);
|
|
$result['json'] = json_decode($result['text'], true);
|
|
|
$cmd_result = (object)$result;
|
|
$cmd_result = (object)$result;
|
|
|
-
|
|
|
|
|
|
|
+ if ($exit_code > 0) {
|
|
|
|
|
+ //log error message in nginx-error.log
|
|
|
|
|
+ trigger_error($result['text']);
|
|
|
|
|
+ //throw exception if command fails
|
|
|
|
|
+ throw new \Exception($result['text']);
|
|
|
|
|
+ }
|
|
|
return ($exit_code === 0);
|
|
return ($exit_code === 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function runUser(string $cmd, $args, &$cmd_result=null) : bool
|
|
|
|
|
|
|
+ public function runUser(string $cmd, $args, &$cmd_result=null): bool
|
|
|
{
|
|
{
|
|
|
if (!empty($args) && is_array($args)) {
|
|
if (!empty($args) && is_array($args)) {
|
|
|
array_unshift($args, $this->user());
|
|
array_unshift($args, $this->user());
|
|
@@ -53,13 +60,13 @@ class HestiaApp {
|
|
|
|
|
|
|
|
$signature = implode(PHP_EOL, $output);
|
|
$signature = implode(PHP_EOL, $output);
|
|
|
if (empty($signature)) {
|
|
if (empty($signature)) {
|
|
|
- throw new \Exception("Error reading composer signature");
|
|
|
|
|
|
|
+ throw new \Exception("Error reading composer signature");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
$composer_setup = self::TMPDIR_DOWNLOADS . DIRECTORY_SEPARATOR . 'composer-setup-' . $signature . '.php';
|
|
$composer_setup = self::TMPDIR_DOWNLOADS . DIRECTORY_SEPARATOR . 'composer-setup-' . $signature . '.php';
|
|
|
|
|
|
|
|
exec("wget https://getcomposer.org/installer --quiet -O " . escapeshellarg($composer_setup), $output, $return_code);
|
|
exec("wget https://getcomposer.org/installer --quiet -O " . escapeshellarg($composer_setup), $output, $return_code);
|
|
|
- if ($return_code !== 0 ) {
|
|
|
|
|
|
|
+ if ($return_code !== 0) {
|
|
|
throw new \Exception("Error downloading composer");
|
|
throw new \Exception("Error downloading composer");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -69,33 +76,34 @@ class HestiaApp {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
$install_folder = $this->getUserHomeDir() . DIRECTORY_SEPARATOR . '.composer';
|
|
$install_folder = $this->getUserHomeDir() . DIRECTORY_SEPARATOR . '.composer';
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!file_exists($install_folder)) {
|
|
if (!file_exists($install_folder)) {
|
|
|
exec(HESTIA_CMD .'v-rebuild-user '.$this -> user(), $output, $return_code);
|
|
exec(HESTIA_CMD .'v-rebuild-user '.$this -> user(), $output, $return_code);
|
|
|
- if($return_code !== 0){
|
|
|
|
|
|
|
+ if ($return_code !== 0) {
|
|
|
throw new \Exception("Unable to rebuild user");
|
|
throw new \Exception("Unable to rebuild user");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
$this->runUser('v-run-cli-cmd', ["/usr/bin/php", $composer_setup, "--quiet", "--install-dir=".$install_folder, "--filename=composer", "--$version" ], $status);
|
|
$this->runUser('v-run-cli-cmd', ["/usr/bin/php", $composer_setup, "--quiet", "--install-dir=".$install_folder, "--filename=composer", "--$version" ], $status);
|
|
|
|
|
|
|
|
unlink($composer_setup);
|
|
unlink($composer_setup);
|
|
|
|
|
|
|
|
- if ($status->code !== 0 ) {
|
|
|
|
|
|
|
+ if ($status->code !== 0) {
|
|
|
throw new \Exception("Error installing composer");
|
|
throw new \Exception("Error installing composer");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- public function updateComposer($version){
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public function updateComposer($version)
|
|
|
|
|
+ {
|
|
|
$this->runUser('v-run-cli-cmd', ["composer", "selfupdate","--$version"]);
|
|
$this->runUser('v-run-cli-cmd', ["composer", "selfupdate","--$version"]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function runComposer($args, &$cmd_result=null,$version=1) : bool
|
|
|
|
|
|
|
+ public function runComposer($args, &$cmd_result=null, $version=1): bool
|
|
|
{
|
|
{
|
|
|
$composer = $this->getUserHomeDir() . DIRECTORY_SEPARATOR . '.composer' . DIRECTORY_SEPARATOR . 'composer';
|
|
$composer = $this->getUserHomeDir() . DIRECTORY_SEPARATOR . '.composer' . DIRECTORY_SEPARATOR . 'composer';
|
|
|
- if(!is_file($composer)) {
|
|
|
|
|
|
|
+ if (!is_file($composer)) {
|
|
|
$this->installComposer($version);
|
|
$this->installComposer($version);
|
|
|
- }else{
|
|
|
|
|
|
|
+ } else {
|
|
|
$this->updateComposer($version);
|
|
$this->updateComposer($version);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -108,21 +116,34 @@ class HestiaApp {
|
|
|
return $this->runUser('v-run-cli-cmd', $args, $cmd_result);
|
|
return $this->runUser('v-run-cli-cmd', $args, $cmd_result);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function runWp($args, &$cmd_result=null): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ $wp = $this->getUserHomeDir() . DIRECTORY_SEPARATOR . '.wp' . DIRECTORY_SEPARATOR . 'wp';
|
|
|
|
|
+ if (!is_file($wp)) {
|
|
|
|
|
+ $this -> runUser('v-add-user-wp-cli');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $this->runUser('v-run-cli-cmd', [$wp, 'cli', 'update']);
|
|
|
|
|
+ }
|
|
|
|
|
+ array_unshift($args, $wp);
|
|
|
|
|
+
|
|
|
|
|
+ return $this->runUser('v-run-cli-cmd', $args, $cmd_result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Logged in user
|
|
// Logged in user
|
|
|
- public function realuser() : string
|
|
|
|
|
|
|
+ public function realuser(): string
|
|
|
{
|
|
{
|
|
|
return $_SESSION['user'];
|
|
return $_SESSION['user'];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Effective user
|
|
// Effective user
|
|
|
- public function user() : string
|
|
|
|
|
|
|
+ public function user(): string
|
|
|
{
|
|
{
|
|
|
$user = $this->realuser();
|
|
$user = $this->realuser();
|
|
|
if ($_SESSION['userContext'] === 'admin' && !empty($_SESSION['look'])) {
|
|
if ($_SESSION['userContext'] === 'admin' && !empty($_SESSION['look'])) {
|
|
|
$user = $_SESSION['look'];
|
|
$user = $_SESSION['look'];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if(strpos($user, DIRECTORY_SEPARATOR) !== false) {
|
|
|
|
|
|
|
+ if (strpos($user, DIRECTORY_SEPARATOR) !== false) {
|
|
|
throw new \Exception("illegal characters in username");
|
|
throw new \Exception("illegal characters in username");
|
|
|
}
|
|
}
|
|
|
return $user;
|
|
return $user;
|
|
@@ -134,25 +155,65 @@ class HestiaApp {
|
|
|
return $info['dir'];
|
|
return $info['dir'];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function userOwnsDomain(string $domain) : bool
|
|
|
|
|
|
|
+ public function userOwnsDomain(string $domain): bool
|
|
|
{
|
|
{
|
|
|
return $this->runUser('v-list-web-domain', [$domain, 'json']);
|
|
return $this->runUser('v-list-web-domain', [$domain, 'json']);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function databaseAdd(string $dbname, string $dbuser, string $dbpass)
|
|
|
|
|
|
|
+ public function databaseAdd(string $dbname, string $dbuser, string $dbpass, string $charset = 'utf8mb4')
|
|
|
{
|
|
{
|
|
|
- $v_password = tempnam("/tmp","hst");
|
|
|
|
|
|
|
+ $v_password = tempnam("/tmp", "hst");
|
|
|
$fp = fopen($v_password, "w");
|
|
$fp = fopen($v_password, "w");
|
|
|
fwrite($fp, $dbpass."\n");
|
|
fwrite($fp, $dbpass."\n");
|
|
|
fclose($fp);
|
|
fclose($fp);
|
|
|
- $status = $this->runUser('v-add-database', [$dbname, $dbuser, $v_password]);
|
|
|
|
|
|
|
+ $status = $this->runUser('v-add-database', [$dbname, $dbuser, $v_password, 'mysql', 'localhost', $charset]);
|
|
|
unlink($v_password);
|
|
unlink($v_password);
|
|
|
return $status;
|
|
return $status;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- public function changeWebTemplate(string $domain, string $template){
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public function changeWebTemplate(string $domain, string $template)
|
|
|
|
|
+ {
|
|
|
$status = $this->runUser('v-change-web-domain-tpl', [$domain, $template]);
|
|
$status = $this->runUser('v-change-web-domain-tpl', [$domain, $template]);
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ public function changeBackendTemplate(string $domain, string $template)
|
|
|
|
|
+ {
|
|
|
|
|
+ $status = $this->runUser('v-change-web-domain-backend-tpl', [$domain, $template]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function listSuportedPHP()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!$this -> phpsupport) {
|
|
|
|
|
+ $status = $this -> run('v-list-sys-php', 'json', $result);
|
|
|
|
|
+ $this -> phpsupport = $result -> json;
|
|
|
|
|
+ }
|
|
|
|
|
+ return $this -> phpsupport;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ Return highest available supported php version
|
|
|
|
|
+ Eg: Package requires: 7.3 or 7.4 and system has 8.0 and 7.4 it will return 7.4
|
|
|
|
|
+ Package requires: 8.0 or 8.1 and system has 8.0 and 7.4 it will return 8.0
|
|
|
|
|
+ Package requires: 7.4 or 8.0 and system has 8.0 and 7.4 it will return 8.0
|
|
|
|
|
+ If package isn't supported by the available php version false will returned
|
|
|
|
|
+ */
|
|
|
|
|
+ public function getSupportedPHP($support)
|
|
|
|
|
+ {
|
|
|
|
|
+ $versions = $this -> listSuportedPHP();
|
|
|
|
|
+ $supported = false;
|
|
|
|
|
+ $supported_versions = array();
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($versions as $version) {
|
|
|
|
|
+ if (in_array($version, $support)) {
|
|
|
|
|
+ $supported = true;
|
|
|
|
|
+ $supported_versions[] = $version;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if ($supported) {
|
|
|
|
|
+ return $supported_versions[count($supported_versions) - 1];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
public function getWebDomainIp(string $domain)
|
|
public function getWebDomainIp(string $domain)
|
|
|
{
|
|
{
|
|
@@ -163,13 +224,13 @@ class HestiaApp {
|
|
|
|
|
|
|
|
public function getWebDomainPath(string $domain)
|
|
public function getWebDomainPath(string $domain)
|
|
|
{
|
|
{
|
|
|
- return Util::join_paths( $this->getUserHomeDir() , "web", $domain);
|
|
|
|
|
|
|
+ return Util::join_paths($this->getUserHomeDir(), "web", $domain);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function downloadUrl(string $src, $path=null, &$result=null)
|
|
public function downloadUrl(string $src, $path=null, &$result=null)
|
|
|
{
|
|
{
|
|
|
- if (strpos($src,'http://') !== 0 &&
|
|
|
|
|
- strpos($src,'https://')!== 0 ) {
|
|
|
|
|
|
|
+ if (strpos($src, 'http://') !== 0 &&
|
|
|
|
|
+ strpos($src, 'https://')!== 0) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -178,11 +239,11 @@ class HestiaApp {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if(!preg_match('/URL:\s*(.+?)\s*\[(.+?)\]\s*->\s*"(.+?)"/', implode(PHP_EOL, $output), $matches)) {
|
|
|
|
|
|
|
+ if (!preg_match('/URL:\s*(.+?)\s*\[(.+?)\]\s*->\s*"(.+?)"/', implode(PHP_EOL, $output), $matches)) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if(empty($matches) || count($matches) != 4) {
|
|
|
|
|
|
|
+ if (empty($matches) || count($matches) != 4) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -200,8 +261,8 @@ class HestiaApp {
|
|
|
|
|
|
|
|
if (realpath($src)) {
|
|
if (realpath($src)) {
|
|
|
$archive_file = $src;
|
|
$archive_file = $src;
|
|
|
- } else {
|
|
|
|
|
- if( !$this->downloadUrl($src, null, $download_result) ) {
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (!$this->downloadUrl($src, null, $download_result)) {
|
|
|
throw new \Exception("Error downloading archive");
|
|
throw new \Exception("Error downloading archive");
|
|
|
}
|
|
}
|
|
|
$archive_file = $download_result->file;
|
|
$archive_file = $download_result->file;
|