2018-04-23 22:56:59 +02:00
< ? php
/**
* @ file src / Core / Install . php
*/
namespace Friendica\Core ;
use Friendica\BaseObject ;
2018-04-28 12:36:40 +02:00
use Friendica\App ;
2018-04-23 22:56:59 +02:00
use Friendica\Database\DBStructure ;
use Friendica\Object\Image ;
use Friendica\Util\Network ;
use Exception ;
use DOMDocument ;
/**
2018-04-29 18:11:24 +02:00
* Contains methods for installation purpose of Friendica
2018-04-23 22:56:59 +02:00
*/
class Install extends BaseObject
{
2018-04-29 18:11:24 +02:00
/**
* Sets the install - mode for further methods
*/
2018-04-28 15:15:54 +02:00
public static function setInstallMode ()
{
2018-04-28 12:36:40 +02:00
self :: getApp () -> mode = App :: MODE_INSTALL ;
2018-04-23 22:56:59 +02:00
}
2018-04-29 18:11:24 +02:00
/**
* Checks the current installation environment . There are optional and mandatory checks .
*
* @ param string $phpath Optional path to the PHP binary ( Default is 'php' )
*
* @ return array First element is a list of all checks and their results ,
* the second element is a list of passed checks
*/
2018-04-23 22:56:59 +02:00
public static function check ( $phpath = 'php' )
{
$checks = [];
self :: checkFunctions ( $checks );
2018-04-29 18:11:24 +02:00
self :: checkImagick ( $checks );
2018-04-23 22:56:59 +02:00
self :: checkHtConfig ( $checks );
self :: checkSmarty3 ( $checks );
self :: checkKeys ( $checks );
self :: checkPHP ( $phpath , $checks );
self :: checkHtAccess ( $checks );
$checkspassed = array_reduce ( $checks ,
function ( $v , $c ) {
2018-06-26 03:00:58 +02:00
if ( ! empty ( $c [ 'require' ])) {
2018-04-23 22:56:59 +02:00
$v = $v && $c [ 'status' ];
}
return $v ;
},
true );
return array ( $checks , $checkspassed );
}
2018-04-29 18:11:24 +02:00
/**
* Executes the installation of Friendica in the given environment .
2018-06-25 02:26:00 +02:00
* - Creates `config/local.ini.php`
2018-04-29 18:11:24 +02:00
* - Installs Database Structure
*
* @ param string $urlpath Path based on the URL of Friendica ( e . g . '/friendica' )
* @ param string $dbhost Hostname / IP of the Friendica Database
* @ param string $dbuser Username of the Database connection credentials
* @ param string $dbpass Password of the Database connection credentials
* @ param string $dbdata Name of the Database
* @ param string $phpath Path to the PHP - Binary ( e . g . 'php' or '/usr/bin/php' )
* @ param string $timezone Timezone of the Friendica Installaton ( e . g . 'Europe/Berlin' )
* @ param string $language 2 - letter ISO 639 - 1 code ( eg . 'en' )
* @ param string $adminmail Mail - Adress of the administrator
* @ param int $rino Rino - enabled ( 1 = true , 0 = false )
*/
2018-06-26 03:00:58 +02:00
public static function install ( $urlpath , $dbhost , $dbuser , $dbpass , $dbdata , $phpath , $timezone , $language , $adminmail )
2018-04-23 22:56:59 +02:00
{
2018-06-25 02:26:00 +02:00
$tpl = get_markup_template ( 'local.ini.tpl' );
2018-04-23 22:56:59 +02:00
$txt = replace_macros ( $tpl ,[
'$dbhost' => $dbhost ,
'$dbuser' => $dbuser ,
'$dbpass' => $dbpass ,
'$dbdata' => $dbdata ,
'$timezone' => $timezone ,
'$language' => $language ,
'$urlpath' => $urlpath ,
'$phpath' => $phpath ,
'$adminmail' => $adminmail ,
]);
2018-06-25 02:26:00 +02:00
$result = file_put_contents ( 'config/local.ini.php' , $txt );
2018-04-23 22:56:59 +02:00
if ( ! $result ) {
self :: getApp () -> data [ 'txt' ] = $txt ;
}
2018-04-29 18:11:24 +02:00
$errors = self :: installDatabaseStructure ();
2018-04-23 22:56:59 +02:00
if ( $errors ) {
self :: getApp () -> data [ 'db_failed' ] = $errors ;
} else {
self :: getApp () -> data [ 'db_installed' ] = true ;
}
}
/**
2018-04-29 18:11:24 +02:00
* Adds new checks to the array $checks
*
* @ param array $checks The list of all checks ( by - ref parameter ! )
* @ param string $title The title of the current check
* @ param bool $status 1 = check passed , 0 = check not passed
* @ param bool $required 1 = check is mandatory , 0 = check is optional
* @ param string $help A help - string for the current check
* @ param string $error_msg Optional . A error message , if the current check failed
2018-04-23 22:56:59 +02:00
*/
2018-04-29 18:11:24 +02:00
private static function addCheck ( & $checks , $title , $status , $required , $help , $error_msg = " " )
2018-04-23 22:56:59 +02:00
{
$checks [] = [
'title' => $title ,
'status' => $status ,
'required' => $required ,
2018-04-28 15:15:54 +02:00
'help' => $help ,
'error_msg' => $error_msg ,
2018-04-23 22:56:59 +02:00
];
}
2018-04-29 18:11:24 +02:00
/**
* PHP Check
*
* Checks the PHP environment .
*
* - Checks if a PHP binary is available
* - Checks if it is the CLI version
* - Checks if " register_argc_argv " is enabled
*
* @ param string $phpath Optional . The Path to the PHP - Binary
* @ param array $checks The list of all checks ( by - ref parameter ! )
*/
2018-04-23 22:56:59 +02:00
public static function checkPHP ( & $phpath , & $checks )
{
$passed = $passed2 = $passed3 = false ;
if ( strlen ( $phpath )) {
$passed = file_exists ( $phpath );
} else {
$phpath = trim ( shell_exec ( 'which php' ));
$passed = strlen ( $phpath );
}
$help = " " ;
if ( ! $passed ) {
2018-04-28 15:15:54 +02:00
$help .= L10n :: t ( 'Could not find a command line version of PHP in the web server PATH.' ) . EOL ;
2018-04-23 22:56:59 +02:00
$help .= L10n :: t ( " If you don't have a command line version of PHP installed on your server, you will not be able to run the background processing. See <a href='https://github.com/friendica/friendica/blob/master/doc/Install.md#set-up-the-worker'>'Setup the worker'</a> " ) . EOL ;
$help .= EOL . EOL ;
$tpl = get_markup_template ( 'field_input.tpl' );
$help .= replace_macros ( $tpl , [
'$field' => [ 'phpath' , L10n :: t ( 'PHP executable path' ), $phpath , L10n :: t ( 'Enter full path to php executable. You can leave this blank to continue the installation.' )],
]);
$phpath = " " ;
}
self :: addCheck ( $checks , L10n :: t ( 'Command line PHP' ) . ( $passed ? " (<tt> $phpath </tt>) " : " " ), $passed , false , $help );
if ( $passed ) {
$cmd = " $phpath -v " ;
$result = trim ( shell_exec ( $cmd ));
2018-04-28 15:15:54 +02:00
$passed2 = ( strpos ( $result , " (cli) " ) !== false );
2018-04-23 22:56:59 +02:00
list ( $result ) = explode ( " \n " , $result );
$help = " " ;
if ( ! $passed2 ) {
2018-04-28 15:15:54 +02:00
$help .= L10n :: t ( " PHP executable is not the php cli binary \x28 could be cgi-fgci version \x29 " ) . EOL ;
$help .= L10n :: t ( 'Found PHP version: ' ) . " <tt> $result </tt> " ;
2018-04-23 22:56:59 +02:00
}
self :: addCheck ( $checks , L10n :: t ( 'PHP cli binary' ), $passed2 , true , $help );
}
if ( $passed2 ) {
$str = autoname ( 8 );
$cmd = " $phpath testargs.php $str " ;
$result = trim ( shell_exec ( $cmd ));
$passed3 = $result == $str ;
$help = " " ;
if ( ! $passed3 ) {
2018-04-28 15:15:54 +02:00
$help .= L10n :: t ( 'The command line version of PHP on your system does not have "register_argc_argv" enabled.' ) . EOL ;
2018-04-23 22:56:59 +02:00
$help .= L10n :: t ( 'This is required for message delivery to work.' );
}
self :: addCheck ( $checks , L10n :: t ( 'PHP register_argc_argv' ), $passed3 , true , $help );
}
}
2018-04-29 18:11:24 +02:00
/**
* OpenSSL Check
*
* Checks the OpenSSL Environment
*
* - Checks , if the command " openssl_pkey_new " is available
*
* @ param array $checks The list of all checks ( by - ref parameter ! )
*/
2018-04-23 22:56:59 +02:00
public static function checkKeys ( & $checks )
{
$help = '' ;
$res = false ;
if ( function_exists ( 'openssl_pkey_new' )) {
$res = openssl_pkey_new ([
2018-04-28 15:15:54 +02:00
'digest_alg' => 'sha1' ,
2018-04-23 22:56:59 +02:00
'private_key_bits' => 4096 ,
2018-04-28 15:15:54 +02:00
'encrypt_key' => false
2018-04-23 22:56:59 +02:00
]);
}
// Get private key
2018-04-28 15:15:54 +02:00
if ( ! $res ) {
$help .= L10n :: t ( 'Error: the "openssl_pkey_new" function on this system is not able to generate encryption keys' ) . EOL ;
2018-04-23 22:56:59 +02:00
$help .= L10n :: t ( 'If running under Windows, please see "http://www.php.net/manual/en/openssl.installation.php".' );
}
self :: addCheck ( $checks , L10n :: t ( 'Generate encryption keys' ), $res , true , $help );
}
2018-04-29 18:11:24 +02:00
/**
* PHP functions Check
*
* Checks the following PHP functions
* - libCurl
* - GD Graphics
* - OpenSSL
* - PDO or MySQLi
* - mb_string
* - XML
* - iconv
* - POSIX
*
* @ param array $checks The list of all checks ( by - ref parameter ! )
*/
2018-04-23 22:56:59 +02:00
public static function checkFunctions ( & $checks )
{
$ck_funcs = [];
self :: addCheck ( $ck_funcs , L10n :: t ( 'libCurl PHP module' ), true , true , " " );
self :: addCheck ( $ck_funcs , L10n :: t ( 'GD graphics PHP module' ), true , true , " " );
self :: addCheck ( $ck_funcs , L10n :: t ( 'OpenSSL PHP module' ), true , true , " " );
self :: addCheck ( $ck_funcs , L10n :: t ( 'PDO or MySQLi PHP module' ), true , true , " " );
self :: addCheck ( $ck_funcs , L10n :: t ( 'mb_string PHP module' ), true , true , " " );
self :: addCheck ( $ck_funcs , L10n :: t ( 'XML PHP module' ), true , true , " " );
self :: addCheck ( $ck_funcs , L10n :: t ( 'iconv PHP module' ), true , true , " " );
self :: addCheck ( $ck_funcs , L10n :: t ( 'POSIX PHP module' ), true , true , " " );
if ( function_exists ( 'apache_get_modules' )) {
if ( ! in_array ( 'mod_rewrite' , apache_get_modules ())) {
self :: addCheck ( $ck_funcs , L10n :: t ( 'Apache mod_rewrite module' ), false , true , L10n :: t ( 'Error: Apache webserver mod-rewrite module is required but not installed.' ));
} else {
self :: addCheck ( $ck_funcs , L10n :: t ( 'Apache mod_rewrite module' ), true , true , " " );
}
}
2018-04-28 15:15:54 +02:00
if ( ! function_exists ( 'curl_init' )) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 0 ][ 'status' ] = false ;
$ck_funcs [ 0 ][ 'help' ] = L10n :: t ( 'Error: libCURL PHP module required but not installed.' );
}
2018-04-28 15:15:54 +02:00
if ( ! function_exists ( 'imagecreatefromjpeg' )) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 1 ][ 'status' ] = false ;
$ck_funcs [ 1 ][ 'help' ] = L10n :: t ( 'Error: GD graphics PHP module with JPEG support required but not installed.' );
}
2018-04-28 15:15:54 +02:00
if ( ! function_exists ( 'openssl_public_encrypt' )) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 2 ][ 'status' ] = false ;
$ck_funcs [ 2 ][ 'help' ] = L10n :: t ( 'Error: openssl PHP module required but not installed.' );
}
2018-04-28 15:15:54 +02:00
if ( ! function_exists ( 'mysqli_connect' ) && ! class_exists ( 'pdo' )) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 3 ][ 'status' ] = false ;
$ck_funcs [ 3 ][ 'help' ] = L10n :: t ( 'Error: PDO or MySQLi PHP module required but not installed.' );
}
2018-05-04 19:48:01 +02:00
if ( ! function_exists ( 'mysqli_connect' ) && class_exists ( 'pdo' ) && ! in_array ( 'mysql' , \PDO :: getAvailableDrivers ())) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 3 ][ 'status' ] = false ;
$ck_funcs [ 3 ][ 'help' ] = L10n :: t ( 'Error: The MySQL driver for PDO is not installed.' );
}
2018-04-28 15:15:54 +02:00
if ( ! function_exists ( 'mb_strlen' )) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 4 ][ 'status' ] = false ;
$ck_funcs [ 4 ][ 'help' ] = L10n :: t ( 'Error: mb_string PHP module required but not installed.' );
}
2018-04-28 15:15:54 +02:00
if ( ! function_exists ( 'iconv_strlen' )) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 6 ][ 'status' ] = false ;
$ck_funcs [ 6 ][ 'help' ] = L10n :: t ( 'Error: iconv PHP module required but not installed.' );
}
2018-04-28 15:15:54 +02:00
if ( ! function_exists ( 'posix_kill' )) {
2018-04-23 22:56:59 +02:00
$ck_funcs [ 7 ][ 'status' ] = false ;
$ck_funcs [ 7 ][ 'help' ] = L10n :: t ( 'Error: POSIX PHP module required but not installed.' );
}
$checks = array_merge ( $checks , $ck_funcs );
// check for XML DOM Documents being able to be generated
try {
$xml = new DOMDocument ();
} catch ( Exception $e ) {
$ck_funcs [ 5 ][ 'status' ] = false ;
$ck_funcs [ 5 ][ 'help' ] = L10n :: t ( 'Error, XML PHP module required but not installed.' );
}
}
2018-04-29 18:11:24 +02:00
/**
2018-06-25 02:26:00 +02:00
* " config/local.ini.php " - Check
2018-04-29 18:11:24 +02:00
*
2018-06-25 02:26:00 +02:00
* Checks if it ' s possible to create the " config/local.ini.php "
2018-04-29 18:11:24 +02:00
*
* @ param array $checks The list of all checks ( by - ref parameter ! )
*/
2018-04-23 22:56:59 +02:00
public static function checkHtConfig ( & $checks )
{
$status = true ;
$help = " " ;
2018-06-25 02:26:00 +02:00
if (( file_exists ( 'config/local.ini.php' ) && ! is_writable ( 'config/local.ini.php' )) ||
( ! file_exists ( 'config/local.ini.php' ) && ! is_writable ( '.' ))) {
2018-04-23 22:56:59 +02:00
$status = false ;
2018-06-25 02:26:00 +02:00
$help = L10n :: t ( 'The web installer needs to be able to create a file called "local.ini.php" in the "config" folder of your web server and it is unable to do so.' ) . EOL ;
2018-04-28 15:15:54 +02:00
$help .= L10n :: t ( 'This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can.' ) . EOL ;
2018-06-25 02:26:00 +02:00
$help .= L10n :: t ( 'At the end of this procedure, we will give you a text to save in a file named local.ini.php in your Friendica "config" folder.' ) . EOL ;
2018-04-28 15:15:54 +02:00
$help .= L10n :: t ( 'You can alternatively skip this procedure and perform a manual installation. Please see the file "INSTALL.txt" for instructions.' ) . EOL ;
2018-04-23 22:56:59 +02:00
}
2018-06-25 02:26:00 +02:00
self :: addCheck ( $checks , L10n :: t ( 'config/local.ini.php is writable' ), $status , false , $help );
2018-04-23 22:56:59 +02:00
}
2018-04-29 18:11:24 +02:00
/**
* Smarty3 Template Check
*
* Checks , if the directory of Smarty3 is writable
*
* @ param array $checks The list of all checks ( by - ref parameter ! )
*/
2018-04-23 22:56:59 +02:00
public static function checkSmarty3 ( & $checks )
{
$status = true ;
$help = " " ;
if ( ! is_writable ( 'view/smarty3' )) {
$status = false ;
2018-04-28 15:15:54 +02:00
$help = L10n :: t ( 'Friendica uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering.' ) . EOL ;
$help .= L10n :: t ( 'In order to store these compiled templates, the web server needs to have write access to the directory view/smarty3/ under the Friendica top level folder.' ) . EOL ;
$help .= L10n :: t ( " Please ensure that the user that your web server runs as \x28 e.g. www-data \x29 has write access to this folder. " ) . EOL ;
$help .= L10n :: t ( " Note: as a security measure, you should give the web server write access to view/smarty3/ only--not the template files \x28 .tpl \x29 that it contains. " ) . EOL ;
2018-04-23 22:56:59 +02:00
}
self :: addCheck ( $checks , L10n :: t ( 'view/smarty3 is writable' ), $status , true , $help );
}
2018-04-29 18:11:24 +02:00
/**
* " .htaccess " - Check
*
* Checks , if " url_rewrite " is enabled in the " .htaccess " file
*
* @ param array $checks The list of all checks ( by - ref parameter ! )
*/
2018-04-23 22:56:59 +02:00
public static function checkHtAccess ( & $checks )
{
$status = true ;
$help = " " ;
2018-04-28 15:15:54 +02:00
$error_msg = " " ;
2018-04-23 22:56:59 +02:00
if ( function_exists ( 'curl_init' )) {
2018-04-28 15:15:54 +02:00
$test = Network :: fetchUrlFull ( System :: baseUrl () . " /install/testrewrite " );
2018-04-23 22:56:59 +02:00
2018-04-28 15:15:54 +02:00
$url = normalise_link ( System :: baseUrl () . " /install/testrewrite " );
if ( $test [ 'body' ] != " ok " ) {
$test = Network :: fetchUrlFull ( $url );
2018-04-23 22:56:59 +02:00
}
2018-04-28 15:15:54 +02:00
if ( $test [ 'body' ] != " ok " ) {
2018-04-23 22:56:59 +02:00
$status = false ;
$help = L10n :: t ( 'Url rewrite in .htaccess is not working. Check your server configuration.' );
2018-04-28 15:15:54 +02:00
$error_msg = [];
$error_msg [ 'head' ] = L10n :: t ( 'Error message from Curl when fetching' );
$error_msg [ 'url' ] = $test [ 'redirect_url' ];
2018-06-26 03:00:58 +02:00
$error_msg [ 'msg' ] = defaults ( $test , 'error' , '' );
2018-04-23 22:56:59 +02:00
}
2018-04-29 18:11:24 +02:00
self :: addCheck ( $checks , L10n :: t ( 'Url rewrite is working' ), $status , true , $help , $error_msg );
2018-04-23 22:56:59 +02:00
} else {
// cannot check modrewrite if libcurl is not installed
/// @TODO Maybe issue warning here?
}
}
2018-04-29 18:11:24 +02:00
/**
* Imagick Check
*
* Checks , if the imagick module is available
*
* @ param array $checks The list of all checks ( by - ref parameter ! )
*/
public static function checkImagick ( & $checks )
2018-04-23 22:56:59 +02:00
{
$imagick = false ;
$gif = false ;
if ( class_exists ( 'Imagick' )) {
$imagick = true ;
$supported = Image :: supportedTypes ();
if ( array_key_exists ( 'image/gif' , $supported )) {
$gif = true ;
}
}
if ( $imagick == false ) {
self :: addCheck ( $checks , L10n :: t ( 'ImageMagick PHP extension is not installed' ), $imagick , false , " " );
} else {
self :: addCheck ( $checks , L10n :: t ( 'ImageMagick PHP extension is installed' ), $imagick , false , " " );
if ( $imagick ) {
self :: addCheck ( $checks , L10n :: t ( 'ImageMagick supports GIF' ), $gif , false , " " );
}
}
}
2018-04-29 18:11:24 +02:00
/**
* Installs the Database structure
*
* @ return string A possible error
*/
public static function installDatabaseStructure ()
2018-04-23 22:56:59 +02:00
{
$errors = DBStructure :: update ( false , true , true );
return $errors ;
}
2018-04-27 21:07:04 +02:00
}