2022-08-29 00:26:46 +03:00
< ? php
require 'vendor/autoload.php' ;
2024-01-09 22:12:15 +03:00
use Amp\Http\Client\HttpClientBuilder ;
use Amp\Http\Client\Request ;
2024-12-05 22:45:51 +03:00
function domainIsOnServer ( $domain )
{
2024-12-05 23:31:49 +03:00
return file_exists ( '/etc/nginx/conf.d/' . $domain . '.conf' );
}
function wikiIsOnServer ( $path )
{
2024-12-06 10:04:16 +03:00
$configFile = glob ( '/home/*/' . $path . '/wakka.config.php' )[ 0 ] ? ? false ;
2024-12-07 18:35:31 +03:00
if ( ! $configFile ) {
return false ;
}
2025-01-24 12:11:02 +03:00
preg_match_all ( '#/home/(.*)/#mU' , $configFile , $user , PREG_SET_ORDER , 0 );
2024-12-20 10:32:07 +03:00
2024-12-07 18:35:31 +03:00
$wakkaConfig = [];
include_once ( $configFile );
return [
'path' => str_replace ( 'wakka.config.php' , '' , $configFile ),
'config' => $wakkaConfig ,
2024-12-20 10:32:07 +03:00
'user' => $user [ 0 ][ 1 ],
'group' => $user [ 0 ][ 1 ],
2024-12-07 18:35:31 +03:00
];
2024-12-05 22:45:51 +03:00
}
2023-04-21 10:25:37 +03:00
function checkIP ( $domain , $withWww = false , $noip6 = false )
2022-08-29 00:26:46 +03:00
{
2024-02-15 21:42:51 +03:00
if ( ! preg_match ( '/(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/' , strtolower ( $domain ))) {
throw new Exception ( 'not valid domain : "' . $domain . '".' );
2022-08-29 00:26:46 +03:00
}
2024-02-15 21:42:51 +03:00
$output = shell_exec ( 'ping -c1 -4 ' . $domain );
preg_match_all ( '/PING.*\(.*((\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}).*\)/m' , $output , $matches , PREG_SET_ORDER , 0 );
$currentip = $matches [ 0 ][ 1 ] ? ? null ;
if ( empty ( $currentip )) {
throw new Exception ( 'the domain ' . $domain . ' has no ip v4.' );
2022-08-29 00:26:46 +03:00
}
2024-02-15 21:42:51 +03:00
if ( $currentip !== $_SERVER [ 'ip4' ]) {
throw new Exception ( 'the current ip v4 address of ' . $domain . ' is ' . $currentip . '. it should be ' . $_SERVER [ 'ip4' ]);
}
if ( ! $noip6 ) {
$output = shell_exec ( 'ping -c1 -6 ' . $domain );
preg_match_all ( '/PING.*\((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\)/m' , $output , $matches , PREG_SET_ORDER , 0 );
$currentip6 = $matches [ 0 ][ 1 ] ? ? null ;
if ( empty ( $currentip6 )) {
throw new Exception ( 'the domain ' . $domain . ' has no ip v6.' );
}
if ( $currentip6 !== $_SERVER [ 'ip6' ]) {
throw new Exception ( 'the current ip v6 address of ' . $domain . ' is ' . $currentip6 . '. it should be ' . $_SERVER [ 'ip6' ]);
2023-04-21 08:22:10 +03:00
}
2022-08-29 00:26:46 +03:00
}
2024-02-15 21:42:51 +03:00
if ( $withWww ) {
$dnsentries = dns_get_record ( 'www.' . $domain , DNS_A + DNS_AAAA + DNS_CNAME );
$foundCnameEntry = false ;
$foundIp4 = false ;
$foundIp6 = false ;
foreach ( $dnsentries as $row ) {
if ( $row [ 'host' ] == 'www.' . $domain && $row [ 'type' ] == 'CNAME' && $row [ 'target' ] == $domain ) {
$foundCnameEntry = true ;
}
if ( $row [ 'host' ] == 'www.' . $domain && $row [ 'type' ] == 'A' && $row [ 'ip' ] == $_SERVER [ 'ip4' ]) {
$foundIp4 = true ;
}
if ( ! $noip6 ) {
if ( $row [ 'host' ] == 'www.' . $domain && $row [ 'type' ] == 'AAAA' && $row [ 'ipv6' ] == $_SERVER [ 'ip6' ]) {
$foundIp6 = true ;
}
}
}
if ( ! $foundCnameEntry ) {
if ( ! $foundIp4 ) {
throw new Exception ( 'the domain www.' . $domain . ' was not found in DNS record as a CNAME targeting ' . $domain . " \n " . ' or A entry to ' . $_SERVER [ 'ip4' ]);
}
if ( ! $noip6 && ! $foundIp6 ) {
throw new Exception ( 'the domain www.' . $domain . ' was not found in DNS record as a CNAME targeting ' . $domain . " \n " . ' or AAAA entry to ' . $_SERVER [ 'ip6' ] . '.' );
}
}
2023-03-31 19:43:21 +03:00
}
2024-02-15 21:42:51 +03:00
return true ;
2022-08-29 00:26:46 +03:00
}
function checkIfInstalled ( $domain )
{
2024-02-15 21:42:51 +03:00
exec ( 'find /home/* -maxdepth 1 -type d | grep ' . $domain , $output );
if ( ! empty ( $output )) {
throw new Exception ( 'the domain ' . $domain . ' was already found on the server.' );
}
2022-08-29 00:26:46 +03:00
}
function checkIfUserExist ( $user )
{
2024-02-15 21:42:51 +03:00
exec ( 'cut -d: -f1 /etc/passwd | grep ' . $user , $output );
return ! empty ( $output );
2022-08-29 00:26:46 +03:00
}
function generateUserFromDomain ( $domain , $recursive = null )
{
2024-02-15 21:42:51 +03:00
if ( $recursive == 100 ) {
throw new Exception ( 'Too much users found, 100 that is too much for ' . $domain );
2022-08-29 00:26:46 +03:00
}
2024-02-15 21:42:51 +03:00
$user = str_split ( str_replace ([ $_SERVER [ 'maindomain' ], '-' , '.' ], '' , $domain ), 30 )[ 0 ] . $recursive ;
// try anthor username if user exists or if reserved name
if ( checkIfUserExist ( $user ) || in_array ( $user , explode ( ',' , $_SERVER [ 'reservedsubdomains' ]))) {
if ( $recursive === null ) {
$recursive = 1 ;
}
$user = generateUserFromDomain ( $domain , $recursive + 1 );
}
return $user ;
2022-08-29 00:26:46 +03:00
}
function findUserFromExistingDomain ( $domain )
{
2024-02-15 21:42:51 +03:00
exec ( 'find /home/* -maxdepth 1 -type d | grep ' . $domain , $output );
if ( empty ( $output )) {
throw new Exception ( 'the domain ' . $domain . ' was not found on the server.' );
} else {
return str_replace ([ '/home/' , '/' . $domain ], '' , $output [ 0 ]);
}
2022-08-29 00:26:46 +03:00
}
function generatePassword ( $length = 32 , $add_dashes = false , $available_sets = 'luds' )
{
2024-02-15 21:42:51 +03:00
$sets = array ();
if ( strpos ( $available_sets , 'l' ) !== false ) {
$sets [] = 'abcdefghjkmnpqrstuvwxyz' ;
}
if ( strpos ( $available_sets , 'u' ) !== false ) {
$sets [] = 'ABCDEFGHJKMNPQRSTUVWXYZ' ;
}
if ( strpos ( $available_sets , 'd' ) !== false ) {
$sets [] = '23456789' ;
}
if ( strpos ( $available_sets , 's' ) !== false ) {
$sets [] = ';-().!?' ;
}
$all = '' ;
$password = '' ;
foreach ( $sets as $set ) {
$password .= $set [ array_rand ( str_split ( $set ))];
$all .= $set ;
}
$all = str_split ( $all );
for ( $i = 0 ; $i < $length - count ( $sets ); $i ++ ) {
$password .= $all [ array_rand ( $all )];
}
$password = str_shuffle ( $password );
if ( ! $add_dashes ) {
return $password ;
}
$dash_len = floor ( sqrt ( $length ));
$dash_str = '' ;
while ( strlen ( $password ) > $dash_len ) {
$dash_str .= substr ( $password , 0 , $dash_len ) . '-' ;
$password = substr ( $password , $dash_len );
}
$dash_str .= $password ;
return $dash_str ;
2022-08-29 00:26:46 +03:00
}
2022-09-06 13:38:00 +03:00
function createSQLUserAndDatabase ( $user , $type )
2022-08-29 00:26:46 +03:00
{
2024-02-15 21:42:51 +03:00
$pass = generatePassword ();
exec ( 'mysql -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -e \'CREATE DATABASE IF NOT EXISTS ' . $user . ';\'' , $output );
exec ( 'mysql -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -e "CREATE USER IF NOT EXISTS \'' . $user . '\'@\'localhost\' IDENTIFIED BY \'' . $pass . '\';"' , $output );
exec ( 'mysql -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -e "GRANT ALL PRIVILEGES ON ' . $user . '.* TO \'' . $user . '\'@\'localhost\';"' , $output );
exec ( 'mysql -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -e "FLUSH PRIVILEGES;"' , $output );
$databaseModel = ( $type === 'solo' ) ? $_SERVER [ 'solomodel' ] : $_SERVER [ 'fermemodel' ];
exec ( 'mysqldump -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -v ' . $databaseModel . ' | mysql -u ' . $user . ' -p\'' . $pass . '\' -D ' . $user , $output );
// TODO: add first user and make him admin
return [ 'database' => $user , 'user' => $user , 'password' => $pass ];
2022-08-29 00:26:46 +03:00
}
function removeMySQLUserAndDatabase ( $user )
{
2024-02-15 21:42:51 +03:00
exec ( 'mysql -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -e \'DROP DATABASE IF EXISTS ' . $user . ';\'' , $output );
exec ( 'mysql -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -e "DROP USER IF EXISTS \'' . $user . '\'@\'localhost\';"' , $output );
exec ( 'mysql -u ' . $_SERVER [ 'mysqluser' ] . ' -p\'' . $_SERVER [ 'mysqlpassword' ] . '\' -e "FLUSH PRIVILEGES;"' , $output );
return ;
2022-08-29 00:26:46 +03:00
}
function createUnixUserWithQuota ( $user , $quota )
{
2024-02-15 21:42:51 +03:00
$pass = generatePassword ();
exec ( 'useradd -m -p "' . $pass . '" ' . $user , $output );
exec ( 'setquota -u ' . $user . ' ' . $quota . ' ' . $quota . ' 0 0 -a /dev/loop0' , $output );
// TODO : handle errors
return [ 'user' => $user , 'password' => $pass , 'quota' => $quota ];
2022-08-29 00:26:46 +03:00
}
function removeUnixUser ( $user )
{
2025-01-24 11:25:07 +03:00
exec ( 'sleep 2s && deluser --remove-home ' . $user , $output );
2024-02-15 21:42:51 +03:00
// TODO : handle errors
return ;
2022-08-29 00:26:46 +03:00
}
2023-04-20 19:28:54 +03:00
function createNginxConfig ( $domain , $user , $herseUser , $hersePass , $nossl )
2022-08-29 00:26:46 +03:00
{
2024-02-15 21:42:51 +03:00
// create folder if not exists
exec ( 'sudo -u ' . $user . ' mkdir -p /home' . '/' . $user . '/' . $domain );
$nginxFile = '/etc/nginx/conf.d/' . $domain . '.conf' ;
if ( empty ( $herseUser ) && empty ( $hersePass )) {
// no herse needed
} elseif ( empty ( $herseUser ) || empty ( $hersePass )) {
throw new Exception ( 'You need an username AND a password to add a herse.' );
} else {
// add password file to domain
file_put_contents (
'/home' . '/' . $user . '/' . $domain . '/.htpasswd' ,
$herseUser . ':' . password_hash ( $hersePass , PASSWORD_BCRYPT )
);
}
$templates = new League\Plates\Engine ( dirname ( __FILE__ ) . '/templates' );
$subDomain = preg_match ( '/.' . $_SERVER [ 'maindomain' ] . '$/isU' , $domain , $matches , PREG_OFFSET_CAPTURE , 0 );
$isFullDomain = ! preg_match ( '/^([a-zA-Z0-9]([-a-zA-Z0-9]{0,61}[a-zA-Z0-9])\.)([a-zA-Z0-9]{1,2}([-a-zA-Z0-9]{0,252}[a-zA-Z0-9])?)\.([a-zA-Z]{2,63})$/isU' , $domain , $matches , PREG_OFFSET_CAPTURE , 0 );
if ( ! $nossl ) {
if ( ! $subDomain ) {
file_put_contents (
$nginxFile ,
$templates -> render (
'nginx-for-ssl-certificate' ,
[
'domain' => $domain ,
'user' => $user ,
]
)
);
exec ( 'service nginx force-reload' , $output );
if ( $isFullDomain ) {
exec ( '/root/.acme.sh/acme.sh --issue -d ' . $domain . ' -d www.' . $domain . ' -k ec-384 -w /home/' . $user . '/' . $domain . '/' , $output );
} else {
exec ( '/root/.acme.sh/acme.sh --issue -d ' . $domain . ' -k ec-384 -w /home/' . $user . '/' . $domain . '/' , $output );
}
exec ( 'mkdir -p /etc/letsencrypt/live/' . $domain , $output );
exec ( '/root/.acme.sh/acme.sh --install-cert -d ' . $domain . ' -- ecc \
2024-01-09 15:40:28 +03:00
-- cert - file / etc / letsencrypt / live / ' . $domain . ' / cert . pem \
-- key - file / etc / letsencrypt / live / ' . $domain . ' / key . pem \
-- fullchain - file / etc / letsencrypt / live / ' . $domain . ' / fullchain . pem \
-- ca - file / etc / letsencrypt / live / ' . $domain . ' / ca . pem \
2023-04-20 19:28:54 +03:00
-- reloadcmd " systemctl restart nginx.service " ' , $output );
2024-02-15 21:42:51 +03:00
}
file_put_contents (
$nginxFile ,
$templates -> render (
'nginx-maindomain' ,
[
'domain' => $domain ,
'user' => $user ,
'herseUser' => $herseUser ,
'hersePass' => $hersePass ,
'subDomain' => $subDomain ,
]
)
);
} else {
file_put_contents (
$nginxFile ,
$templates -> render (
'nginx-nossl' ,
[
'domain' => $domain ,
'user' => $user ,
'herseUser' => $herseUser ,
'hersePass' => $hersePass ,
'subDomain' => $subDomain ,
]
)
);
2022-09-06 17:10:16 +03:00
}
2024-02-15 21:42:51 +03:00
exec ( 'service nginx force-reload' , $output );
2022-09-06 13:38:00 +03:00
}
function removeNginxConfig ( $domain )
{
2024-02-15 21:42:51 +03:00
$nginxFile = '/etc/nginx/conf.d/' . $domain . '.conf' ;
unlink ( $nginxFile );
exec ( 'service nginx force-reload' , $output );
2022-08-29 00:26:46 +03:00
}
function createPhpFpmConfig ( $user )
{
2024-02-15 21:42:51 +03:00
$phpVersion = str_replace ([ 'php' , '-fpm' ], '' , $_SERVER [ 'phpservice' ]);
$phpConfFile = '/etc/php/' . $phpVersion . '/fpm/pool.d/' . $user . '.conf' ;
$templates = new League\Plates\Engine ( dirname ( __FILE__ ) . '/templates' );
file_put_contents ( $phpConfFile , $templates -> render ( 'php-fpm' , [ 'user' => $user ]));
exec ( 'service ' . $_SERVER [ 'phpservice' ] . ' reload' , $output );
2022-08-29 00:26:46 +03:00
}
function removePhpFpmConfig ( $user )
{
2024-02-15 21:42:51 +03:00
$phpVersion = str_replace ([ 'php' , '-fpm' ], '' , $_SERVER [ 'phpservice' ]);
$phpConfFile = '/etc/php/' . $phpVersion . '/fpm/pool.d/' . $user . '.conf' ;
unlink ( $phpConfFile );
exec ( 'service ' . $_SERVER [ 'phpservice' ] . ' reload' , $output );
2022-08-29 00:26:46 +03:00
}
2025-03-25 21:07:57 +03:00
function saveSql ( $wikiDir , $backupDir )
2025-03-25 19:10:10 +03:00
{
2025-03-25 20:25:01 +03:00
$user = posix_getpwuid ( fileowner ( $wikiDir . '/wakka.config.php' ))[ 'name' ];
2025-03-25 19:10:10 +03:00
$sudo = 'sudo -u ' . $user . ' ' ;
$wakkaConfig = [];
2025-03-25 21:07:57 +03:00
include $wikiDir . '/wakka.config.php' ;
2025-03-25 22:10:46 +03:00
cli ( $sudo . 'mysqldump -u\'' . $wakkaConfig [ 'mysql_user' ] . '\' -p\'' . $wakkaConfig [ 'mysql_password' ] . '\' ' . $wakkaConfig [ 'mysql_database' ] . ' ' . $wakkaConfig [ 'table_prefix' ] . 'acls ' . $wakkaConfig [ 'table_prefix' ] . 'links ' . $wakkaConfig [ 'table_prefix' ] . 'nature ' . $wakkaConfig [ 'table_prefix' ] . 'pages ' . $wakkaConfig [ 'table_prefix' ] . 'referrers ' . $wakkaConfig [ 'table_prefix' ] . 'triples ' . $wakkaConfig [ 'table_prefix' ] . 'users > ' . $backupDir . '/database.sql' );
2025-03-25 20:41:36 +03:00
return '==== Mysql database saved in ' . $backupDir . '/database.sql ===========' . " \n " ;
2025-03-25 19:10:10 +03:00
}
2022-08-29 00:26:46 +03:00
2025-03-27 13:17:06 +03:00
function upgradeWiki ( $srcDir , $destDir , $nobackup = false )
2024-01-30 21:17:10 +03:00
{
2025-03-25 19:10:10 +03:00
$filesToMove = [ 'actions' , 'cache' , 'docker' , 'docs' , 'formatters' , 'handlers' , 'includes' , 'javascripts' , 'lang' , 'private' , 'setup' , 'styles' , 'templates' , 'tests' , 'themes' , 'tools' , 'vendor' , 'composer.json' , 'composer.lock' , 'index.php' , 'INSTALL.md' , 'interwiki.conf' , 'LICENSE' , 'Makefile' , 'package.json' , 'README.md' , 'SECURITY.md' , 'wakka.basic.css' , 'wakka.css' , 'wakka.php' , 'yarn.lock' , 'yeswicli' ];
2025-03-26 13:35:03 +03:00
$defaultExtensions = array_map ( 'basename' , array_filter ( glob ( $srcDir . '/tools/*' ), 'is_dir' ));
$installedExtensions = array_map ( 'basename' , array_filter ( glob ( $destDir . '/tools/*' ), 'is_dir' ));
2025-03-25 19:10:10 +03:00
$filesToCopy = [ 'files' , 'custom' , 'wakka.config.php' ];
2024-02-15 21:42:51 +03:00
$bars = '============' . " \n " ;
$output = '== Update wiki in path: ' . $destDir . ' ' . $bars ;
$user = posix_getpwuid ( fileowner ( $destDir . '/wakka.config.php' ))[ 'name' ];
$sudo = 'sudo -u ' . $user . ' ' ;
2025-03-25 19:10:10 +03:00
$wakkaConfig = [];
2025-03-25 20:41:36 +03:00
require $destDir . '/wakka.config.php' ;
2025-03-27 13:17:06 +03:00
if ( ! $nobackup ) {
2025-03-27 13:07:09 +03:00
$tmpbackupDir = '/root/yeswiki_backups/' . str_replace ([ 'https://' , 'http://' , '/?' , '/' ], [ '' , '' , '' , '-' ], $wakkaConfig [ 'base_url' ]);
if ( is_dir ( $tmpbackupDir )) {
$output .= 'ERROR: found existing backup in "' . $tmpbackupDir . '" aborting' . " \n " ;
exit ( $output );
return ;
2024-02-15 21:42:51 +03:00
} else {
2025-03-27 13:07:09 +03:00
exec ( 'mkdir -p ' . $tmpbackupDir );
2024-02-15 21:42:51 +03:00
}
2025-03-27 13:07:09 +03:00
$output .= '==== Move files to temporary backup dir ' . $tmpbackupDir . ' ' . $bars ;
foreach ( $filesToMove as $f ) {
if ( file_exists ( $destDir . '/' . $f )) {
exec ( 'mv ' . $destDir . '/' . $f . ' ' . $tmpbackupDir . '/' . $f );
} else {
$output .= 'File not found "' . $f . '"' . " \n " ;
}
}
foreach ( $filesToCopy as $f ) {
if ( file_exists ( $destDir . '/' . $f )) {
exec ( 'cp -rf ' . $destDir . '/' . $f . ' ' . $tmpbackupDir . '/' . $f );
} else {
$output .= 'File not found "' . $f . '"' . " \n " ;
}
}
$output .= saveSql ( $destDir , $tmpbackupDir );
} else {
$output .= '==== Remove old files before upgrade ' . $bars ;
foreach ( $filesToMove as $f ) {
if ( file_exists ( $destDir . '/' . $f )) {
exec ( 'rm -rf ' . $destDir . '/' . $f );
}
2024-02-15 21:42:51 +03:00
}
}
$output .= '==== Update to latest sources ' . $bars ;
foreach ( $filesToMove as $f ) {
if ( file_exists ( $srcDir . '/' . $f )) {
exec ( $sudo . 'cp -R ' . $srcDir . '/' . $f . ' ' . $destDir . '/' . $f );
} else {
$output .= 'File not found "' . $f . '"' . " \n " ;
}
}
2025-03-26 13:35:03 +03:00
$output .= runUpgrades ( $sudo , $destDir , array_diff ( $installedExtensions , $defaultExtensions ));
$version = trim ( cli ( " cat $destDir /includes/constants.php | grep YESWIKI_RELEASE | sed -r -e \" s/.*([0-9]+ \\ .[0-9]+ \\ .[0-9]+).*/ \\ 1/ \" " ));
$output .= '==== Change YesWiki version to ' . $version . ' ' . $bars ;
2025-03-27 13:07:09 +03:00
$output .= cli ( " sed -r -i -e \" s/([' \\ \" ]yeswiki_release[' \\ \" ].*)/'yeswiki_release' => ' $version ',/ \" $destDir /wakka.config.php " );
2024-02-15 21:42:51 +03:00
$output .= '== End update wiki in path: ' . $destDir . ' ' . $bars ;
return $output ;
2024-01-30 21:17:10 +03:00
}
2025-03-25 22:00:10 +03:00
function cli ( $cmd )
{
2025-03-26 13:35:03 +03:00
$output = '' ;
ob_start ();
2025-03-25 22:00:10 +03:00
system ( $cmd , $return_value );
( $return_value == 0 ) or die ( 'ERROR : ' . $cmd . " \n " );
2025-03-26 13:35:03 +03:00
$output .= ob_get_contents ();
ob_end_clean ();
return $output ;
}
function runUpgrades ( $sudo , $destDir , $extensions = [])
{
$output = '' ;
$startDir = getcwd ();
$output .= runMigrations ( $sudo , $destDir );
foreach ( $extensions as $ext ) {
$output .= '==== Install latest version of extension ' . $ext . " \n " ;
2025-03-27 13:07:09 +03:00
cli ( 'cd ' . $destDir . ' && ' . $sudo . ' ./yeswicli upgrade ' . $ext );
2025-03-26 13:35:03 +03:00
}
$output .= cli ( 'cd ' . $startDir );
return $output ;
2025-03-25 22:00:10 +03:00
}
2025-03-26 13:35:03 +03:00
function runMigrations ( $sudo , $destDir )
2025-01-24 15:32:12 +03:00
{
2025-03-26 13:35:03 +03:00
$output = '==== Run migrations on core' . " \n " ;
2025-03-25 22:21:34 +03:00
$startDir = getcwd ();
2025-03-27 13:07:09 +03:00
cli ( 'cd ' . $destDir . ' && ' . $sudo . ' ./yeswicli migrate' );
$output .= cli ( 'cd ' . $startDir );
2025-03-26 13:35:03 +03:00
return $output ;
2025-01-24 15:32:12 +03:00
}
2023-04-20 19:28:54 +03:00
function copyYesWikiFiles ( $domain , $user , $dbUser , $herseUser = null , $hersePass = null , $nossl = null )
2022-08-29 00:26:46 +03:00
{
2024-02-15 21:42:51 +03:00
$tmpFile = '/tmp/yeswiki.zip' ;
$destDir = '/home' . '/' . $user . '/' . $domain ;
$sudo = 'sudo -u ' . $user . ' ' ;
exec ( $sudo . ' mkdir -p ' . $destDir , $output );
exec ( $sudo . ' chown ' . $user . ':' . $user . ' -R ' . $destDir , $output );
if ( file_exists ( $tmpFile )) {
unlink ( $tmpFile );
}
exec ( $sudo . 'curl --insecure -o ' . $tmpFile . ' ' . $_SERVER [ 'source_archive_url' ]);
exec ( $sudo . 'unzip ' . $tmpFile . ' "doryphore/*" -d ' . $destDir );
exec ( $sudo . 'mv ' . $destDir . '/doryphore/* ' . $destDir . '/' );
exec ( $sudo . 'rm -rf ' . $destDir . '/doryphore' );
2023-02-01 13:22:42 +03:00
unlink ( $tmpFile );
2025-01-24 13:31:05 +03:00
createWakkaConfig ( $domain , $user , $dbUser );
2025-01-24 17:33:22 +03:00
exec ( 'chown ' . $user . ':' . $user . ' ' . $destDir . '/wakka.config.php' );
2025-01-24 15:32:12 +03:00
runMigrations ( $sudo , $destDir );
2024-02-15 21:42:51 +03:00
return ;
2022-08-29 00:26:46 +03:00
}
2025-01-24 13:31:05 +03:00
function createWakkaConfig ( $domain , $user , $dbUser )
{
$destDir = '/home' . '/' . $user . '/' . $domain ;
$templates = new League\Plates\Engine ( dirname ( __FILE__ ) . '/templates' );
2025-01-24 14:06:51 +03:00
file_put_contents ( $destDir . '/wakka.config.php' , '<?php' . " \n \n " . $templates -> render ( 'wakka.config' , [
2025-01-24 13:31:05 +03:00
'domain' => $domain ,
'database' => $dbUser [ 'database' ],
'databaseUser' => $dbUser [ 'user' ],
'databasePassword' => $dbUser [ 'password' ],
'yeswikiVersion' => preg_replace ( '/.*-(.*)\.zip/m' , " $ 1 " , $_SERVER [ 'source_archive_url' ]),
2025-01-24 16:37:40 +03:00
'contactFrom' => $_SERVER [ 'contactFrom' ],
'smtpHost' => $_SERVER [ 'smtpHost' ],
'smtpPort' => $_SERVER [ 'smtpPort' ],
'smtpUser' => $_SERVER [ 'smtpUser' ],
'smtpPass' => $_SERVER [ 'smtpPass' ],
2025-01-24 13:31:05 +03:00
]));
}
2022-08-29 00:26:46 +03:00
function checkHerse ( $herseUser , $hersePass )
{
2024-02-15 21:42:51 +03:00
if ( empty ( $herseUser ) && empty ( $hersePass )) {
return false ; // no herse needed
} elseif ( empty ( $herseUser ) || empty ( $hersePass )) {
throw new Exception ( 'You need an username AND a password to add a herse.' );
}
return true ; // herse needed
2024-01-09 15:40:28 +03:00
}
function editConfigFile ()
{
2024-02-15 21:42:51 +03:00
// change fuseau horaire
// 'contact_mail_func' => 'smtp',
// 'contact_smtp_host' => 'ssl://mail.gandi.net',
// 'contact_smtp_port' => '465',
// 'contact_smtp_user' => 'noreply@yeswiki.pro',
// 'contact_reply_to' => 'noreply@yeswiki.pro',
// 'contact_smtp_pass' => 'Ceci est 1 message automatique!',
2024-01-09 15:40:28 +03:00
}
function addExtensions ()
{
2024-02-15 21:42:51 +03:00
// ferme pour l'option ferme
2022-08-29 00:26:46 +03:00
}
2023-02-03 21:36:26 +03:00
2024-01-09 15:40:28 +03:00
function addStatistics ()
{
2024-02-15 21:42:51 +03:00
// requete SQL
2024-12-05 22:45:51 +03:00
// INSERT INTO `matomo_site` (`idsite`, `name`, `main_url`, `ts_created`, `ecommerce`, `sitesearch`, `sitesearch_keyword_parameters`, `sitesearch_category_parameters`, `timezone`, `currency`, `exclude_unknown_urls`, `excluded_ips`, `excluded_parameters`, `excluded_user_agents`, `excluded_referrers`, `group`, `type`, `keep_url_fragment`, `creator_login`) VALUES (NULL, 'Partage ton outil', 'https://partagetonoutil.fr', '2023-02-01 00:00:00', '0', '1', '', '', 'Europe/Paris', 'EUR', '0', '', '', '', '', '', 'website', '0', 'superadmin');
2024-01-09 15:40:28 +03:00
2023-02-03 21:36:26 +03:00
}
2025-03-27 14:50:01 +03:00
function searchWikis ( $path , $pattern , $depth = 1 , $httpCheck = false )
2024-01-09 15:40:28 +03:00
{
2025-03-27 14:50:01 +03:00
// Use a more efficient iterator that filters files by name
$dirIterator = new RecursiveDirectoryIterator ( $path , RecursiveDirectoryIterator :: SKIP_DOTS );
$iterator = new RecursiveIteratorIterator ( $dirIterator );
$iterator -> setMaxDepth ( $depth );
$list = [];
$count = 0 ;
// Process filesystem operations first
foreach ( $iterator as $file ) {
// Direct basename comparison instead of regex
if ( $file -> isFile () && basename ( $file ) === $pattern ) {
$count ++ ;
$filePath = $file -> getPathname ();
// Extract config without using include_once which is slow
$wakkaConfig = extractWakkaConfig ( $filePath );
$list [ $count ] = [
'PATH' => dirname ( $filePath ),
2024-02-15 21:42:51 +03:00
'URL' => $wakkaConfig [ 'base_url' ] ? ? 'KO' ,
'VERSION' => $wakkaConfig [ 'yeswiki_version' ] ? ? 'KO' ,
'RELEASE' => $wakkaConfig [ 'yeswiki_release' ] ? ? 'KO' ,
];
}
2024-01-23 22:21:26 +03:00
}
2025-03-27 14:50:01 +03:00
// Process HTTP requests in smaller batches to avoid overwhelming resources
if ( ! empty ( $list ) && $httpCheck ) {
try {
2025-03-27 15:01:29 +03:00
$responses = Amp\Future\awaitAll ( array_map ( function ( $l ) use ( $httpClient ) {
return Amp\async ( fn () => $httpClient -> request ( new Request ( $l [ 'URL' ], 'HEAD' )));
}, $list ));
foreach ( $responses [ 0 ] as $key => $response ) {
$list [ $key ][ 'STATUS' ] = 'ERROR' ;
}
foreach ( $responses [ 1 ] as $key => $response ) {
$list [ $key ][ 'STATUS' ] = $response -> getStatus () . ' ' . $response -> getReason ();
2025-03-27 14:50:01 +03:00
}
} catch ( Exception $e ) {
2025-03-27 15:01:29 +03:00
// If any one of the requests fails the combo will fail
2025-03-27 14:50:01 +03:00
echo $e -> getMessage (), " \n " ;
2024-02-15 21:42:51 +03:00
}
2024-01-09 22:12:15 +03:00
}
2024-01-09 15:49:46 +03:00
2024-02-15 21:42:51 +03:00
return $list ;
2024-01-09 22:12:15 +03:00
}
2025-03-27 14:50:01 +03:00
/**
* Extract configuration from a wakka . config . php file without using include
* which is much faster for many files
*/
function extractWakkaConfig ( $filePath )
{
$config = [];
$content = file_get_contents ( $filePath );
// Extract base_url
if ( preg_match ( '/[\'"]base_url[\'"]\s*=>\s*[\'"]([^\'"]+)[\'"]/i' , $content , $matches )) {
$config [ 'base_url' ] = $matches [ 1 ];
}
// Extract yeswiki_version
if ( preg_match ( '/[\'"]yeswiki_version[\'"]\s*=>\s*[\'"]([^\'"]+)[\'"]/i' , $content , $matches )) {
$config [ 'yeswiki_version' ] = $matches [ 1 ];
}
// Extract yeswiki_release
if ( preg_match ( '/[\'"]yeswiki_release[\'"]\s*=>\s*[\'"]([^\'"]+)[\'"]/i' , $content , $matches )) {
$config [ 'yeswiki_release' ] = $matches [ 1 ];
}
return $config ;
}