2014-12-04 00:04:47 +01:00
< ? php
2014-12-09 23:48:16 +01:00
2014-12-04 00:04:47 +01:00
/**
* User : sourcefabric
* Date : 02 / 12 / 14
* Class DatabaseSetup
* Wrapper class for validating and installing the Airtime database during the installation process
*/
class DatabaseSetup extends Setup {
2017-11-13 21:03:15 +01:00
2014-12-09 23:48:16 +01:00
// airtime.conf section header
2017-11-13 21:03:15 +01:00
protected static $_section = " [database] " ;
2014-12-09 23:48:16 +01:00
// Constant form field names for passing errors back to the front-end
2014-12-04 00:04:47 +01:00
const DB_USER = " dbUser " ,
DB_PASS = " dbPass " ,
DB_NAME = " dbName " ,
DB_HOST = " dbHost " ;
2014-12-09 23:48:16 +01:00
// Array of key->value pairs for airtime.conf
2015-06-24 01:02:55 +02:00
protected static $_properties ;
2014-12-09 23:48:16 +01:00
2015-06-24 01:02:55 +02:00
/**
* @ var PDO
*/
2014-12-11 18:54:50 +01:00
static $dbh = null ;
2014-12-04 00:04:47 +01:00
2014-12-11 18:54:50 +01:00
public function __construct ( $settings ) {
2015-06-24 01:02:55 +02:00
static :: $_properties = array (
" host " => $settings [ self :: DB_HOST ],
" dbname " => $settings [ self :: DB_NAME ],
" dbuser " => $settings [ self :: DB_USER ],
" dbpass " => $settings [ self :: DB_PASS ],
2014-12-09 23:48:16 +01:00
);
2014-12-04 00:04:47 +01:00
}
2014-12-11 18:54:50 +01:00
private function setNewDatabaseConnection ( $dbName ) {
2015-06-24 01:02:55 +02:00
self :: $dbh = new PDO ( " pgsql:host= " . self :: $_properties [ " host " ] . " ;dbname= " . $dbName . " ;port=5432 "
. " ;user= " . self :: $_properties [ " dbuser " ] . " ;password= " . self :: $_properties [ " dbpass " ]);
2014-12-11 18:54:50 +01:00
$err = self :: $dbh -> errorInfo ();
if ( $err [ 1 ] != null ) {
2014-12-11 21:54:55 +01:00
throw new PDOException ();
2014-12-11 18:54:50 +01:00
}
}
2014-12-04 00:04:47 +01:00
/**
* Runs various database checks against the given settings . If a database with the given name already exists ,
* we attempt to install the Airtime schema . If not , we first check if the user can create databases , then try
* to create the database . If we encounter errors , the offending fields are returned in an array to the browser .
* @ return array associative array containing a display message and fields with errors
2014-12-11 18:54:50 +01:00
* @ throws AirtimeDatabaseException
2014-12-04 00:04:47 +01:00
*/
2014-12-11 18:54:50 +01:00
public function runSetup () {
2017-11-13 21:03:15 +01:00
$this -> writeToTemp ();
2014-12-11 21:54:55 +01:00
try {
$this -> setNewDatabaseConnection ( " postgres " );
if ( $this -> checkDatabaseExists ()) {
$this -> installDatabaseTables ();
} else {
$this -> checkUserCanCreateDb ();
$this -> createDatabase ();
$this -> installDatabaseTables ();
}
} catch ( PDOException $e ) {
2017-11-13 21:03:15 +01:00
throw new AirtimeDatabaseException ( " Couldn't establish a connection to the database! " .
" Please check your credentials and try again. "
2015-02-11 00:06:46 +01:00
. " PDO Exception: " . $e -> getMessage (),
2015-06-24 01:02:55 +02:00
array ( self :: DB_NAME , self :: DB_USER , self :: DB_PASS ));
2014-12-04 00:04:47 +01:00
}
2014-12-11 18:54:50 +01:00
self :: $dbh = null ;
2014-12-04 00:04:47 +01:00
return array (
2014-12-11 18:54:50 +01:00
" message " => " Airtime database was created successfully! " ,
" errors " => array (),
2014-12-04 00:04:47 +01:00
);
}
2014-12-11 18:54:50 +01:00
private function installDatabaseTables () {
$this -> checkDatabaseEncoding ();
2015-06-24 01:02:55 +02:00
$this -> setNewDatabaseConnection ( self :: $_properties [ " dbname " ]);
2014-12-11 18:54:50 +01:00
$this -> checkSchemaExists ();
$this -> createDatabaseTables ();
2020-01-02 03:03:30 +01:00
$this -> updateIcecastPassword ();
2014-12-04 00:04:47 +01:00
}
/**
* Check if the database settings and credentials given are valid
* @ return boolean true if the database given exists and the user is valid and can access it
*/
2014-12-11 18:54:50 +01:00
private function checkDatabaseExists () {
$statement = self :: $dbh -> prepare ( " SELECT datname FROM pg_database WHERE datname = :dbname " );
2015-06-24 01:02:55 +02:00
$statement -> execute ( array ( " :dbname " => self :: $_properties [ " dbname " ]));
2014-12-11 18:54:50 +01:00
$result = $statement -> fetch ();
return isset ( $result [ 0 ]);
2014-12-04 00:04:47 +01:00
}
2014-12-09 23:48:16 +01:00
/**
* Check if the database schema has already been set up
2014-12-11 18:54:50 +01:00
* @ throws AirtimeDatabaseException
2014-12-09 23:48:16 +01:00
*/
2014-12-11 18:54:50 +01:00
private function checkSchemaExists () {
$statement = self :: $dbh -> prepare ( " SELECT EXISTS (SELECT relname FROM pg_class WHERE relname='cc_files') " );
$statement -> execute ();
$result = $statement -> fetch ();
if ( isset ( $result [ 0 ]) && $result [ 0 ] == " t " ) {
throw new AirtimeDatabaseException ( " Airtime is already installed in this database! " , array ());
}
2014-12-09 23:48:16 +01:00
}
2014-12-04 00:04:47 +01:00
/**
* Check if the given user has access on the given host to create a new database
2014-12-11 18:54:50 +01:00
* @ throws AirtimeDatabaseException
2014-12-04 00:04:47 +01:00
*/
2014-12-11 18:54:50 +01:00
private function checkUserCanCreateDb () {
$statement = self :: $dbh -> prepare ( " SELECT 1 FROM pg_roles WHERE rolname=:dbuser AND rolcreatedb='t' " );
2015-06-24 01:02:55 +02:00
$statement -> execute ( array ( " :dbuser " => self :: $_properties [ " dbuser " ]));
2014-12-11 18:54:50 +01:00
$result = $statement -> fetch ();
if ( ! isset ( $result [ 0 ])) {
2015-06-24 01:02:55 +02:00
throw new AirtimeDatabaseException ( " No database " . self :: $_properties [ " dbname " ] . " exists; user ' "
. self :: $_properties [ " dbuser " ] . " ' does not have permission to "
. " create databases on " . self :: $_properties [ " host " ],
array ( self :: DB_NAME , self :: DB_USER , self :: DB_PASS ));
2014-12-11 18:54:50 +01:00
}
2014-12-04 00:04:47 +01:00
}
/**
* Creates the Airtime database using the given credentials
2014-12-11 18:54:50 +01:00
* @ throws AirtimeDatabaseException
2014-12-04 00:04:47 +01:00
*/
2014-12-11 18:54:50 +01:00
private function createDatabase () {
2015-06-24 01:02:55 +02:00
$statement = self :: $dbh -> prepare ( " CREATE DATABASE " . pg_escape_string ( self :: $_properties [ " dbname " ])
2018-12-22 23:42:39 +01:00
. " WITH ENCODING 'UNICODE' TEMPLATE template0 "
2015-06-24 01:02:55 +02:00
. " OWNER " . pg_escape_string ( self :: $_properties [ " dbuser " ]));
2014-12-11 18:54:50 +01:00
if ( ! $statement -> execute ()) {
throw new AirtimeDatabaseException ( " There was an error creating the database! " ,
array ( self :: DB_NAME ,));
}
2014-12-04 00:04:47 +01:00
}
/**
* Creates the Airtime database schema using the given credentials
2014-12-11 18:54:50 +01:00
* @ throws AirtimeDatabaseException
2014-12-04 00:04:47 +01:00
*/
2014-12-11 18:54:50 +01:00
private function createDatabaseTables () {
2014-12-04 00:04:47 +01:00
$sqlDir = dirname ( dirname ( __DIR__ )) . " /build/sql/ " ;
$files = array ( " schema.sql " , " sequences.sql " , " views.sql " , " triggers.sql " , " defaultdata.sql " );
2014-12-09 23:48:16 +01:00
foreach ( $files as $f ) {
2014-12-04 00:04:47 +01:00
try {
2014-12-11 18:54:50 +01:00
/*
* Unfortunately , we need to use exec here due to PDO ' s lack of support for importing
* multi - line . sql files . PDO -> exec () almost works , but any SQL errors stop the import ,
* so the necessary DROPs on non - existent tables make it unusable . Prepared statements
* have multiple issues ; they similarly die on any SQL errors , fail to read in multi - line
* commands , and fail on any unescaped ? or $ characters .
*/
2015-06-24 01:02:55 +02:00
exec ( " export PGPASSWORD= " . self :: $_properties [ " dbpass " ] . " && psql -U " . self :: $_properties [ " dbuser " ]
. " --dbname " . self :: $_properties [ " dbname " ] . " -h " . self :: $_properties [ " host " ]
. " -f $sqlDir $f 2>/dev/null " , $out , $status );
2014-12-09 23:48:16 +01:00
} catch ( Exception $e ) {
2014-12-11 18:54:50 +01:00
throw new AirtimeDatabaseException ( " There was an error setting up the Airtime schema! " ,
array ( self :: DB_NAME ,));
2014-12-04 00:04:47 +01:00
}
}
}
2014-12-09 23:48:16 +01:00
/**
* Checks whether the newly - created database ' s encoding was properly set to UTF8
2014-12-11 18:54:50 +01:00
* @ throws AirtimeDatabaseException
2014-12-09 23:48:16 +01:00
*/
2014-12-11 18:54:50 +01:00
private function checkDatabaseEncoding () {
$statement = self :: $dbh -> prepare ( " SELECT pg_encoding_to_char(encoding) "
2014-12-11 21:54:55 +01:00
. " FROM pg_database WHERE datname = :dbname " );
2015-06-24 01:02:55 +02:00
$statement -> execute ( array ( " :dbname " => self :: $_properties [ " dbname " ]));
2014-12-11 18:54:50 +01:00
$encoding = $statement -> fetch ();
if ( ! ( $encoding && $encoding [ 0 ] == " UTF8 " )) {
throw new AirtimeDatabaseException ( " The database was installed with an incorrect encoding type! " ,
array ( self :: DB_NAME ,));
}
2014-12-09 23:48:16 +01:00
}
2020-01-02 03:03:30 +01:00
/**
* Updates the icecast password in the database based upon the temp file created during install
* @ throws AirtimeDatabaseException
*/
private function updateIcecastPassword () {
if ( ! file_exists ( LIBRETIME_CONF_DIR . '/icecast_pass' )) {
throw new AirtimeDatabaseException ( " The Icecast Password file was not accessible " , array ());
};
$icecastPass = file_get_contents ( LIBRETIME_CONF_DIR . '/icecast_pass' , true );
error_log ( $icecastPass );
$statement = self :: $dbh -> prepare ( " UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's1_pass' AND SET value = :icecastpass WHERE keyname = 's1_admin_pass'; "
. " UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's2_pass'; "
. " UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's2_admin_pass'; "
. " UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's3_pass'; "
. " UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's3_admin_pass'; "
. " INSERT INTO cc_pref (keystr, valstr) VALUES ('default_icecast_password', :icecastpass ) " );
if ( ! $statement -> execute ( array ( " :icecastpass " => $icecastPass ))) {
throw new AirtimeDatabaseException ( " Could not update the database with icecast password! " , array ());
}
}
2014-12-09 23:48:16 +01:00
2017-11-13 21:03:15 +01:00
}