2017-08-26 08:04:21 +02:00
< ? php
2017-11-19 20:15:25 +01:00
/**
2023-01-01 15:36:24 +01:00
* @ copyright Copyright ( C ) 2010 - 2023 , the Friendica project
2020-02-09 15:45:36 +01:00
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
2017-11-19 20:15:25 +01:00
*/
2020-02-09 15:45:36 +01:00
2017-08-26 08:04:21 +02:00
namespace Friendica\Core ;
2022-12-03 21:18:19 +01:00
use Friendica\Content\Text\BBCode ;
use Friendica\Content\Text\HTML ;
2021-10-24 20:43:59 +02:00
use Friendica\Core\Config\Capability\IManageConfigValues ;
2019-12-15 22:34:11 +01:00
use Friendica\DI ;
2022-01-03 20:18:43 +01:00
use Friendica\Module\Response ;
2021-10-30 01:21:07 +02:00
use Friendica\Network\HTTPException\FoundException ;
use Friendica\Network\HTTPException\MovedPermanentlyException ;
use Friendica\Network\HTTPException\TemporaryRedirectException ;
2021-11-04 21:29:59 +01:00
use Friendica\Util\BasePath ;
2018-01-27 17:59:10 +01:00
use Friendica\Util\XML ;
2021-10-24 20:43:59 +02:00
use Psr\Log\LoggerInterface ;
2017-08-26 08:04:21 +02:00
/**
2020-01-19 07:05:23 +01:00
* Contains the class with system relevant stuff
2017-08-26 08:04:21 +02:00
*/
2019-12-15 23:28:01 +01:00
class System
2017-11-19 20:15:25 +01:00
{
2021-10-24 20:43:59 +02:00
/**
* @ var LoggerInterface
*/
private $logger ;
/**
* @ var IManageConfigValues
*/
private $config ;
/**
* @ var string
*/
private $basePath ;
2021-11-01 13:54:54 +01:00
public function __construct ( LoggerInterface $logger , IManageConfigValues $config , string $basepath )
2021-10-24 20:43:59 +02:00
{
$this -> logger = $logger ;
$this -> config = $config ;
$this -> basePath = $basepath ;
}
/**
* Checks if the maximum number of database processes is reached
*
* @ return bool Is the limit reached ?
*/
public function isMaxProcessesReached () : bool
{
// Deactivated, needs more investigating if this check really makes sense
return false ;
/*
* Commented out to suppress static analyzer issues
*
if ( $this -> mode -> isBackend ()) {
$process = 'backend' ;
$max_processes = $this -> config -> get ( 'system' , 'max_processes_backend' );
if ( intval ( $max_processes ) == 0 ) {
$max_processes = 5 ;
}
} else {
$process = 'frontend' ;
$max_processes = $this -> config -> get ( 'system' , 'max_processes_frontend' );
if ( intval ( $max_processes ) == 0 ) {
$max_processes = 20 ;
}
}
$processlist = DBA :: processlist ();
if ( $processlist [ 'list' ] != '' ) {
$this -> logger -> debug ( 'Processcheck: Processes: ' . $processlist [ 'amount' ] . ' - Processlist: ' . $processlist [ 'list' ]);
if ( $processlist [ 'amount' ] > $max_processes ) {
$this -> logger -> debug ( 'Processcheck: Maximum number of processes for ' . $process . ' tasks (' . $max_processes . ') reached.' );
return true ;
}
}
return false ;
*/
}
/**
* Checks if the minimal memory is reached
*
* @ return bool Is the memory limit reached ?
*/
public function isMinMemoryReached () : bool
{
2021-11-01 13:58:36 +01:00
// Deactivated, needs more investigating if this check really makes sense
return false ;
/*
* Commented out to suppress static analyzer issues
*
2021-10-24 20:43:59 +02:00
$min_memory = $this -> config -> get ( 'system' , 'min_memory' , 0 );
if ( $min_memory == 0 ) {
return false ;
}
if ( ! is_readable ( '/proc/meminfo' )) {
return false ;
}
$memdata = explode ( " \n " , file_get_contents ( '/proc/meminfo' ));
$meminfo = [];
foreach ( $memdata as $line ) {
$data = explode ( ':' , $line );
if ( count ( $data ) != 2 ) {
continue ;
}
[ $key , $val ] = $data ;
$meminfo [ $key ] = ( int ) trim ( str_replace ( 'kB' , '' , $val ));
$meminfo [ $key ] = ( int )( $meminfo [ $key ] / 1024 );
}
if ( ! isset ( $meminfo [ 'MemFree' ])) {
return false ;
}
$free = $meminfo [ 'MemFree' ];
$reached = ( $free < $min_memory );
if ( $reached ) {
$this -> logger -> warning ( 'Minimal memory reached.' , [ 'free' => $free , 'memtotal' => $meminfo [ 'MemTotal' ], 'limit' => $min_memory ]);
}
return $reached ;
2021-11-01 13:58:36 +01:00
*/
2021-10-24 20:43:59 +02:00
}
/**
* Checks if the maximum load is reached
*
* @ return bool Is the load reached ?
*/
public function isMaxLoadReached () : bool
{
2021-11-01 13:54:54 +01:00
$maxsysload = intval ( $this -> config -> get ( 'system' , 'maxloadavg' ));
if ( $maxsysload < 1 ) {
$maxsysload = 50 ;
2021-10-24 20:43:59 +02:00
}
$load = System :: currentLoad ();
if ( $load ) {
if ( intval ( $load ) > $maxsysload ) {
2021-11-01 13:54:54 +01:00
$this -> logger -> warning ( 'system load for process too high.' , [ 'load' => $load , 'process' => 'backend' , 'maxsysload' => $maxsysload ]);
2021-10-24 20:43:59 +02:00
return true ;
}
}
return false ;
}
/**
* Executes a child process with 'proc_open'
*
* @ param string $command The command to execute
* @ param array $args Arguments to pass to the command ( [ 'key' => value , 'key2' => value2 , ... ]
*/
public function run ( string $command , array $args )
{
if ( ! function_exists ( 'proc_open' )) {
$this -> logger -> warning ( '"proc_open" not available - quitting' );
return ;
}
$cmdline = $this -> config -> get ( 'config' , 'php_path' , 'php' ) . ' ' . escapeshellarg ( $command );
foreach ( $args as $key => $value ) {
if ( ! is_null ( $value ) && is_bool ( $value ) && ! $value ) {
continue ;
}
$cmdline .= ' --' . $key ;
if ( ! is_null ( $value ) && ! is_bool ( $value )) {
$cmdline .= ' ' . $value ;
}
}
if ( $this -> isMinMemoryReached ()) {
$this -> logger -> warning ( 'Memory limit reached - quitting' );
return ;
}
if ( strtoupper ( substr ( PHP_OS , 0 , 3 )) === 'WIN' ) {
$resource = proc_open ( 'cmd /c start /b ' . $cmdline , [], $foo , $this -> basePath );
} else {
$resource = proc_open ( $cmdline . ' &' , [], $foo , $this -> basePath );
}
2021-11-01 07:53:26 +01:00
2021-10-24 20:43:59 +02:00
if ( ! is_resource ( $resource )) {
$this -> logger -> warning ( 'We got no resource for command.' , [ 'command' => $cmdline ]);
return ;
}
2021-11-01 07:53:47 +01:00
2021-10-24 20:43:59 +02:00
proc_close ( $resource );
$this -> logger -> info ( 'Executed "proc_open"' , [ 'command' => $cmdline , 'callstack' => System :: callstack ( 10 )]);
}
2017-08-26 12:01:50 +02:00
/**
2020-01-19 07:05:23 +01:00
* Returns a string with a callstack . Can be used for logging .
2020-01-19 10:51:37 +01:00
*
2020-07-27 06:20:30 +02:00
* @ param integer $depth How many calls to include in the stacks after filtering
* @ param int $offset How many calls to shave off the top of the stack , for example if
* this is called from a centralized method that isn ' t relevant to the callstack
2017-08-26 12:01:50 +02:00
* @ return string
*/
2021-10-24 20:43:59 +02:00
public static function callstack ( int $depth = 4 , int $offset = 0 ) : string
2017-11-19 20:15:25 +01:00
{
2017-10-17 22:51:46 +02:00
$trace = debug_backtrace ( DEBUG_BACKTRACE_IGNORE_ARGS );
2017-08-26 12:01:50 +02:00
2020-07-27 06:20:30 +02:00
// We remove at least the first two items from the list since they contain data that we don't need.
$trace = array_slice ( $trace , 2 + $offset );
2017-08-26 12:01:50 +02:00
2018-01-15 14:05:12 +01:00
$callstack = [];
2020-07-10 09:01:28 +02:00
$previous = [ 'class' => '' , 'function' => '' , 'database' => false ];
2017-10-17 22:51:46 +02:00
// The ignore list contains all functions that are only wrapper functions
2020-07-27 13:50:36 +02:00
$ignore = [ 'call_user_func_array' ];
2017-10-17 22:51:46 +02:00
while ( $func = array_pop ( $trace )) {
2017-08-26 12:01:50 +02:00
if ( ! empty ( $func [ 'class' ])) {
2021-02-14 15:24:48 +01:00
if ( in_array ( $previous [ 'function' ], [ 'insert' , 'fetch' , 'toArray' , 'exists' , 'count' , 'selectFirst' , 'selectToArray' ,
'select' , 'update' , 'delete' , 'selectFirstForUser' , 'selectForUser' ])
&& ( substr ( $previous [ 'class' ], 0 , 15 ) === 'Friendica\Model' )) {
continue ;
}
2020-07-10 09:01:28 +02:00
// Don't show multiple calls from the Database classes to show the essential parts of the callstack
$func [ 'database' ] = in_array ( $func [ 'class' ], [ 'Friendica\Database\DBA' , 'Friendica\Database\Database' ]);
2022-12-18 03:13:24 +01:00
if ( ! $previous [ 'database' ] || ! $func [ 'database' ]) {
2017-10-17 22:51:46 +02:00
$classparts = explode ( " \\ " , $func [ 'class' ]);
$callstack [] = array_pop ( $classparts ) . '::' . $func [ 'function' ];
$previous = $func ;
}
} elseif ( ! in_array ( $func [ 'function' ], $ignore )) {
2020-07-10 09:01:28 +02:00
$func [ 'database' ] = ( $func [ 'function' ] == 'q' );
2017-08-26 12:01:50 +02:00
$callstack [] = $func [ 'function' ];
2018-07-01 06:15:11 +02:00
$func [ 'class' ] = '' ;
2017-10-17 22:51:46 +02:00
$previous = $func ;
2017-08-26 12:01:50 +02:00
}
}
2018-01-15 14:05:12 +01:00
$callstack2 = [];
2017-10-17 22:51:46 +02:00
while (( count ( $callstack2 ) < $depth ) && ( count ( $callstack ) > 0 )) {
$callstack2 [] = array_pop ( $callstack );
}
return implode ( ', ' , $callstack2 );
2017-08-26 12:01:50 +02:00
}
2018-01-27 17:59:10 +01:00
/**
* Generic XML return
* Outputs a basic dfrn XML status structure to STDOUT , with a < status > variable
* of $st and an optional text < message > of $message and terminates the current process .
2019-01-06 22:06:53 +01:00
*
* @ param $st
* @ param string $message
* @ throws \Exception
2018-01-27 17:59:10 +01:00
*/
public static function xmlExit ( $st , $message = '' )
{
$result = [ 'status' => $st ];
if ( $message != '' ) {
$result [ 'message' ] = $message ;
}
if ( $st ) {
2021-11-04 00:19:24 +01:00
Logger :: notice ( 'xml_status returning non_zero: ' . $st . " message= " . $message );
2018-01-27 17:59:10 +01:00
}
2022-01-03 20:18:43 +01:00
DI :: apiResponse () -> setType ( Response :: TYPE_XML );
2022-12-31 17:59:19 +01:00
DI :: apiResponse () -> addContent ( XML :: fromArray ([ 'result' => $result ]));
2022-01-03 20:18:43 +01:00
DI :: page () -> exit ( DI :: apiResponse () -> generate ());
2018-01-27 17:59:10 +01:00
2022-05-18 04:13:54 +02:00
self :: exit ();
2018-01-27 17:59:10 +01:00
}
/**
2020-01-19 07:05:23 +01:00
* Send HTTP status header and exit .
2018-01-27 17:59:10 +01:00
*
2019-05-02 03:33:33 +02:00
* @ param integer $val HTTP status result value
* @ param string $message Error message . Optional .
* @ param string $content Response body . Optional .
* @ throws \Exception
2018-01-27 17:59:10 +01:00
*/
2022-04-10 10:31:55 +02:00
public static function httpError ( $httpCode , $message = '' , $content = '' )
2018-01-27 17:59:10 +01:00
{
2022-04-10 10:31:55 +02:00
if ( $httpCode >= 400 ) {
Logger :: debug ( 'Exit with error' , [ 'code' => $httpCode , 'message' => $message , 'callstack' => System :: callstack ( 20 ), 'method' => DI :: args () -> getMethod (), 'agent' => $_SERVER [ 'HTTP_USER_AGENT' ] ? ? '' ]);
2021-10-30 01:21:07 +02:00
}
2022-04-10 10:31:55 +02:00
DI :: apiResponse () -> setStatus ( $httpCode , $message );
2022-01-03 20:18:43 +01:00
DI :: apiResponse () -> addContent ( $content );
2022-01-02 23:21:52 +01:00
DI :: page () -> exit ( DI :: apiResponse () -> generate ());
2018-01-27 17:59:10 +01:00
2022-05-18 04:13:54 +02:00
self :: exit ();
2018-01-27 17:59:10 +01:00
}
2022-04-10 10:31:55 +02:00
/**
* This function adds the content and a content - teype HTTP header to the output .
* After finishing the process is getting killed .
*
* @ param string $content
2022-09-05 07:01:23 +02:00
* @ param string $type
2022-04-10 10:31:55 +02:00
* @ param string | null $content_type
* @ return void
*/
2022-09-05 07:01:23 +02:00
public static function httpExit ( string $content , string $type = Response :: TYPE_HTML , ? string $content_type = null ) {
DI :: apiResponse () -> setType ( $type , $content_type );
2022-04-10 10:31:55 +02:00
DI :: apiResponse () -> addContent ( $content );
DI :: page () -> exit ( DI :: apiResponse () -> generate ());
2022-05-18 04:13:54 +02:00
self :: exit ();
2022-04-10 10:31:55 +02:00
}
public static function jsonError ( $httpCode , $content , $content_type = 'application/json' )
2019-02-23 21:25:41 +01:00
{
2021-10-30 01:21:07 +02:00
if ( $httpCode >= 400 ) {
2022-01-03 19:30:27 +01:00
Logger :: debug ( 'Exit with error' , [ 'code' => $httpCode , 'content_type' => $content_type , 'callstack' => System :: callstack ( 20 ), 'method' => DI :: args () -> getMethod (), 'agent' => $_SERVER [ 'HTTP_USER_AGENT' ] ? ? '' ]);
2021-10-30 01:21:07 +02:00
}
2022-01-03 20:18:43 +01:00
DI :: apiResponse () -> setStatus ( $httpCode );
2022-04-10 10:31:55 +02:00
self :: jsonExit ( $content , $content_type );
2019-02-23 21:25:41 +01:00
}
2018-01-27 17:59:10 +01:00
/**
2020-01-19 07:05:23 +01:00
* Encodes content to json .
2018-01-27 17:59:10 +01:00
*
* This function encodes an array to json format
* and adds an application / json HTTP header to the output .
* After finishing the process is getting killed .
*
2022-04-10 10:31:55 +02:00
* @ param mixed $content The input content
2022-03-05 21:34:09 +01:00
* @ param string $content_type Type of the input ( Default : 'application/json' )
* @ param integer $options JSON options
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-01-27 17:59:10 +01:00
*/
2022-04-10 10:31:55 +02:00
public static function jsonExit ( $content , $content_type = 'application/json' , int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT ) {
2022-01-03 20:18:43 +01:00
DI :: apiResponse () -> setType ( Response :: TYPE_JSON , $content_type );
2022-04-10 10:31:55 +02:00
DI :: apiResponse () -> addContent ( json_encode ( $content , $options ));
2022-01-02 23:21:52 +01:00
DI :: page () -> exit ( DI :: apiResponse () -> generate ());
2022-05-18 04:13:54 +02:00
self :: exit ();
}
/**
* Exit the program execution .
*/
public static function exit ()
{
2022-06-10 20:49:03 +02:00
DI :: page () -> logRuntime ( DI :: config (), 'exit' );
2018-12-26 06:40:12 +01:00
exit ();
2018-01-27 17:59:10 +01:00
}
2018-09-26 22:03:46 +02:00
/**
* Generates a random string in the UUID format
*
2019-01-06 22:06:53 +01:00
* @ param bool | string $prefix A given prefix ( default is empty )
2018-09-26 22:03:46 +02:00
* @ return string a generated UUID
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-09-26 22:03:46 +02:00
*/
2018-09-27 13:52:15 +02:00
public static function createUUID ( $prefix = '' )
2018-09-26 22:03:46 +02:00
{
$guid = System :: createGUID ( 32 , $prefix );
2018-11-22 17:25:43 +01:00
return substr ( $guid , 0 , 8 ) . '-' . substr ( $guid , 8 , 4 ) . '-' . substr ( $guid , 12 , 4 ) . '-' . substr ( $guid , 16 , 4 ) . '-' . substr ( $guid , 20 , 12 );
2018-09-26 22:03:46 +02:00
}
2018-07-09 21:38:16 +02:00
/**
* Generates a GUID with the given parameters
*
2019-01-06 22:06:53 +01:00
* @ param int $size The size of the GUID ( default is 16 )
* @ param bool | string $prefix A given prefix ( default is empty )
2018-07-09 21:38:16 +02:00
* @ return string a generated GUID
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-07-09 21:38:16 +02:00
*/
public static function createGUID ( $size = 16 , $prefix = '' )
{
if ( is_bool ( $prefix ) && ! $prefix ) {
$prefix = '' ;
2018-07-09 22:10:35 +02:00
} elseif ( empty ( $prefix )) {
2019-12-16 00:47:24 +01:00
$prefix = hash ( 'crc32' , DI :: baseUrl () -> getHostname ());
2018-07-09 21:38:16 +02:00
}
while ( strlen ( $prefix ) < ( $size - 13 )) {
$prefix .= mt_rand ();
}
if ( $size >= 24 ) {
$prefix = substr ( $prefix , 0 , $size - 22 );
return str_replace ( '.' , '' , uniqid ( $prefix , true ));
} else {
$prefix = substr ( $prefix , 0 , max ( $size - 13 , 0 ));
return uniqid ( $prefix );
}
}
2018-10-13 18:57:31 +02:00
/**
* Returns the current Load of the System
*
* @ return integer
*/
public static function currentLoad ()
{
if ( ! function_exists ( 'sys_getloadavg' )) {
return false ;
}
$load_arr = sys_getloadavg ();
if ( ! is_array ( $load_arr )) {
return false ;
}
return max ( $load_arr [ 0 ], $load_arr [ 1 ]);
}
2022-09-04 15:54:32 +02:00
/**
* Fetch the load and number of processes
*
2022-12-03 20:44:50 +01:00
* @ param bool $get_processes
2022-09-04 15:54:32 +02:00
* @ return array
*/
2022-12-03 20:44:50 +01:00
public static function getLoadAvg ( bool $get_processes = true ) : array
2022-09-04 15:54:32 +02:00
{
2022-12-03 20:44:50 +01:00
if ( $get_processes && @ is_readable ( '/proc/loadavg' )) {
2022-10-18 10:02:55 +02:00
$content = @ file_get_contents ( '/proc/loadavg' );
if ( empty ( $content )) {
2022-12-03 11:34:17 +01:00
$content = shell_exec ( 'uptime | sed "s/.*averages*: //" | sed "s/,//g"' );
2022-10-18 10:02:55 +02:00
}
2022-09-04 15:54:32 +02:00
}
2022-10-18 10:02:55 +02:00
2022-09-22 07:45:42 +02:00
if ( empty ( $content ) || ! preg_match ( " #([. \ d]+) \ s([. \ d]+) \ s([. \ d]+) \ s( \ d+)/( \ d+)# " , $content , $matches )) {
$load_arr = sys_getloadavg ();
if ( empty ( $load_arr )) {
return [];
}
return [
'average1' => $load_arr [ 0 ],
'average5' => $load_arr [ 1 ],
'average15' => $load_arr [ 2 ],
'runnable' => 0 ,
'scheduled' => 0
];
2022-09-04 15:54:32 +02:00
}
return [
'average1' => ( float ) $matches [ 1 ],
'average5' => ( float ) $matches [ 2 ],
'average15' => ( float ) $matches [ 3 ],
'runnable' => ( float ) $matches [ 4 ],
'scheduled' => ( float ) $matches [ 5 ]
];
}
2018-10-19 20:11:27 +02:00
/**
* Redirects to an external URL ( fully qualified URL )
* If you want to route relative to the current Friendica base , use App -> internalRedirect ()
*
2019-05-04 09:16:37 +02:00
* @ param string $url The new Location to redirect
* @ param int $code The redirection code , which is used ( Default is 302 )
2022-07-07 21:47:39 +02:00
*
* @ throws FoundException
* @ throws MovedPermanentlyException
* @ throws TemporaryRedirectException
*
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-10-19 20:11:27 +02:00
*/
2019-05-04 09:16:37 +02:00
public static function externalRedirect ( $url , $code = 302 )
2018-10-19 20:11:27 +02:00
{
2018-12-03 16:59:53 +01:00
if ( empty ( parse_url ( $url , PHP_URL_SCHEME ))) {
2021-11-01 22:21:03 +01:00
Logger :: warning ( 'No fully qualified URL provided' , [ 'url' => $url , 'callstack' => self :: callstack ( 20 )]);
DI :: baseUrl () -> redirect ( $url );
2018-10-19 20:11:27 +02:00
}
2021-10-30 01:21:07 +02:00
header ( " Location: $url " );
2019-05-04 09:16:37 +02:00
switch ( $code ) {
case 302 :
2021-10-30 01:21:07 +02:00
throw new FoundException ();
2019-05-04 09:16:37 +02:00
case 301 :
2021-10-30 01:21:07 +02:00
throw new MovedPermanentlyException ();
2019-05-04 13:42:26 +02:00
case 307 :
2021-10-30 01:21:07 +02:00
throw new TemporaryRedirectException ();
2019-05-04 09:16:37 +02:00
}
2022-05-18 04:13:54 +02:00
self :: exit ();
2018-10-19 20:11:27 +02:00
}
2019-02-03 22:22:04 +01:00
/**
2020-01-19 07:05:23 +01:00
* Returns the system user that is executing the script
2019-02-03 22:22:04 +01:00
*
* This mostly returns something like " www-data " .
*
* @ return string system username
*/
public static function getUser ()
{
if ( ! function_exists ( 'posix_getpwuid' ) || ! function_exists ( 'posix_geteuid' )) {
return '' ;
}
$processUser = posix_getpwuid ( posix_geteuid ());
return $processUser [ 'name' ];
}
2019-02-05 22:30:18 +01:00
/**
2020-01-19 07:05:23 +01:00
* Checks if a given directory is usable for the system
2019-02-05 22:30:18 +01:00
*
* @ param $directory
* @ param bool $check_writable
*
* @ return boolean the directory is usable
*/
2022-08-30 21:45:30 +02:00
private static function isDirectoryUsable ( $directory , $check_writable = true )
2019-02-05 22:30:18 +01:00
{
if ( $directory == '' ) {
2022-08-30 21:45:30 +02:00
Logger :: warning ( 'Directory is empty. This shouldn\'t happen.' );
2019-02-05 22:30:18 +01:00
return false ;
}
if ( ! file_exists ( $directory )) {
2022-08-30 21:45:30 +02:00
Logger :: warning ( 'Path does not exist' , [ 'directory' => $directory , 'user' => static :: getUser ()]);
2019-02-05 22:30:18 +01:00
return false ;
}
if ( is_file ( $directory )) {
2022-08-30 21:45:30 +02:00
Logger :: warning ( 'Path is a file' , [ 'directory' => $directory , 'user' => static :: getUser ()]);
2019-02-05 22:30:18 +01:00
return false ;
}
if ( ! is_dir ( $directory )) {
2022-08-30 21:45:30 +02:00
Logger :: warning ( 'Path is not a directory' , [ 'directory' => $directory , 'user' => static :: getUser ()]);
2019-02-05 22:30:18 +01:00
return false ;
}
if ( $check_writable && ! is_writable ( $directory )) {
2022-08-30 21:45:30 +02:00
Logger :: warning ( 'Path is not writable' , [ 'directory' => $directory , 'user' => static :: getUser ()]);
2019-02-05 22:30:18 +01:00
return false ;
}
return true ;
}
2020-02-16 10:32:56 +01:00
/**
* Exit method used by asynchronous update modules
*
* @ param string $o
*/
public static function htmlUpdateExit ( $o )
{
2022-01-03 20:18:43 +01:00
DI :: apiResponse () -> setType ( Response :: TYPE_HTML );
2020-02-16 10:32:56 +01:00
echo " <!DOCTYPE html><html><body> \r \n " ;
// We can remove this hack once Internet Explorer recognises HTML5 natively
echo " <section> " ;
// reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well
echo str_replace ( " \t " , " " , $o );
echo " </section> " ;
echo " </body></html> \r \n " ;
2022-05-18 04:13:54 +02:00
self :: exit ();
2020-02-16 10:32:56 +01:00
}
2021-11-04 21:29:59 +01:00
/**
* Fetch the temp path of the system
*
* @ return string Path for temp files
*/
public static function getTempPath ()
{
$temppath = DI :: config () -> get ( " system " , " temppath " );
2022-08-30 21:45:30 +02:00
if (( $temppath != " " ) && self :: isDirectoryUsable ( $temppath )) {
2021-11-04 21:29:59 +01:00
// We have a temp path and it is usable
return BasePath :: getRealPath ( $temppath );
}
// We don't have a working preconfigured temp path, so we take the system path.
$temppath = sys_get_temp_dir ();
// Check if it is usable
2022-08-30 21:45:30 +02:00
if (( $temppath != " " ) && self :: isDirectoryUsable ( $temppath )) {
2021-11-04 21:29:59 +01:00
// Always store the real path, not the path through symlinks
$temppath = BasePath :: getRealPath ( $temppath );
// To avoid any interferences with other systems we create our own directory
$new_temppath = $temppath . " / " . DI :: baseUrl () -> getHostname ();
if ( ! is_dir ( $new_temppath )) {
/// @TODO There is a mkdir()+chmod() upwards, maybe generalize this (+ configurable) into a function/method?
mkdir ( $new_temppath );
}
2022-08-30 21:45:30 +02:00
if ( self :: isDirectoryUsable ( $new_temppath )) {
2021-11-04 21:29:59 +01:00
// The new path is usable, we are happy
DI :: config () -> set ( " system " , " temppath " , $new_temppath );
return $new_temppath ;
} else {
// We can't create a subdirectory, strange.
// But the directory seems to work, so we use it but don't store it.
return $temppath ;
}
}
// Reaching this point means that the operating system is configured badly.
return '' ;
}
/**
* Returns the path where spool files are stored
*
* @ return string Spool path
*/
public static function getSpoolPath ()
{
$spoolpath = DI :: config () -> get ( 'system' , 'spoolpath' );
2022-08-30 21:45:30 +02:00
if (( $spoolpath != " " ) && self :: isDirectoryUsable ( $spoolpath )) {
2021-11-04 21:29:59 +01:00
// We have a spool path and it is usable
return $spoolpath ;
}
// We don't have a working preconfigured spool path, so we take the temp path.
$temppath = self :: getTempPath ();
if ( $temppath != " " ) {
// To avoid any interferences with other systems we create our own directory
$spoolpath = $temppath . " /spool " ;
if ( ! is_dir ( $spoolpath )) {
mkdir ( $spoolpath );
}
2022-08-30 21:45:30 +02:00
if ( self :: isDirectoryUsable ( $spoolpath )) {
2021-11-04 21:29:59 +01:00
// The new path is usable, we are happy
DI :: config () -> set ( " system " , " spoolpath " , $spoolpath );
return $spoolpath ;
} else {
// We can't create a subdirectory, strange.
// But the directory seems to work, so we use it but don't store it.
return $temppath ;
}
}
// Reaching this point means that the operating system is configured badly.
return " " ;
}
2022-12-03 21:18:19 +01:00
/**
* Fetch the system rules
2022-12-25 11:44:06 +01:00
* @ param bool $numeric_id If set to " true " , the rules are returned with a numeric id as key .
2022-12-03 21:18:19 +01:00
*
* @ return array
*/
2022-12-25 11:44:06 +01:00
public static function getRules ( bool $numeric_id = false ) : array
2022-12-03 21:18:19 +01:00
{
$rules = [];
$id = 0 ;
if ( DI :: config () -> get ( 'system' , 'tosdisplay' )) {
2022-12-03 22:15:08 +01:00
$rulelist = DI :: config () -> get ( 'system' , 'tosrules' ) ? : DI :: config () -> get ( 'system' , 'tostext' );
$html = BBCode :: convert ( $rulelist , false , BBCode :: EXTERNAL );
2022-12-03 21:18:19 +01:00
$msg = HTML :: toPlaintext ( $html , 0 , true );
2022-12-18 03:13:24 +01:00
foreach ( explode ( " \n " , trim ( $msg )) as $line ) {
2022-12-03 21:18:19 +01:00
$line = trim ( $line );
if ( $line ) {
2022-12-25 11:44:06 +01:00
if ( $numeric_id ) {
$rules [ ++ $id ] = $line ;
} else {
$rules [] = [ 'id' => ( string ) ++ $id , 'text' => $line ];
}
2022-12-03 21:18:19 +01:00
}
}
}
return $rules ;
}
2017-08-26 08:04:21 +02:00
}