.
*
* @category Installation
* @package Installation
*
* @author Adrian Lang %s
', $req));
$pass = false;
}
}
// Make sure we have at least one database module available
$missingExtensions = array();
foreach (self::$dbModules as $type => $info) {
if (!$this->checkExtension($info['check_module'])) {
$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;
}
// @fixme this check seems to be insufficient with Windows ACLs
if (!is_writable(INSTALLDIR)) {
$this->warning(sprintf('Cannot write config file to: %s
chmod a+w %s
', INSTALLDIR));
$pass = false;
}
// Check the subdirs used for file uploads
$fileSubdirs = array('avatar', 'background', 'file');
foreach ($fileSubdirs as $fileSubdir) {
$fileFullPath = INSTALLDIR."/$fileSubdir/";
if (!is_writable($fileFullPath)) {
$this->warning(sprintf('Cannot write to %s directory: %s
', $fileSubdir, $fileFullPath),
sprintf('On your server, try this command: chmod a+w %s
', $fileFullPath));
$pass = false;
}
}
return $pass;
}
/**
* Checks if a php extension is both installed and loaded
*
* @param string $name of extension to check
*
* @return boolean whether extension is installed and loaded
*/
function checkExtension($name)
{
if (extension_loaded($name)) {
return true;
} elseif (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode')) {
// dl will throw a fatal error if it's disabled or we're in safe mode.
// More fun, it may not even exist under some SAPIs in 5.3.0 or later...
$soname = $name . '.' . PHP_SHLIB_SUFFIX;
if (PHP_SHLIB_SUFFIX == 'dll') {
$soname = "php_" . $soname;
}
return @dl($soname);
} else {
return false;
}
}
/**
* Basic validation on the database paramters
* Side effects: error output if not valid
*
* @return boolean success
*/
function validateDb()
{
$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;
}
/**
* Basic validation on the administrator user paramters
* Side effects: error output if not valid
*
* @return boolean success
*/
function validateAdmin()
{
$fail = false;
if (empty($this->adminNick)) {
$this->updateStatus("No initial user nickname specified.", true);
$fail = true;
}
if ($this->adminNick && !preg_match('/^[0-9a-z]{1,64}$/', $this->adminNick)) {
$this->updateStatus('The user nickname "' . htmlspecialchars($this->adminNick) .
'" is invalid; should be plain letters and numbers no longer than 64 characters.', true);
$fail = true;
}
// @fixme hardcoded list; should use Nickname::isValid()
// if/when it's safe to have loaded the infrastructure here
$blacklist = array('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');
if (in_array($this->adminNick, $blacklist)) {
$this->updateStatus('The user nickname "' . htmlspecialchars($this->adminNick) .
'" is reserved.', true);
$fail = true;
}
if (empty($this->adminPass)) {
$this->updateStatus("No initial user password specified.", true);
$fail = true;
}
return !$fail;
}
/**
* Make sure a site profile was selected
*
* @return type boolean success
*/
function validateSiteProfile()
{
if (empty($this->siteProfile)) {
$this->updateStatus("No site profile selected.", true);
return false;
}
return true;
}
/**
* Set up the database with the appropriate function for the selected type...
* Saves database info into $this->db.
*
* @fixme escape things in the connection string in case we have a funny pass etc
* @return mixed array of database connection params on success, false on failure
*/
function setupDatabase()
{
if ($this->db) {
throw new Exception("Bad order of operations: DB already set up.");
}
$this->updateStatus("Starting installation...");
if (empty($this->password)) {
$auth = '';
} else {
$auth = ":$this->password";
}
$scheme = self::$dbModules[$this->dbtype]['scheme'];
$dsn = "{$scheme}://{$this->username}{$auth}@{$this->host}/{$this->database}";
$this->updateStatus("Checking database...");
$conn = $this->connectDatabase($dsn);
// ensure database encoding is UTF8
if ($this->dbtype == 'mysql') {
// @fixme utf8m4 support for mysql 5.5?
// Force the comms charset to utf8 for sanity
// This doesn't currently work. :P
//$conn->executes('set names utf8');
} else if ($this->dbtype == 'pgsql') {
$record = $conn->getRow('SHOW server_encoding');
if ($record->server_encoding != 'UTF8') {
$this->updateStatus("GNU social requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
return false;
}
}
if (!$conn instanceof DB_common) {
// Is not the right instance
throw new Exception('Cannot connect to database: ' . $conn->getMessage());
}
$res = $this->updateStatus("Creating database tables...");
if (!$this->createCoreTables($conn)) {
$this->updateStatus("Error creating tables.", true);
return false;
}
foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source',
'foreign_services' => 'foreign service')
as $scr => $name) {
$this->updateStatus(sprintf("Adding %s data to database...", $name));
$res = $this->runDbScript($scr.'.sql', $conn);
if ($res === false) {
$this->updateStatus(sprintf("Can't run %s script.", $name), true);
return false;
}
}
$db = array('type' => $this->dbtype, 'database' => $dsn);
return $db;
}
/**
* Open a connection to the database.
*
* @param