/**
* StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
+ * Copyright (C) 2009-2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* @author Sarven Capadisli <csarven@status.net>
* @author Tom Adams <tom@holizz.com>
* @author Zach Copley <zach@status.net>
+ * @copyright 2009-2010 StatusNet, Inc http://status.net
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/
- * @version 0.9.x
+ * @version 1.0.x
* @link http://status.net
*/
'mysql' => array(
'name' => 'MySQL',
'check_module' => 'mysqli',
- 'installer' => 'mysql_db_installer',
+ 'scheme' => 'mysqli', // DSN prefix for PEAR::DB
),
'pgsql' => array(
'name' => 'PostgreSQL',
'check_module' => 'pgsql',
- 'installer' => 'pgsql_db_installer',
+ 'scheme' => 'pgsql', // DSN prefix for PEAR::DB
),
);
$config = INSTALLDIR.'/config.php';
if (file_exists($config)) {
if (!is_writable($config) || filesize($config) > 0) {
- $this->warning('Config file "config.php" already exists.');
+ 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.');
+ }
$pass = false;
}
}
if (version_compare(PHP_VERSION, '5.2.3', '<')) {
- $errors[] = 'Require PHP version 5.2.3 or greater.';
+ $this->warning('Require PHP version 5.2.3 or greater.');
$pass = false;
}
}
// @fixme hardcoded list; should use User::allowed_nickname()
// if/when it's safe to have loaded the infrastructure here
- $blacklist = array('main', 'admin', 'twitter', 'settings', 'rsd.xml', 'favorited', 'featured', 'favoritedrss', 'featuredrss', 'rss', 'getfile', 'api', 'groups', 'group', 'peopletag', 'tag', 'user', 'message', 'conversation', 'bookmarklet', 'notice', 'attachment', 'search', 'index.php', 'doc', 'opensearch', 'robots.txt', 'xd_receiver.html', 'facebook');
+ $blacklist = array('main', 'panel', 'twitter', 'settings', 'rsd.xml', 'favorited', 'featured', 'favoritedrss', 'featuredrss', 'rss', 'getfile', 'api', 'groups', 'group', 'peopletag', 'tag', 'user', 'message', 'conversation', 'bookmarklet', '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);
* 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.");
}
- $method = self::$dbModules[$this->dbtype]['installer'];
- $db = call_user_func(array($this, $method),
- $this->host,
- $this->database,
- $this->username,
- $this->password);
- $this->db = $db;
- return $this->db;
- }
-
- /**
- * Set up a database on PostgreSQL.
- * Will output status updates during the operation.
- *
- * @param string $host
- * @param string $database
- * @param string $username
- * @param string $password
- * @return mixed array of database connection params on success, false on failure
- *
- * @fixme escape things in the connection string in case we have a funny pass etc
- */
- function Pgsql_Db_installer($host, $database, $username, $password)
- {
- $connstring = "dbname=$database host=$host user=$username";
-
- //No password would mean trust authentication used.
- if (!empty($password)) {
- $connstring .= " password=$password";
- }
$this->updateStatus("Starting installation...");
- $this->updateStatus("Checking database...");
- $conn = pg_connect($connstring);
- if ($conn ===false) {
- $this->updateStatus("Failed to connect to database: $connstring");
- return false;
+ if (empty($this->password)) {
+ $auth = '';
+ } else {
+ $auth = ":$this->password";
}
+ $scheme = self::$dbModules[$this->dbtype]['scheme'];
+ $dsn = "{$scheme}://{$this->username}{$auth}@{$this->host}/{$this->database}";
- //ensure database encoding is UTF8
- $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
- if ($record->server_encoding != 'UTF8') {
- $this->updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
- return false;
+ $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("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
+ return false;
+ }
}
- $this->updateStatus("Running database script...");
- //wrap in transaction;
- pg_query($conn, 'BEGIN');
- $res = $this->runDbScript('statusnet_pg.sql', $conn, 'pgsql');
-
- if ($res === false) {
- $this->updateStatus("Can't run database script.", true);
+ $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, 'pgsql');
+ $res = $this->runDbScript($scr.'.sql', $conn);
if ($res === false) {
$this->updateStatus(sprintf("Can't run %d script.", $name), true);
return false;
}
}
- pg_query($conn, 'COMMIT');
-
- if (empty($password)) {
- $sqlUrl = "pgsql://$username@$host/$database";
- } else {
- $sqlUrl = "pgsql://$username:$password@$host/$database";
- }
-
- $db = array('type' => 'pgsql', 'database' => $sqlUrl);
+ $db = array('type' => $this->dbtype, 'database' => $dsn);
return $db;
}
/**
- * Set up a database on MySQL.
- * Will output status updates during the operation.
- *
- * @param string $host
- * @param string $database
- * @param string $username
- * @param string $password
- * @return mixed array of database connection params on success, false on failure
- *
- * @fixme escape things in the connection string in case we have a funny pass etc
+ * Open a connection to the database.
+ *
+ * @param <type> $dsn
+ * @return <type>
*/
- function Mysql_Db_installer($host, $database, $username, $password)
+ function connectDatabase($dsn)
{
- $this->updateStatus("Starting installation...");
- $this->updateStatus("Checking database...");
-
- $conn = mysqli_init();
- if (!$conn->real_connect($host, $username, $password)) {
- $this->updateStatus("Can't connect to server '$host' as '$username'.", true);
- return false;
- }
- $this->updateStatus("Changing to database...");
- if (!$conn->select_db($database)) {
- $this->updateStatus("Can't change to database.", true);
- return false;
- }
+ // @fixme move this someplace more sensible
+ //set_include_path(INSTALLDIR . '/extlib' . PATH_SEPARATOR . get_include_path());
+ require_once 'DB.php';
+ return DB::connect($dsn);
+ }
- $this->updateStatus("Running database script...");
- $res = $this->runDbScript('statusnet.sql', $conn);
- if ($res === false) {
- $this->updateStatus("Can't run database script.", 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 %d script.", $name), true);
- return false;
+ /**
+ * Create core tables on the given database connection.
+ *
+ * @param DB_common $conn
+ */
+ function createCoreTables(DB_common $conn)
+ {
+ $schema = Schema::get($conn);
+ $tableDefs = $this->getCoreSchema();
+ foreach ($tableDefs as $name => $def) {
+ if (defined('DEBUG_INSTALLER')) {
+ echo " $name ";
}
+ $schema->ensureTable($name, $def);
}
+ return true;
+ }
- $sqlUrl = "mysqli://$username:$password@$host/$database";
- $db = array('type' => 'mysql', 'database' => $sqlUrl);
- return $db;
+ /**
+ * Fetch the core table schema definitions.
+ *
+ * @return array of table names => table def arrays
+ */
+ function getCoreSchema()
+ {
+ $schema = array();
+ include INSTALLDIR . '/db/core.php';
+ return $schema;
+ }
+
+ /**
+ * Return a parseable PHP literal for the given value.
+ * This will include quotes for strings, etc.
+ *
+ * @param mixed $val
+ * @return string
+ */
+ function phpVal($val)
+ {
+ return var_export($val, true);
+ }
+
+ /**
+ * Return an array of parseable PHP literal for the given values.
+ * These will include quotes for strings, etc.
+ *
+ * @param mixed $val
+ * @return array
+ */
+ function phpVals($map)
+ {
+ return array_map(array($this, 'phpVal'), $map);
}
/**
*/
function writeConf()
{
+ $vals = $this->phpVals(array(
+ 'sitename' => $this->sitename,
+ 'server' => $this->server,
+ 'path' => $this->path,
+ 'db_database' => $this->db['database'],
+ 'db_type' => $this->db['type'],
+ ));
+
// assemble configuration file in a string
$cfg = "<?php\n".
"if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
// site name
- "\$config['site']['name'] = '{$this->sitename}';\n\n".
+ "\$config['site']['name'] = {$vals['sitename']};\n\n".
// site location
- "\$config['site']['server'] = '{$this->server}';\n".
- "\$config['site']['path'] = '{$this->path}'; \n\n".
+ "\$config['site']['server'] = {$vals['server']};\n".
+ "\$config['site']['path'] = {$vals['path']}; \n\n".
// checks if fancy URLs are enabled
($this->fancy ? "\$config['site']['fancy'] = true;\n\n":'').
// database
- "\$config['db']['database'] = '{$this->db['database']}';\n\n".
+ "\$config['db']['database'] = {$vals['db_database']};\n\n".
($this->db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
- "\$config['db']['type'] = '{$this->db['type']}';\n\n";
+ "\$config['db']['type'] = {$vals['db_type']};\n\n";
// Normalize line endings for Windows servers
$cfg = str_replace("\n", PHP_EOL, $cfg);
/**
* Install schema into the database
*
- * @param string $filename location of database schema file
- * @param dbconn $conn connection to database
- * @param string $type type of database, currently mysql or pgsql
+ * @param string $filename location of database schema file
+ * @param DB_common $conn connection to database
*
* @return boolean - indicating success or failure
*/
- function runDbScript($filename, $conn, $type = 'mysqli')
+ function runDbScript($filename, DB_common $conn)
{
$sql = trim(file_get_contents(INSTALLDIR . '/db/' . $filename));
$stmts = explode(';', $sql);
if (!mb_strlen($stmt)) {
continue;
}
- // FIXME: use PEAR::DB or PDO instead of our own switch
- switch ($type) {
- case 'mysqli':
- $res = $conn->query($stmt);
- if ($res === false) {
- $error = $conn->error;
- }
- break;
- case 'pgsql':
- $res = pg_query($conn, $stmt);
- if ($res === false) {
- $error = pg_last_error();
- }
- break;
- default:
- $this->updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
- }
- if ($res === false) {
+ try {
+ $res = $conn->simpleQuery($stmt);
+ } catch (Exception $e) {
+ $error = $e->getMessage();
$this->updateStatus("ERROR ($error) for SQL '$stmt'");
- return $res;
+ return false;
}
}
return true;
*/
function registerInitialUser()
{
- define('STATUSNET', true);
- define('LACONICA', true); // compatibility
-
require_once INSTALLDIR . '/lib/common.php';
$data = array('nickname' => $this->adminNick,
*/
function doInstall()
{
- $this->db = $this->setupDatabase();
-
- if (!$this->db) {
- // database connection failed, do not move on to create config file.
+ $this->updateStatus("Initializing...");
+ ini_set('display_errors', 1);
+ error_reporting(E_ALL);
+ define('STATUSNET', 1);
+ require_once INSTALLDIR . '/lib/framework.php';
+ StatusNet::initDefaults($this->server, $this->path);
+
+ 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);
return false;
}