2010-04-08 01:47:08 +01:00
< ? php
2019-07-15 01:40:31 +01:00
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
2010-04-08 01:47:08 +01:00
/**
2019-07-15 01:40:31 +01:00
* Installation lib
2010-04-08 01:47:08 +01:00
*
2019-07-15 01:40:31 +01:00
* @ package Installation
* @ author Adrian Lang < mail @ adrianlang . de >
* @ author Brenda Wallace < shiny @ cpan . org >
* @ author Brett Taylor < brett @ webfroot . co . nz >
* @ author Brion Vibber < brion @ pobox . com >
* @ author CiaranG < ciaran @ ciarang . com >
* @ author Craig Andrews < candrews @ integralblue . com >
* @ author Eric Helgeson < helfire @ Erics - MBP . local >
* @ author Evan Prodromou < evan @ status . net >
* @ author Mikael Nordfeldth < mmn @ hethane . se >
* @ author Robin Millette < millette @ controlyourself . ca >
* @ author Sarven Capadisli < csarven @ status . net >
* @ author Tom Adams < tom @ holizz . com >
* @ author Zach Copley < zach @ status . net >
* @ author Diogo Cordeiro < diogo @ fc . up . pt >
* @ copyright 2019 Free Software Foundation , Inc http :// www . fsf . org
* @ license https :// www . gnu . org / licenses / agpl . html GNU AGPL v3 or later
2010-04-08 01:47:08 +01:00
*/
abstract class Installer
{
/** Web site info */
2019-07-15 01:40:31 +01:00
public $sitename ;
public $server ;
public $path ;
public $fancy ;
public $siteProfile ;
public $ssl ;
2010-04-08 01:47:08 +01:00
/** DB info */
2019-07-15 01:40:31 +01:00
public $host ;
public $database ;
public $dbtype ;
public $username ;
public $password ;
public $db ;
2018-08-25 03:32:02 +01:00
/** Storage info */
2019-07-15 01:40:31 +01:00
public $avatarDir ;
public $fileDir ;
2010-04-08 01:47:08 +01:00
/** Administrator info */
2019-07-15 01:40:31 +01:00
public $adminNick ;
public $adminPass ;
public $adminEmail ;
2010-04-08 01:47:08 +01:00
/** Should we skip writing the configuration file? */
public $skipConfig = false ;
2019-07-15 01:40:31 +01:00
public static $dbModules = [
'mysql' => [
2019-07-25 00:43:25 +01:00
'name' => 'MariaDB 10.3+' ,
2010-04-19 19:57:12 +01:00
'check_module' => 'mysqli' ,
2020-09-14 20:46:29 +01:00
'scheme' => 'mysqli' , // DSN prefix for MDB2
2019-07-15 01:40:31 +01:00
],
2019-09-11 12:14:40 +01:00
'pgsql' => [
'name' => 'PostgreSQL 11+' ,
2010-04-08 01:47:08 +01:00
'check_module' => 'pgsql' ,
2020-09-14 20:46:29 +01:00
'scheme' => 'pgsql' , // DSN prefix for MDB2
2019-09-11 12:14:40 +01:00
]
2019-07-15 01:40:31 +01:00
];
2010-04-08 01:47:08 +01:00
/**
* Attempt to include a PHP file and report if it worked , while
* suppressing the annoying warning messages on failure .
2019-07-15 01:40:31 +01:00
* @ param string $filename
* @ return bool
2010-04-08 01:47:08 +01:00
*/
2019-07-15 01:40:31 +01:00
private function haveIncludeFile ( string $filename ) : bool
{
2010-04-08 01:47:08 +01:00
$old = error_reporting ( error_reporting () & ~ E_WARNING );
$ok = include_once ( $filename );
error_reporting ( $old );
return $ok ;
}
2011-08-27 05:10:24 +01:00
2010-04-08 01:47:08 +01:00
/**
* Check if all is ready for installation
*
2019-07-15 01:40:31 +01:00
* @ return bool
2010-04-08 01:47:08 +01:00
*/
2019-07-15 01:40:31 +01:00
public function checkPrereqs () : bool
2010-04-08 01:47:08 +01:00
{
$pass = true ;
2019-07-15 01:40:31 +01:00
$config = INSTALLDIR . '/config.php' ;
2017-11-26 21:14:30 +00:00
if ( ! $this -> skipConfig && file_exists ( $config )) {
2010-06-03 18:13:28 +01:00
if ( ! is_writable ( $config ) || filesize ( $config ) > 0 ) {
2010-10-12 21:00:03 +01:00
if ( filesize ( $config ) == 0 ) {
$this -> warning ( 'Config file "config.php" already exists and is empty, but is not writable.' );
} else {
$this -> warning ( 'Config file "config.php" already exists.' );
}
2010-06-03 18:13:28 +01:00
$pass = false ;
}
2010-04-08 01:47:08 +01:00
}
2019-07-25 01:29:20 +01:00
if ( version_compare ( PHP_VERSION , '7.3.0' , '<' )) {
$this -> warning ( 'Require PHP version 7.3.0 or greater.' );
2010-04-08 01:47:08 +01:00
$pass = false ;
}
2019-07-15 01:40:31 +01:00
$reqs = [ 'bcmath' , 'curl' , 'dom' , 'gd' , 'intl' , 'json' , 'mbstring' , 'openssl' , 'simplexml' , 'xml' , 'xmlwriter' ];
2010-04-08 01:47:08 +01:00
foreach ( $reqs as $req ) {
2019-07-15 01:40:31 +01:00
// Checks if a php extension is both installed and loaded
if ( ! extension_loaded ( $req )) {
2010-04-08 01:47:08 +01:00
$this -> warning ( sprintf ( 'Cannot load required extension: <code>%s</code>' , $req ));
$pass = false ;
}
}
// Make sure we have at least one database module available
2019-07-15 01:40:31 +01:00
$missingExtensions = [];
2010-04-08 01:47:08 +01:00
foreach ( self :: $dbModules as $type => $info ) {
2019-07-15 01:40:31 +01:00
if ( ! extension_loaded ( $info [ 'check_module' ])) {
2010-04-08 01:47:08 +01:00
$missingExtensions [] = $info [ 'check_module' ];
}
}
if ( count ( $missingExtensions ) == count ( self :: $dbModules )) {
$req = implode ( ', ' , $missingExtensions );
$this -> warning ( sprintf ( 'Cannot find a database extension. You need at least one of %s.' , $req ));
$pass = false ;
}
2010-05-11 20:16:13 +01:00
// @fixme this check seems to be insufficient with Windows ACLs
2017-11-26 21:14:30 +00:00
if ( ! $this -> skipConfig && ! is_writable ( INSTALLDIR )) {
2019-07-15 01:40:31 +01:00
$this -> warning (
sprintf ( 'Cannot write config file to: <code>%s</code></p>' , INSTALLDIR ),
sprintf ( 'On your server, try this command: <code>chmod a+w %s</code>' , INSTALLDIR )
);
2010-04-08 01:47:08 +01:00
$pass = false ;
}
// Check the subdirs used for file uploads
2017-11-26 21:14:30 +00:00
// TODO get another flag for this --skipFileSubdirCreation
if ( ! $this -> skipConfig ) {
2018-08-25 03:32:02 +01:00
define ( 'GNUSOCIAL' , true );
define ( 'STATUSNET' , true );
2019-08-23 13:36:02 +01:00
require_once INSTALLDIR . '/lib/util/language.php' ;
2019-07-15 01:40:31 +01:00
$_server = $this -> server ;
$_path = $this -> path ; // We won't be using those so it's safe to do this small hack
2019-08-23 13:36:02 +01:00
require_once INSTALLDIR . '/lib/util/util.php' ;
require_once INSTALLDIR . '/lib/util/default.php' ;
2019-07-15 01:40:31 +01:00
$fileSubdirs = [
empty ( $this -> avatarDir ) ? $default [ 'avatar' ][ 'dir' ] : $this -> avatarDir ,
empty ( $this -> fileDir ) ? $default [ 'attachments' ][ 'dir' ] : $this -> fileDir
];
2018-08-25 03:32:02 +01:00
unset ( $default );
foreach ( $fileSubdirs as $fileFullPath ) {
if ( ! file_exists ( $fileFullPath )) {
2019-07-14 21:02:38 +01:00
$this -> warning (
sprintf ( 'GNU social was unable to create a directory on this path: %s' , $fileFullPath ),
'Either create that directory with the right permissions so that GNU social can use it or ' .
'set the necessary permissions and it will be created.'
);
2018-08-25 03:32:02 +01:00
$pass = $pass && mkdir ( $fileFullPath );
} elseif ( ! is_dir ( $fileFullPath )) {
2019-07-15 01:40:31 +01:00
$this -> warning (
sprintf ( 'GNU social expected a directory but found something else on this path: %s' , $fileFullPath ),
'Either make sure it goes to a directory or remove it and a directory will be created.'
);
2018-08-25 03:32:02 +01:00
$pass = false ;
} elseif ( ! is_writable ( $fileFullPath )) {
2019-07-15 01:40:31 +01:00
$this -> warning (
sprintf ( 'Cannot write to directory: <code>%s</code>' , $fileFullPath ),
sprintf ( 'On your server, try this command: <code>chmod a+w %s</code>' , $fileFullPath )
);
2018-08-25 03:32:02 +01:00
$pass = false ;
}
2010-04-08 01:47:08 +01:00
}
}
return $pass ;
}
/**
2019-07-15 01:40:31 +01:00
* Basic validation on the database parameters
2010-04-08 01:47:08 +01:00
* Side effects : error output if not valid
2011-08-27 05:10:24 +01:00
*
2019-07-15 01:40:31 +01:00
* @ return bool success
2010-04-08 01:47:08 +01:00
*/
2019-07-15 01:40:31 +01:00
public function validateDb () : bool
2010-04-08 01:47:08 +01:00
{
$fail = false ;
if ( empty ( $this -> host )) {
$this -> updateStatus ( " No hostname specified. " , true );
$fail = true ;
}
if ( empty ( $this -> database )) {
$this -> updateStatus ( " No database specified. " , true );
$fail = true ;
}
if ( empty ( $this -> username )) {
$this -> updateStatus ( " No username specified. " , true );
$fail = true ;
}
if ( empty ( $this -> sitename )) {
$this -> updateStatus ( " No sitename specified. " , true );
$fail = true ;
}
return ! $fail ;
}
/**
2019-07-15 01:40:31 +01:00
* Basic validation on the administrator user parameters
2010-04-08 01:47:08 +01:00
* Side effects : error output if not valid
2011-08-27 05:10:24 +01:00
*
2019-07-15 01:40:31 +01:00
* @ return bool success
2010-04-08 01:47:08 +01:00
*/
2019-07-15 01:40:31 +01:00
public function validateAdmin () : bool
2010-04-08 01:47:08 +01:00
{
$fail = false ;
if ( empty ( $this -> adminNick )) {
2014-03-01 13:14:39 +00:00
$this -> updateStatus ( " No initial user nickname specified. " , true );
2010-04-08 01:47:08 +01:00
$fail = true ;
}
if ( $this -> adminNick && ! preg_match ( '/^[0-9a-z]{1,64}$/' , $this -> adminNick )) {
$this -> updateStatus ( 'The user nickname "' . htmlspecialchars ( $this -> adminNick ) .
2019-07-15 01:40:31 +01:00
'" is invalid; should be plain letters and numbers no longer than 64 characters.' , true );
2010-04-08 01:47:08 +01:00
$fail = true ;
}
2019-07-15 01:40:31 +01:00
2013-10-16 13:58:22 +01:00
// @fixme hardcoded list; should use Nickname::isValid()
2010-04-08 01:47:08 +01:00
// if/when it's safe to have loaded the infrastructure here
2019-07-15 01:40:31 +01:00
$blacklist = [ 'main' , 'panel' , 'twitter' , 'settings' , 'rsd.xml' , 'favorited' , 'featured' , 'favoritedrss' , 'featuredrss' , 'rss' , 'getfile' , 'api' , 'groups' , 'group' , 'peopletag' , 'tag' , 'user' , 'message' , 'conversation' , 'notice' , 'attachment' , 'search' , 'index.php' , 'doc' , 'opensearch' , 'robots.txt' , 'xd_receiver.html' , 'facebook' , 'activity' ];
2010-04-08 01:47:08 +01:00
if ( in_array ( $this -> adminNick , $blacklist )) {
$this -> updateStatus ( 'The user nickname "' . htmlspecialchars ( $this -> adminNick ) .
2019-07-15 01:40:31 +01:00
'" is reserved.' , true );
2010-04-08 01:47:08 +01:00
$fail = true ;
}
if ( empty ( $this -> adminPass )) {
2014-03-01 13:14:39 +00:00
$this -> updateStatus ( " No initial user password specified. " , true );
2010-04-08 01:47:08 +01:00
$fail = true ;
}
return ! $fail ;
}
2011-08-27 05:10:24 +01:00
/**
* Make sure a site profile was selected
*
2019-07-15 01:40:31 +01:00
* @ return bool success
2011-08-27 05:10:24 +01:00
*/
2019-07-15 01:40:31 +01:00
public function validateSiteProfile () : bool
2011-08-27 05:10:24 +01:00
{
2019-07-15 01:40:31 +01:00
if ( empty ( $this -> siteProfile )) {
2011-08-27 05:10:24 +01:00
$this -> updateStatus ( " No site profile selected. " , true );
2013-10-28 15:22:09 +00:00
return false ;
2011-08-27 05:10:24 +01:00
}
2013-10-28 15:22:09 +00:00
return true ;
2011-08-27 05:10:24 +01:00
}
2010-04-08 01:47:08 +01:00
/**
* Set up the database with the appropriate function for the selected type ...
* Saves database info into $this -> db .
2011-08-27 05:10:24 +01:00
*
2010-10-15 20:20:10 +01:00
* @ fixme escape things in the connection string in case we have a funny pass etc
2010-04-08 01:47:08 +01:00
* @ return mixed array of database connection params on success , false on failure
2019-07-15 01:40:31 +01:00
* @ throws Exception
2010-04-08 01:47:08 +01:00
*/
2019-07-15 01:40:31 +01:00
public function setupDatabase ()
2010-04-08 01:47:08 +01:00
{
2020-09-14 20:46:29 +01:00
if ( ! empty ( $this -> db )) {
throw new Exception ( 'Bad order of operations: DB already set up.' );
2010-04-08 01:47:08 +01:00
}
2020-09-14 20:46:29 +01:00
$this -> updateStatus ( 'Starting installation...' );
2010-04-08 01:47:08 +01:00
2020-09-14 20:46:29 +01:00
$auth = '' ;
if ( ! empty ( $this -> password )) {
$auth .= " : { $this -> password } " ;
2010-04-08 01:47:08 +01:00
}
2010-10-15 20:20:10 +01:00
$scheme = self :: $dbModules [ $this -> dbtype ][ 'scheme' ];
2010-10-15 21:47:38 +01:00
$dsn = " { $scheme } :// { $this -> username } { $auth } @ { $this -> host } / { $this -> database } " ;
2010-04-08 01:47:08 +01:00
2020-09-14 20:46:29 +01:00
$this -> updateStatus ( 'Checking database...' );
2010-10-15 20:20:10 +01:00
$conn = $this -> connectDatabase ( $dsn );
2010-04-08 01:47:08 +01:00
2020-09-14 20:46:29 +01:00
$charset = ( $this -> dbtype !== 'mysql' ) ? 'UTF8' : 'utf8mb4' ;
$server_charset = $this -> getDatabaseCharset ( $conn , $this -> dbtype );
// Ensure the database server character set is UTF-8.
$conn -> exec ( " SET NAMES ' { $charset } ' " );
if ( $server_charset !== $charset ) {
$this -> updateStatus (
'GNU social requires the "' . $charset . '" character set. '
. 'Yours is ' . htmlentities ( $server_charset )
);
return false ;
2016-05-24 15:49:50 +01:00
}
2020-09-14 20:46:29 +01:00
// Ensure the timezone is UTC.
if ( $this -> dbtype !== 'mysql' ) {
$conn -> exec ( " SET TIME ZONE INTERVAL '+00:00' HOUR TO MINUTE " );
} else {
$conn -> exec ( " SET time_zone = '+0:00' " );
2010-10-15 20:20:10 +01:00
}
2010-04-08 01:47:08 +01:00
2020-09-14 20:46:29 +01:00
$res = $this -> updateStatus ( 'Creating database tables...' );
2010-10-15 20:20:10 +01:00
if ( ! $this -> createCoreTables ( $conn )) {
2020-09-14 20:46:29 +01:00
$this -> updateStatus ( 'Error creating tables.' , true );
2010-04-08 01:47:08 +01:00
return false ;
}
2010-10-15 20:20:10 +01:00
2020-09-12 13:40:39 +01:00
foreach ([
'sms_carrier' => 'SMS carrier' ,
'notice_source' => 'notice source' ,
'foreign_services' => 'foreign service' ,
] as $scr => $name ) {
2010-04-08 01:47:08 +01:00
$this -> updateStatus ( sprintf ( " Adding %s data to database... " , $name ));
2019-07-15 01:40:31 +01:00
$res = $this -> runDbScript ( $scr . '.sql' , $conn );
2010-04-08 01:47:08 +01:00
if ( $res === false ) {
2012-04-03 02:41:37 +01:00
$this -> updateStatus ( sprintf ( " Can't run %s script. " , $name ), true );
2010-04-08 01:47:08 +01:00
return false ;
}
}
2019-07-15 01:40:31 +01:00
$db = [ 'type' => $this -> dbtype , 'database' => $dsn ];
2010-04-08 01:47:08 +01:00
return $db ;
}
/**
2010-10-15 20:20:10 +01:00
* Open a connection to the database .
*
2019-07-15 01:40:31 +01:00
* @ param string $dsn
2020-09-14 20:46:29 +01:00
* @ return MDB2_Driver_Common
* @ throws Exception
*/
protected function connectDatabase ( string $dsn )
{
$conn = MDB2 :: connect ( $dsn );
if ( MDB2 :: isError ( $conn )) {
throw new Exception (
'Cannot connect to database: ' . $conn -> getMessage ()
);
}
return $conn ;
}
/**
* Get the database server character set .
*
* @ param MDB2_Driver_Common $conn
* @ param string $dbtype
* @ return string
* @ throws Exception
2010-04-08 01:47:08 +01:00
*/
2020-09-14 20:46:29 +01:00
protected function getDatabaseCharset ( $conn , string $dbtype ) : string
2010-04-08 01:47:08 +01:00
{
2020-09-14 20:46:29 +01:00
$database = $conn -> getDatabase ();
switch ( $dbtype ) {
case 'pgsql' :
$res = $conn -> query ( 'SHOW server_encoding' );
if ( MDB2 :: isError ( $res )) {
throw new Exception ( $res -> getMessage ());
}
$ret = $res -> fetchOne ();
break ;
case 'mysql' :
$res = $conn -> query (
" SHOW SESSION VARIABLES LIKE 'character_set_server' "
);
if ( MDB2 :: isError ( $res )) {
throw new Exception ( $res -> getMessage ());
}
[, $ret ] = $res -> fetchRow ( MDB2_FETCHMODE_ORDERED );
break ;
default :
throw new Exception ( 'Unknown DB type selected.' );
}
if ( MDB2 :: isError ( $ret )) {
throw new Exception ( $ret -> getMessage ());
}
return $ret ;
2010-10-15 20:20:10 +01:00
}
2010-04-08 01:47:08 +01:00
2010-10-15 20:20:10 +01:00
/**
* Create core tables on the given database connection .
*
2020-09-14 20:46:29 +01:00
* @ param MDB2_Driver_Common $conn
2019-07-15 01:40:31 +01:00
* @ return bool
2010-10-15 20:20:10 +01:00
*/
2020-09-14 20:46:29 +01:00
public function createCoreTables ( $conn ) : bool
2010-10-15 20:20:10 +01:00
{
2019-09-11 12:14:40 +01:00
$schema = Schema :: get ( $conn , $this -> dbtype );
2010-10-15 20:20:10 +01:00
$tableDefs = $this -> getCoreSchema ();
foreach ( $tableDefs as $name => $def ) {
2010-10-15 21:47:38 +01:00
if ( defined ( 'DEBUG_INSTALLER' )) {
echo " $name " ;
}
2010-10-15 20:20:10 +01:00
$schema -> ensureTable ( $name , $def );
2010-04-08 01:47:08 +01:00
}
2010-10-30 00:26:45 +01:00
return true ;
2010-10-15 20:20:10 +01:00
}
2010-04-08 01:47:08 +01:00
2010-10-15 20:20:10 +01:00
/**
* Fetch the core table schema definitions .
*
* @ return array of table names => table def arrays
*/
2019-07-15 01:40:31 +01:00
public function getCoreSchema () : array
2010-10-15 20:20:10 +01:00
{
2019-07-15 01:40:31 +01:00
$schema = [];
2010-10-15 20:20:10 +01:00
include INSTALLDIR . '/db/core.php' ;
return $schema ;
2010-04-08 01:47:08 +01:00
}
2010-10-15 00:25:43 +01:00
/**
* Return a parseable PHP literal for the given value .
* This will include quotes for strings , etc .
*
* @ param mixed $val
* @ return string
*/
2019-07-15 01:40:31 +01:00
public function phpVal ( $val ) : string
2010-10-15 00:25:43 +01:00
{
return var_export ( $val , true );
}
/**
* Return an array of parseable PHP literal for the given values .
* These will include quotes for strings , etc .
*
2019-07-15 01:40:31 +01:00
* @ param mixed $map
2010-10-15 00:25:43 +01:00
* @ return array
*/
2019-07-15 01:40:31 +01:00
public function phpVals ( $map ) : array
2010-10-15 00:25:43 +01:00
{
2019-07-15 01:40:31 +01:00
return array_map ([ $this , 'phpVal' ], $map );
2010-10-15 00:25:43 +01:00
}
2010-04-08 01:47:08 +01:00
/**
* Write a stock configuration file .
*
2019-07-15 01:40:31 +01:00
* @ return bool success
2011-08-27 05:10:24 +01:00
*
2010-04-08 01:47:08 +01:00
* @ fixme escape variables in output in case we have funny chars , apostrophes etc
*/
2019-07-15 01:40:31 +01:00
public function writeConf () : bool
2010-04-08 01:47:08 +01:00
{
2019-07-15 01:40:31 +01:00
$vals = $this -> phpVals ([
2010-10-15 00:25:43 +01:00
'sitename' => $this -> sitename ,
'server' => $this -> server ,
'path' => $this -> path ,
2019-07-15 01:40:31 +01:00
'ssl' => in_array ( $this -> ssl , [ 'never' , 'always' ])
? $this -> ssl
: 'never' ,
2010-10-15 00:25:43 +01:00
'db_database' => $this -> db [ 'database' ],
2011-08-28 00:02:00 +01:00
'db_type' => $this -> db [ 'type' ]
2019-07-15 01:40:31 +01:00
]);
2010-10-15 00:25:43 +01:00
2010-04-08 01:47:08 +01:00
// assemble configuration file in a string
2019-07-15 01:40:31 +01:00
$cfg = " <?php \n " .
" if (!defined('GNUSOCIAL')) { exit(1); } \n \n " .
2010-04-08 01:47:08 +01:00
2019-07-15 01:40:31 +01:00
// site name
" \$ config['site']['name'] = { $vals [ 'sitename' ] } ; \n \n " .
2010-04-08 01:47:08 +01:00
2019-07-15 01:40:31 +01:00
// site location
" \$ config['site']['server'] = { $vals [ 'server' ] } ; \n " .
" \$ config['site']['path'] = { $vals [ 'path' ] } ; \n \n " .
" \$ config['site']['ssl'] = { $vals [ 'ssl' ] } ; \n \n " .
2020-03-15 21:37:37 +00:00
( $this -> ssl === 'proxy' ? " \$ config['site']['sslproxy'] = true; \n \n " : '' ) .
2010-04-08 01:47:08 +01:00
2019-07-15 01:40:31 +01:00
// checks if fancy URLs are enabled
( $this -> fancy ? " \$ config['site']['fancy'] = true; \n \n " : '' ) .
2010-04-08 01:47:08 +01:00
2019-07-15 01:40:31 +01:00
// database
" \$ config['db']['database'] = { $vals [ 'db_database' ] } ; \n \n " .
" \$ config['db']['type'] = { $vals [ 'db_type' ] } ; \n \n " .
2015-05-03 22:07:31 +01:00
2019-07-15 01:40:31 +01:00
" // Uncomment below for better performance. Just remember you must run \n " .
" // php scripts/checkschema.php whenever your enabled plugins change! \n " .
" // \$ config['db']['schemacheck'] = 'script'; \n \n " ;
2011-08-28 00:02:00 +01:00
// Normalize line endings for Windows servers
$cfg = str_replace ( " \n " , PHP_EOL , $cfg );
2011-08-27 05:10:24 +01:00
2011-08-28 00:02:00 +01:00
// write configuration file out to install directory
2019-09-11 06:15:16 +01:00
$res = file_put_contents ( INSTALLDIR . DIRECTORY_SEPARATOR . 'config.php' , $cfg );
2011-08-28 00:02:00 +01:00
return $res ;
}
/**
* Write the site profile . We do this after creating the initial user
* in case the site profile is set to single user . This gets around the
* 'chicken-and-egg' problem of the system requiring a valid user for
* single user mode , before the intial user is actually created . Yeah ,
* we should probably do this in smarter way .
*
* @ return int res number of bytes written
*/
2019-07-15 01:40:31 +01:00
public function writeSiteProfile () : int
2011-08-28 00:02:00 +01:00
{
2019-07-15 01:40:31 +01:00
$vals = $this -> phpVals ([
2011-08-28 00:02:00 +01:00
'site_profile' => $this -> siteProfile ,
'nickname' => $this -> adminNick
2019-07-15 01:40:31 +01:00
]);
2011-08-28 00:02:00 +01:00
$cfg =
2019-07-15 01:40:31 +01:00
// site profile
" \$ config['site']['profile'] = { $vals [ 'site_profile' ] } ; \n " ;
2011-08-27 22:25:12 +01:00
if ( $this -> siteProfile == " singleuser " ) {
$cfg .= " \$ config['singleuser']['nickname'] = { $vals [ 'nickname' ] } ; \n \n " ;
} else {
$cfg .= " \n " ;
}
2010-05-11 20:16:13 +01:00
// Normalize line endings for Windows servers
$cfg = str_replace ( " \n " , PHP_EOL , $cfg );
2010-04-08 01:47:08 +01:00
// write configuration file out to install directory
2019-07-15 01:40:31 +01:00
$res = file_put_contents ( INSTALLDIR . '/config.php' , $cfg , FILE_APPEND );
2010-04-08 01:47:08 +01:00
return $res ;
}
/**
* Install schema into the database
*
2019-07-15 01:40:31 +01:00
* @ param string $filename location of database schema file
2020-09-14 20:46:29 +01:00
* @ param MDB2_Driver_Common $conn connection to database
2010-04-08 01:47:08 +01:00
*
2019-07-15 01:40:31 +01:00
* @ return bool - indicating success or failure
2010-04-08 01:47:08 +01:00
*/
2020-09-14 20:46:29 +01:00
public function runDbScript ( string $filename , $conn ) : bool
2010-04-08 01:47:08 +01:00
{
$sql = trim ( file_get_contents ( INSTALLDIR . '/db/' . $filename ));
$stmts = explode ( ';' , $sql );
foreach ( $stmts as $stmt ) {
$stmt = trim ( $stmt );
if ( ! mb_strlen ( $stmt )) {
continue ;
}
2010-10-30 00:26:45 +01:00
try {
2019-07-15 01:40:31 +01:00
$res = $conn -> query ( $stmt );
2010-10-30 00:26:45 +01:00
} catch ( Exception $e ) {
$error = $e -> getMessage ();
2010-04-08 01:47:08 +01:00
$this -> updateStatus ( " ERROR ( $error ) for SQL ' $stmt ' " );
2010-10-30 00:26:45 +01:00
return false ;
2010-04-08 01:47:08 +01:00
}
}
return true ;
}
/**
* Create the initial admin user account .
2014-03-01 13:14:39 +00:00
* Side effect : may load portions of GNU social framework .
2010-04-08 01:47:08 +01:00
* Side effect : outputs program info
*/
2019-07-15 01:40:31 +01:00
public function registerInitialUser () : bool
2010-04-08 01:47:08 +01:00
{
2017-12-17 16:52:24 +00:00
// initalize hostname from install arguments, so it can be used to find
// the /etc config file from the commandline installer
$server = $this -> server ;
2019-08-23 13:36:02 +01:00
require_once INSTALLDIR . '/lib/util/common.php' ;
2010-04-08 01:47:08 +01:00
2019-07-15 01:40:31 +01:00
$data = [ 'nickname' => $this -> adminNick ,
'password' => $this -> adminPass ,
'fullname' => $this -> adminNick ];
2010-04-08 01:47:08 +01:00
if ( $this -> adminEmail ) {
$data [ 'email' ] = $this -> adminEmail ;
}
2015-03-01 11:36:19 +00:00
try {
2016-01-16 16:20:26 +00:00
$user = User :: register ( $data , true ); // true to skip email sending verification
2015-03-01 11:36:19 +00:00
} catch ( Exception $e ) {
2010-04-08 01:47:08 +01:00
return false ;
}
// give initial user carte blanche
$user -> grantRole ( 'owner' );
$user -> grantRole ( 'moderator' );
$user -> grantRole ( 'administrator' );
2011-08-27 05:10:24 +01:00
2010-04-08 01:47:08 +01:00
return true ;
}
/**
* The beef of the installer !
* Create database , config file , and admin user .
2011-08-27 05:10:24 +01:00
*
2010-04-08 01:47:08 +01:00
* Prerequisites : validation of input data .
2011-08-27 05:10:24 +01:00
*
2019-07-15 01:40:31 +01:00
* @ return bool success
2010-04-08 01:47:08 +01:00
*/
2019-07-15 01:40:31 +01:00
public function doInstall () : bool
2010-04-08 01:47:08 +01:00
{
2013-10-28 15:22:09 +00:00
global $config ;
2010-10-15 21:47:38 +01:00
$this -> updateStatus ( " Initializing... " );
ini_set ( 'display_errors' , 1 );
2014-07-10 12:16:51 +01:00
error_reporting ( E_ALL & ~ E_STRICT & ~ E_NOTICE );
2013-09-17 23:35:49 +01:00
if ( ! defined ( 'GNUSOCIAL' )) {
define ( 'GNUSOCIAL' , true );
}
2011-04-28 20:38:07 +01:00
if ( ! defined ( 'STATUSNET' )) {
2013-09-17 23:35:49 +01:00
define ( 'STATUSNET' , true );
2011-04-28 20:38:07 +01:00
}
2013-10-28 15:22:09 +00:00
2019-08-23 13:36:02 +01:00
require_once INSTALLDIR . '/lib/util/framework.php' ;
2015-02-27 11:44:15 +00:00
GNUsocial :: initDefaults ( $this -> server , $this -> path );
2010-10-15 21:47:38 +01:00
2013-10-28 15:22:09 +00:00
if ( $this -> siteProfile == " singleuser " ) {
// Until we use ['site']['profile']==='singleuser' everywhere
$config [ 'singleuser' ][ 'enabled' ] = true ;
}
2010-10-15 21:47:38 +01:00
try {
$this -> db = $this -> setupDatabase ();
if ( ! $this -> db ) {
// database connection failed, do not move on to create config file.
return false ;
}
} catch ( Exception $e ) {
// Lower-level DB error!
$this -> updateStatus ( " Database error: " . $e -> getMessage (), true );
2010-04-08 01:47:08 +01:00
return false ;
}
2017-11-26 21:14:30 +00:00
if ( ! $this -> skipConfig ) {
2019-07-15 01:40:31 +01:00
// Make sure we can write to the file twice
$oldUmask = umask ( 000 );
2011-08-28 00:02:00 +01:00
2010-04-08 01:47:08 +01:00
$this -> updateStatus ( " Writing config file... " );
$res = $this -> writeConf ();
if ( ! $res ) {
$this -> updateStatus ( " Can't write config file. " , true );
return false ;
}
}
if ( ! empty ( $this -> adminNick )) {
// Okay, cross fingers and try to register an initial user
if ( $this -> registerInitialUser ()) {
$this -> updateStatus (
" An initial user with the administrator role has been created. "
);
} else {
$this -> updateStatus (
2014-03-01 13:14:39 +00:00
" Could not create initial user account. " ,
2010-04-08 01:47:08 +01:00
true
);
return false ;
}
}
2011-08-28 00:02:00 +01:00
if ( ! $this -> skipConfig ) {
$this -> updateStatus ( " Setting site profile... " );
$res = $this -> writeSiteProfile ();
if ( ! $res ) {
$this -> updateStatus ( " Can't write to config file. " , true );
return false ;
}
2019-07-15 01:40:31 +01:00
// Restore original umask
umask ( $oldUmask );
// Set permissions back to something decent
chmod ( INSTALLDIR . '/config.php' , 0644 );
2017-11-26 21:14:30 +00:00
}
2019-07-15 01:40:31 +01:00
2013-10-18 17:17:37 +01:00
$scheme = $this -> ssl === 'always' ? 'https' : 'http' ;
$link = " { $scheme } :// { $this -> server } / { $this -> path } " ;
2010-04-08 01:47:08 +01:00
2014-03-01 13:14:39 +00:00
$this -> updateStatus ( " GNU social has been installed at $link " );
2010-04-08 01:47:08 +01:00
$this -> updateStatus (
2019-07-15 01:40:31 +01:00
'<strong>DONE!</strong> You can visit your <a href="' . htmlspecialchars ( $link ) . '">new GNU social site</a> (log in as "' . htmlspecialchars ( $this -> adminNick ) . '"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the <a href="https://gnu.io/social/resources/">mailing list or IRC</a>. <a href="' . htmlspecialchars ( $link ) . '/doc/faq">FAQ is found here</a>.'
2010-04-08 01:47:08 +01:00
);
return true ;
}
/**
* Output a pre - install - time warning message
* @ param string $message HTML ok , but should be plaintext - able
* @ param string $submessage HTML ok , but should be plaintext - able
*/
2019-07-15 01:40:31 +01:00
abstract public function warning ( string $message , string $submessage = '' );
2010-04-08 01:47:08 +01:00
/**
* Output an install - time progress message
2019-07-15 01:40:31 +01:00
* @ param string $status HTML ok , but should be plaintext - able
* @ param bool $error true if this should be marked as an error condition
2010-04-08 01:47:08 +01:00
*/
2019-07-15 01:40:31 +01:00
abstract public function updateStatus ( string $status , bool $error = false );
2010-04-08 01:47:08 +01:00
}