Files
luos/workflow/engine/controllers/InstallerModule.php
2025-06-13 12:15:44 -04:00

464 lines
16 KiB
PHP

<?php
use ProcessMaker\Util\Common;
use ProcessMaker\Core\System;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
global $translation;
include PATH_LANGUAGECONT . "translation." . SYS_LANG;
class InstallerModule extends Controller
{
const MYSQL_VERSION_MAXIMUM_SUPPORTED = "5.7";
const PHP_VERSION_MINIMUM_SUPPORTED = "8.1";
const PHP_VERSION_NOT_SUPPORTED = "8.4";
public $path_config;
public $path_languages;
public $path_plugins;
public $path_xmlforms;
public $path_shared;
public $path_sep;
public $systemName;
public $link; #resource for database connection
/**
* Default name for connection
*
* @var string
*/
const CONNECTION_INSTALL = 'install';
const CONNECTION_TEST_INSTALL = 'testInstall';
/**
* Constructor
* We defined the paths for the installer
*/
public function __construct()
{
$this->path_config = PATH_CORE . 'config/';
$this->path_languages = PATH_CORE . 'content/languages/';
$this->path_plugins = PATH_CORE . 'plugins/';
$this->path_xmlforms = PATH_CORE . 'xmlform/';
$this->path_public = PATH_HOME . 'public_html/index.html';
$this->path_shared = PATH_TRUNK . 'shared/';
$this->path_sep = PATH_SEP;
$this->systemName = '';
$this->path_translations = PATH_CORE . 'js/labels/';
$this->path_translationsMafe = PATH_HOME . 'public_html/translations/';
}
public function index($httpData)
{
if (file_exists(FILE_PATHS_INSTALLED)) {
$this->setJSVar('messageError', G::LoadTranslation('ID_PROCESSMAKER_ALREADY_INSTALLED'));
$this->includeExtJS('installer/stopInstall');
$this->setView('installer/mainStopInstall');
G::RenderPage('publish', 'extJs');
return;
}
if ((strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') && (file_exists($this->path_shared . 'partner.info'))) {
$this->setJSVar('messageError', G::LoadTranslation('ID_NO_INSTALL'));
$this->includeExtJS('installer/stopInstall');
$this->setView('installer/mainStopInstall');
G::RenderPage('publish', 'extJs');
return;
}
$licenseContent = file_get_contents(PATH_TRUNK . 'LICENSE.txt');
$this->includeExtJS('installer/CardLayout', false);
$this->includeExtJS('installer/Wizard', false);
$this->includeExtJS('installer/Header', false);
$this->includeExtJS('installer/Card', false);
$this->includeExtJS('installer/installer_cards');
$this->includeExtJS('installer/main', false);
$this->setView('installer/main');
G::RenderPage('publish', 'extJs');
}
/**
* Set config connection
*
* @param string $nameConnection name Connection
* @param string $host
* @param string $user
* @param string $pass
* @param string $database
* @param int $port
* @param array $options
*
* @throws Exception
*/
public static function setNewConnection($nameConnection, $host, $user, $pass, $database, $port, $options = [])
{
try {
if (empty($port)) {
$dbHost = explode(':', $host);
$port = 3306;
if (count($dbHost) > 1) {
$port = $dbHost[1];
}
$host = $dbHost[0];
}
config(['database.connections.' . $nameConnection => [
'driver' => 'mysql',
'host' => $host,
'port' => $port,
'database' => $database,
'username' => $user,
'password' => $pass,
'unix_socket' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_0900_ai_ci',
'prefix' => '',
'strict' => false,
'engine' => 'InnoDB',
'options' => $options
]]);
DB::connection($nameConnection)->getPdo();
} catch (Exception $e) {
throw new Exception(G::LoadTranslation('ID_MYSQL_CREDENTIALS_WRONG'));
}
}
/**
* Get system information for review the requirements to install ProcessMaker
*
* @return object
*/
public function getSystemInfo()
{
$this->setResponseType('json');
// PHP info and verification
$phpVer = phpversion();
preg_match('/[0-9\.]+/', $phpVer, $match);
$phpVerNum = (float)$match[0];
$info = new stdclass();
$info->php = new stdclass();
$info->mysql = new stdclass();
$info->openssl = new stdclass();
$info->curl = new stdclass();
$info->dom = new stdclass();
$info->gd = new stdclass();
$info->multibyte = new stdclass();
$info->soap = new stdclass();
$info->ldap = new stdclass();
$info->memory = new stdclass();
$info->php->version = $phpVer;
$info->php->result = (
version_compare(phpversion(), self::PHP_VERSION_MINIMUM_SUPPORTED, '>=') &&
version_compare(phpversion(), self::PHP_VERSION_NOT_SUPPORTED, '<')) ? true : false;
// MYSQL info and verification
$info->mysql->result = false;
if (function_exists('mysqli_query')) {
$mysqlVer = mysqli_get_client_info();
preg_match('/[0-9\.]+/', $mysqlVer, $match);
$mysqlNum = (float)$match[0];
$info->mysql->version = 'Client API version ' . $mysqlVer;
$info->mysql->result = $mysqlNum >= 5.0;
}
// OpenSSL info
$info->openssl->result = false;
$info->openssl->version = G::LoadTranslation('ID_NOT_ENABLED');
if (function_exists('openssl_open')) {
$info->openssl->result = true;
$info->openssl->version = G::LoadTranslation('ID_ENABLED');
}
// Curl info
$info->curl->result = false;
$info->curl->version = G::LoadTranslation('ID_NOT_ENABLED');
if (function_exists('curl_version')) {
$info->curl->result = true;
$version = curl_version();
$info->curl->version = 'cURL ' . $version['version'];
$info->openssl->version = $version['ssl_version'];
}
// DOMDocument info
$info->dom->result = false;
$info->dom->version = G::LoadTranslation('ID_NOT_ENABLED');
if (class_exists('DOMDocument')) {
$info->dom->result = true;
$info->dom->version = G::LoadTranslation('ID_ENABLED');
}
// GD info
$info->gd->result = false;
$info->gd->version = G::LoadTranslation('ID_NOT_ENABLED');
if (function_exists('gd_info')) {
$info->gd->result = true;
$gdinfo = gd_info();
$info->gd->version = $gdinfo['GD Version'];
}
// Multibyte info
$info->multibyte->result = false;
$info->multibyte->version = G::LoadTranslation('ID_NOT_ENABLED');
if (function_exists('mb_check_encoding')) {
$info->multibyte->result = true;
$info->multibyte->version = G::LoadTranslation('ID_ENABLED');
}
// soap info
$info->soap->result = false;
$info->soap->version = G::LoadTranslation('ID_NOT_ENABLED');
if (class_exists('SoapClient')) {
$info->soap->result = true;
$info->soap->version = G::LoadTranslation('ID_ENABLED');
}
// ldap info
$info->ldap->result = false;
$info->ldap->version = G::LoadTranslation('ID_NOT_ENABLED');
if (function_exists('ldap_connect')) {
$info->ldap->result = true;
$info->ldap->version = G::LoadTranslation('ID_ENABLED');
}
// memory limit verification
$memory = (int)ini_get('memory_limit');
$info->memory->version = changeAbbreviationOfDirectives(ini_get('memory_limit'));
$info->memory->result = $memory > 255;
return $info;
}
public function is_dir_writable($path)
{
return G::is_writable_r($path);
}
/**
* Test db connection
*
* @return StdClass
*/
public function testConnection()
{
$this->setResponseType('json');
$info = new StdClass();
try {
$info = $this->testMySQLConnection();
} catch (Exception $e) {
$info->result = false;
$info->message = G::LoadTranslation('DBCONNECTIONS_MSGA');
}
return $info;
}
/**
* log the queries and other information to install.log,
* the install.log files should be placed in shared/logs
* for that reason we are using the $_REQUEST of pathShared
*/
private function installLog($text)
{
//if this function is called outside the createWorkspace, just returns and do nothing
if (!isset($_REQUEST['pathShared'])) {
return;
}
//log file is in shared/logs
$pathShared = trim($_REQUEST['pathShared']);
if (substr($pathShared, -1) !== '/') {
$pathShared .= '/';
}
$pathSharedLog = $pathShared . 'logs/';
G::verifyPath($pathSharedLog, true);
$logFile = $pathSharedLog . 'install.log';
if (!is_file($logFile)) {
G::mk_dir(dirname($pathShared));
$fpt = fopen($logFile, 'w');
if ($fpt !== null) {
fwrite($fpt, sprintf("%s %s\n", date('Y:m:d H:i:s'), '----- ' . G::LoadTranslation('ID_STARTING_LOG_FILE') . ' ------'));
fclose($fpt);
} else {
throw new Exception(G::LoadTranslation('ID_FILE_NOT_WRITEABLE', SYS_LANG, [$logFile]));
return $false;
}
}
$filter = new InputFilter();
$logFile = $filter->validateInput($logFile, 'path');
$fpt = fopen($logFile, 'a');
fwrite($fpt, sprintf("%s %s\n", date('Y:m:d H:i:s'), trim($text)));
fclose($fpt);
return true;
}
/**
* Display an error when processMaker is already installed
*
* @return void
*/
private function displayError()
{
$this->setJSVar('messageError', G::LoadTranslation('ID_PROCESSMAKER_ALREADY_INSTALLED'));
$this->includeExtJS('installer/stopInstall');
$this->setView('installer/mainStopInstall');
G::RenderPage('publish', 'extJs');
}
public function forceTogenerateTranslationsFiles($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, G::browserCacheFilesUrl(System::getServerProtocolHost() . "/js/ext/translation.en.js?r=" . rand(1, 10000)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
curl_exec($ch);
curl_close($ch);
}
/**
* query_sql_file send many statements to server
*
* @param string $file
* @param string $connection
* @return array $report
*/
public function mysqlFileQuery($file)
{
if (!is_file($file)) {
throw new Exception(G::LoadTranslation('ID_SQL_FILE_INVALID', SYS_LANG, [$file]));
return $false;
}
$this->installLog(G::LoadTranslation('ID_PROCESING', SYS_LANG, [$file]));
$startTime = microtime(true);
//New Update, to support more complex queries
$lines = file($file);
$previous = null;
DB::connection(self::CONNECTION_INSTALL)
->statement("SET NAMES 'utf8'");
foreach ($lines as $j => $line) {
$line = trim($line); // Remove comments from the script
if (strpos($line, '--') === 0) {
$line = substr($line, 0, strpos($line, '--'));
}
if (empty($line)) {
continue;
}
if (strpos($line, '#') === 0) {
$line = substr($line, 0, strpos($line, '#'));
}
if (empty($line)) {
continue;
}
// Concatenate the previous line, if any, with the current
if ($previous) {
$line = $previous . ' ' . $line;
}
$previous = null;
// If the current line doesnt end with ; then put this line together
// with the next one, thus supporting multi-line statements.
if (strrpos($line, ';') !== strlen($line) - 1) {
$previous = $line;
continue;
}
$line = substr($line, 0, strrpos($line, ';'));
DB::connection(self::CONNECTION_INSTALL)
->statement($line);
}
$endTime = microtime(true);
$this->installLog(G::LoadTranslation('ID_FILE_PROCESSED', SYS_LANG, [basename($file), $endTime - $startTime]));
return true;
}
/**
* Privates functions section, non callable by http request
*/
private function testMySQLConnection()
{
try {
$filter = new InputFilter();
$info = new StdClass();
$info->result = false;
$info->message = '';
if (!function_exists('mysqli_connect')) {
$info->message = G::LoadTranslation('ID_PHP_MYSQLI_NOT_INSTALL');
return $info;
}
$dataRequest = $_REQUEST;
$db_hostname = $filter->validateInput($dataRequest['db_hostname']);
$db_port = $filter->validateInput($dataRequest['db_port']);
$db_username = $filter->validateInput($dataRequest['db_username']);
$db_password = urlencode($dataRequest['db_password']);
$db_password = urldecode($filter->validateInput($db_password));
$fp = @fsockopen($db_hostname, $db_port, $errno, $errstr, 30);
if (!$fp) {
$info->message .= G::LoadTranslation('ID_CONNECTION_ERROR', SYS_LANG, ["$errstr ($errno)"]);
return $info;
}
$db_username = $filter->validateInput($db_username, 'nosql');
$db_hostname = $filter->validateInput($db_hostname, 'nosql');
self::setNewConnection(self::CONNECTION_TEST_INSTALL, $db_hostname, $db_username, $db_password, '', $db_port);
$query = "SELECT * FROM `information_schema`.`USER_PRIVILEGES` where (GRANTEE = \"'$db_username'@'$db_hostname'\" OR GRANTEE = \"'$db_username'@'%%'\") ";
$response = DB::connection(self::CONNECTION_TEST_INSTALL)->select($query);
if (!is_array($response)) {
$info->message .= G::LoadTranslation('ID_CONNECTION_ERROR_PRIVILEGE', SYS_LANG, [$db_username]);
return $info;
}
$info->message .= G::LoadTranslation('ID_MYSQL_SUCCESS_CONNECT');
$info->result = true;
} catch (Exception $e) {
$info->result = false;
$info->message = G::LoadTranslation('ID_MYSQL_CREDENTIALS_WRONG');
}
return $info;
}
/**
* Verify/create framework shared directory structure
*
*/
private function verifySharedFrameworkPaths($sharedPath)
{
// Check if $sharedPath ends with a slash and append if necessary
if (substr($sharedPath, -1) !== DIRECTORY_SEPARATOR) {
$sharedPath .= DIRECTORY_SEPARATOR;
}
$paths = [
$sharedPath . 'fonts' => 0770,
$sharedPath . 'framework' => 0770,
$sharedPath . 'framework' . DIRECTORY_SEPARATOR . 'cache' => 0770,
];
foreach ($paths as $path => $permission) {
if (!file_exists($path)) {
G::mk_dir($path, $permission);
}
if (!file_exists($path)) {
return false;
}
}
return true;
}
}