2017-08-26 06:04:21 +00:00
< ? php
2017-11-19 19:15:25 +00:00
/**
* @ file src / Core / System . php
*/
2017-08-26 06:04:21 +00:00
namespace Friendica\Core ;
2017-12-17 16:35:06 +00:00
use Friendica\BaseObject ;
2018-10-19 18:11:27 +00:00
use Friendica\Network\HTTPException\InternalServerErrorException ;
2018-01-27 16:59:10 +00:00
use Friendica\Util\XML ;
2017-08-26 06:04:21 +00:00
/**
* @ file include / Core / System . php
*
* @ brief Contains the class with system relevant stuff
*/
/**
* @ brief System methods
*/
2017-12-17 16:35:06 +00:00
class System extends BaseObject
2017-11-19 19:15:25 +00:00
{
2017-08-26 06:04:21 +00:00
/**
* @ brief Retrieves the Friendica instance base URL
*
* @ param bool $ssl Whether to append http or https under SSL_POLICY_SELFSIGN
* @ return string Friendica server base URL
2019-01-06 21:06:53 +00:00
* @ throws InternalServerErrorException
2017-08-26 06:04:21 +00:00
*/
2017-11-19 19:15:25 +00:00
public static function baseUrl ( $ssl = false )
{
2018-10-09 17:58:58 +00:00
return self :: getApp () -> getBaseURL ( $ssl );
2017-08-26 06:04:21 +00:00
}
/**
* @ brief Removes the baseurl from an url . This avoids some mixed content problems .
*
2017-11-19 19:15:25 +00:00
* @ param string $orig_url The url to be cleaned
2017-08-26 06:04:21 +00:00
*
* @ return string The cleaned url
2019-01-06 21:06:53 +00:00
* @ throws \Exception
2017-08-26 06:04:21 +00:00
*/
2017-11-19 19:15:25 +00:00
public static function removedBaseUrl ( $orig_url )
{
2018-10-09 17:58:58 +00:00
return self :: getApp () -> removeBaseURL ( $orig_url );
2017-08-26 06:04:21 +00:00
}
2017-08-26 10:01:50 +00:00
/**
* @ brief Returns a string with a callstack . Can be used for logging .
2017-11-19 19:15:25 +00:00
* @ param integer $depth optional , default 4
2017-08-26 10:01:50 +00:00
* @ return string
*/
2017-11-19 19:15:25 +00:00
public static function callstack ( $depth = 4 )
{
2017-10-17 20:51:46 +00:00
$trace = debug_backtrace ( DEBUG_BACKTRACE_IGNORE_ARGS );
2017-08-26 10:01:50 +00:00
// We remove the first two items from the list since they contain data that we don't need.
array_shift ( $trace );
array_shift ( $trace );
2018-01-15 13:05:12 +00:00
$callstack = [];
$previous = [ 'class' => '' , 'function' => '' ];
2017-10-17 20:51:46 +00:00
// The ignore list contains all functions that are only wrapper functions
2018-04-10 05:55:36 +00:00
$ignore = [ 'fetchUrl' , 'call_user_func_array' ];
2017-10-17 20:51:46 +00:00
while ( $func = array_pop ( $trace )) {
2017-08-26 10:01:50 +00:00
if ( ! empty ( $func [ 'class' ])) {
2018-04-13 20:09:12 +00:00
// Don't show multiple calls from the "dba" class to show the essential parts of the callstack
2018-08-23 13:51:58 +00:00
if ((( $previous [ 'class' ] != $func [ 'class' ]) || ( $func [ 'class' ] != 'Friendica\Database\DBA' )) && ( $previous [ 'function' ] != 'q' )) {
2017-10-17 20:51:46 +00:00
$classparts = explode ( " \\ " , $func [ 'class' ]);
$callstack [] = array_pop ( $classparts ) . '::' . $func [ 'function' ];
$previous = $func ;
}
} elseif ( ! in_array ( $func [ 'function' ], $ignore )) {
2017-08-26 10:01:50 +00:00
$callstack [] = $func [ 'function' ];
2018-07-01 04:15:11 +00:00
$func [ 'class' ] = '' ;
2017-10-17 20:51:46 +00:00
$previous = $func ;
2017-08-26 10:01:50 +00:00
}
}
2018-01-15 13:05:12 +00:00
$callstack2 = [];
2017-10-17 20:51:46 +00:00
while (( count ( $callstack2 ) < $depth ) && ( count ( $callstack ) > 0 )) {
$callstack2 [] = array_pop ( $callstack );
}
return implode ( ', ' , $callstack2 );
2017-08-26 10:01:50 +00:00
}
2018-01-27 16:59:10 +00: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 21:06:53 +00:00
*
* @ param $st
* @ param string $message
* @ throws \Exception
2018-01-27 16:59:10 +00:00
*/
public static function xmlExit ( $st , $message = '' )
{
$result = [ 'status' => $st ];
if ( $message != '' ) {
$result [ 'message' ] = $message ;
}
if ( $st ) {
2018-10-29 21:20:46 +00:00
Logger :: log ( 'xml_status returning non_zero: ' . $st . " message= " . $message );
2018-01-27 16:59:10 +00:00
}
header ( " Content-type: text/xml " );
$xmldata = [ " result " => $result ];
echo XML :: fromArray ( $xmldata , $xml );
2018-12-26 05:40:12 +00:00
exit ();
2018-01-27 16:59:10 +00:00
}
/**
* @ brief Send HTTP status header and exit .
*
* @ param integer $val HTTP status result value
* @ param array $description optional message
* 'title' => header title
* 'description' => optional message
2019-01-06 21:06:53 +00:00
* @ throws InternalServerErrorException
2018-01-27 16:59:10 +00:00
*/
public static function httpExit ( $val , $description = [])
{
$err = '' ;
if ( $val >= 400 ) {
2018-12-02 20:10:53 +00:00
if ( ! empty ( $description [ 'title' ])) {
$err = $description [ 'title' ];
} else {
$title = [
'400' => L10n :: t ( 'Error 400 - Bad Request' ),
'401' => L10n :: t ( 'Error 401 - Unauthorized' ),
'403' => L10n :: t ( 'Error 403 - Forbidden' ),
'404' => L10n :: t ( 'Error 404 - Not Found' ),
'500' => L10n :: t ( 'Error 500 - Internal Server Error' ),
'503' => L10n :: t ( 'Error 503 - Service Unavailable' ),
];
$err = defaults ( $title , $val , 'Error ' . $val );
$description [ 'title' ] = $err ;
}
if ( empty ( $description [ 'description' ])) {
// Explanations are taken from https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
$explanation = [
'400' => L10n :: t ( 'The server cannot or will not process the request due to an apparent client error.' ),
'401' => L10n :: t ( 'Authentication is required and has failed or has not yet been provided.' ),
'403' => L10n :: t ( 'The request was valid, but the server is refusing action. The user might not have the necessary permissions for a resource, or may need an account.' ),
'404' => L10n :: t ( 'The requested resource could not be found but may be available in the future.' ),
'500' => L10n :: t ( 'An unexpected condition was encountered and no more specific message is suitable.' ),
'503' => L10n :: t ( 'The server is currently unavailable (because it is overloaded or down for maintenance). Please try again later.' ),
];
if ( ! empty ( $explanation [ $val ])) {
$description [ 'description' ] = $explanation [ $val ];
}
2018-01-27 16:59:10 +00:00
}
}
if ( $val >= 200 && $val < 300 ) {
$err = 'OK' ;
}
2018-10-29 21:20:46 +00:00
Logger :: log ( 'http_status_exit ' . $val );
2018-01-27 16:59:10 +00:00
header ( $_SERVER [ " SERVER_PROTOCOL " ] . ' ' . $val . ' ' . $err );
if ( isset ( $description [ " title " ])) {
2018-10-31 14:44:06 +00:00
$tpl = Renderer :: getMarkupTemplate ( 'http_status.tpl' );
2018-10-31 14:35:50 +00:00
echo Renderer :: replaceMacros ( $tpl , [ '$title' => $description [ " title " ],
2018-07-08 09:37:05 +00:00
'$description' => defaults ( $description , 'description' , '' )]);
2018-01-27 16:59:10 +00:00
}
2018-10-22 02:24:47 +00:00
exit ();
2018-01-27 16:59:10 +00:00
}
/**
2018-06-18 21:05:44 +00:00
* @ brief Encodes content to json .
2018-01-27 16:59:10 +00: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 .
*
2018-06-18 21:05:44 +00:00
* @ param array $x The input content .
* @ param string $content_type Type of the input ( Default : 'application/json' ) .
2018-01-27 16:59:10 +00:00
*/
2018-06-18 21:05:44 +00:00
public static function jsonExit ( $x , $content_type = 'application/json' ) {
header ( " Content-type: $content_type " );
2018-01-27 16:59:10 +00:00
echo json_encode ( $x );
2018-12-26 05:40:12 +00:00
exit ();
2018-01-27 16:59:10 +00:00
}
2018-09-26 20:03:46 +00:00
/**
* Generates a random string in the UUID format
*
2019-01-06 21:06:53 +00:00
* @ param bool | string $prefix A given prefix ( default is empty )
2018-09-26 20:03:46 +00:00
* @ return string a generated UUID
2019-01-06 21:06:53 +00:00
* @ throws \Exception
2018-09-26 20:03:46 +00:00
*/
2018-09-27 11:52:15 +00:00
public static function createUUID ( $prefix = '' )
2018-09-26 20:03:46 +00:00
{
$guid = System :: createGUID ( 32 , $prefix );
2018-11-22 16:25:43 +00: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 20:03:46 +00:00
}
2018-07-09 19:38:16 +00:00
/**
* Generates a GUID with the given parameters
*
2019-01-06 21:06:53 +00: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 19:38:16 +00:00
* @ return string a generated GUID
2019-01-06 21:06:53 +00:00
* @ throws \Exception
2018-07-09 19:38:16 +00:00
*/
public static function createGUID ( $size = 16 , $prefix = '' )
{
if ( is_bool ( $prefix ) && ! $prefix ) {
$prefix = '' ;
2018-07-09 20:10:35 +00:00
} elseif ( empty ( $prefix )) {
2018-10-09 17:58:58 +00:00
$prefix = hash ( 'crc32' , self :: getApp () -> getHostName ());
2018-07-09 19:38:16 +00: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 16:57:31 +00: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 ]);
}
2018-10-19 18:11:27 +00:00
/**
* Redirects to an external URL ( fully qualified URL )
* If you want to route relative to the current Friendica base , use App -> internalRedirect ()
*
* @ param string $url The new Location to redirect
* @ throws InternalServerErrorException If the URL is not fully qualified
*/
public static function externalRedirect ( $url )
{
2018-12-03 15:59:53 +00:00
if ( empty ( parse_url ( $url , PHP_URL_SCHEME ))) {
2018-10-23 10:17:41 +00:00
throw new InternalServerErrorException ( " ' $url ' is not a fully qualified URL, please use App->internalRedirect() instead " );
2018-10-19 18:11:27 +00:00
}
header ( " Location: $url " );
exit ();
}
2019-02-03 21:22:04 +00:00
/**
* @ brief Returns the system user that is executing the script
*
* 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 21:30:18 +00:00
/**
* @ brief Checks if a given directory is usable for the system
*
* @ param $directory
* @ param bool $check_writable
*
* @ return boolean the directory is usable
*/
public static function isDirectoryUsable ( $directory , $check_writable = true )
{
if ( $directory == '' ) {
Logger :: log ( 'Directory is empty. This shouldn\'t happen.' , Logger :: DEBUG );
return false ;
}
if ( ! file_exists ( $directory )) {
Logger :: log ( 'Path "' . $directory . '" does not exist for user ' . static :: getUser (), Logger :: DEBUG );
return false ;
}
if ( is_file ( $directory )) {
Logger :: log ( 'Path "' . $directory . '" is a file for user ' . static :: getUser (), Logger :: DEBUG );
return false ;
}
if ( ! is_dir ( $directory )) {
Logger :: log ( 'Path "' . $directory . '" is not a directory for user ' . static :: getUser (), Logger :: DEBUG );
return false ;
}
if ( $check_writable && ! is_writable ( $directory )) {
Logger :: log ( 'Path "' . $directory . '" is not writable for user ' . static :: getUser (), Logger :: DEBUG );
return false ;
}
return true ;
}
2017-08-26 06:04:21 +00:00
/// @todo Move the following functions from boot.php
/*
function killme ()
function local_user ()
function public_contact ()
function remote_user ()
function notice ( $s )
function info ( $s )
function is_site_admin ()
function get_server ()
function get_temppath ()
function get_cachefile ( $file , $writemode = true )
function get_itemcachepath ()
function get_spoolpath ()
*/
}