diff options
Diffstat (limited to 'setup/index.php')
-rw-r--r-- | setup/index.php | 1226 |
1 files changed, 1226 insertions, 0 deletions
diff --git a/setup/index.php b/setup/index.php new file mode 100644 index 0000000..f55e748 --- /dev/null +++ b/setup/index.php @@ -0,0 +1,1226 @@ +<?php +// +---------------------------------------------------------------------- +// | Installer - there is still a lot to clean up, but it works +// +---------------------------------------------------------------------- +// | Copyright (C) 2005 by Jeffery Fernandez <developer@jefferyfernandez.id.au> +// | Copyright (C) 2006-2007 by Cristian Rodriguez <judas.iscariote@flyspray.org> and Florian Schmitz <floele@gmail.com> +// +---------------------------------------------------------------------- + +@set_time_limit(0); +ini_set('memory_limit', '64M'); + +define('IN_FS', 1 ); +define('APPLICATION_NAME', 'Flyspray'); +define('BASEDIR', dirname(__FILE__)); +define('APPLICATION_PATH', dirname(BASEDIR)); +define('OBJECTS_PATH', APPLICATION_PATH . '/includes'); +define('TEMPLATE_FOLDER', BASEDIR . '/templates/'); + +require_once OBJECTS_PATH.'/fix.inc.php'; +require_once OBJECTS_PATH.'/class.gpc.php'; +require_once OBJECTS_PATH.'/class.flyspray.php'; +require_once OBJECTS_PATH.'/i18n.inc.php'; +require_once OBJECTS_PATH.'/class.tpl.php'; + +// Load translations +load_translations(); + +# must be sure no-cache before any possible redirect, we maybe come back later here after composer install stuff. +header("Expires: Tue, 03 Jul 2001 06:00:00 GMT"); +header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +if (is_readable(APPLICATION_PATH . '/vendor/autoload.php')){ + // Use composer autoloader + require APPLICATION_PATH . '/vendor/autoload.php'; +} else{ + Flyspray::redirect('composertest.php'); + exit; +} + +// no transparent session id improperly configured servers +ini_set('session.use_trans_sid', 0); +session_start(); + +if (is_readable('../flyspray.conf.php') && count(parse_ini_file('../flyspray.conf.php')) > 0){ + die('<div style="text-align:center;padding:20px;font-family:sans-serif;font-size:16px;"> +Flyspray already installed. Use the <a href="upgrade.php" +style=" +margin:2em; +background-color: white; +border: 1px solid #bbb; +border-radius: 4px; +box-shadow: 0 1px 1px #ddd; +color: #565656; +cursor: pointer; +display: inline-block; +font-family: sans-serif; +font-size: 100%; +font-weight: bold; +line-height: 130%; +padding: 8px 13px 8px 10px; +text-decoration: none; +">Upgrader</a> to upgrade your Flyspray, +or delete flyspray.conf.php to run setup. You can *not* use the setup on an existing database.</div>'); +} + +$conf['general']['syntax_plugin'] = ''; + +// --------------------------------------------------------------------- +// Application Web locations +// --------------------------------------------------------------------- +define('APPLICATION_SETUP_INDEX', Flyspray::absoluteURI()); + +class Setup extends Flyspray +{ + public $mPhpRequired; + public $mSupportedDatabases; + public $mAvailableDatabases; + + public $mProceed; + public $mPhpVersionStatus; + public $mDatabaseStatus; + public $xmlStatus; + public $mConfigText; + public $mHtaccessText; + public $mWriteStatus; + + public $mDbConnection; + public $mProductName; + + /** + * @var string To store the data filter type + */ + public $mDataFilter; + + /** + * @var array To store the type of database setup (install or Upgrade). + */ + + public $mAttachmentsTable; + public $mCommentsTable; + + public $mServerSoftware; + public $mMinPasswordLength; + public $mAdminUsername; + public $mAdminPassword; + /** + * @var object to store the adodb datadict object. + */ + public $mDataDict; + + public $mXmlSchema; + + public function __construct() + { + // Look for ADOdb + $this->mAdodbPath = dirname(__DIR__) . '/vendor/adodb/adodb-php/adodb.inc.php'; + $this->mProductName = 'Flyspray'; + $this->mMinPasswordLength = 8; + + // Initialise flag for proceeding to next step. + $this->mProceed = false; + $this->mPhpRequired = '5.4'; + $this->xmlStatus = function_exists('xml_parser_create'); + $this->sapiStatus = (php_sapi_name() != 'cgi'); + + // If the database is supported in Flyspray, the function to check in PHP. + $this->mSupportedDatabases = array( + 'MySQLi' => array(true,'mysqli_connect','mysqli'), + 'MySQL' => array(true, 'mysql_connect', 'mysql'), + 'Postgres' => array(true, 'pg_connect', 'pgsql'), + ); + $this->mAvailableDatabases = array(); + + // Process the page actions + $this->processActions(); + } + + /** + * Function to check the permission of the config file + * @param void + * @return string An html formatted boolean answer + */ + public function checkWriteability($path) + { + // Get the full path to the file + $file = APPLICATION_PATH .'/' . $path; + + // In case it is flyspray.conf.php, the file does not exist + // so we can't tell that it is writeable. So we attempt to create an empty one + if ($path == 'flyspray.conf.php') { + $fp = @fopen($file, 'wb'); + @fclose($fp); + // Let's try at least... + #@chmod($file, 0666); + @chmod($file, 0644); # looks a bit better than worldwritable + } + if(is_dir($path)){ + # for cache and attachement directories x-bit needed + @chmod($file, 0755); + } + $this->mWriteStatus[$path] = $this->IsWriteable($file); + + // Return an html formated writeable/un-writeable string + return $this->returnStatus($this->mWriteStatus[$path], $type = 'writeable'); + } + + /** + * Function to check the availability of the Database support + * @param void + * @return void + */ + public function checkDatabaseSupport() + { + $status = array(); + + foreach ($this->mSupportedDatabases as $which => $database) + { + // Checking if the database has libraries built into PHP. Returns true/false + $this->mAvailableDatabases[$which]['status'] = function_exists($database[1]); + + // If the Application(Flyspray) supports the available database supported in PHP + $this->mAvailableDatabases[$which]['supported'] = ($database[0] === $this->mAvailableDatabases[$which]['status']) + ? $this->mAvailableDatabases[$which]['status'] + : false; + + // Just transferring the value for ease of veryfying Database support. + $status[] = $this->mAvailableDatabases[$which]['supported']; + + // Generating the output to be displayed + $this->mAvailableDatabases[$which]['status_output'] = + $this->returnStatus($this->mAvailableDatabases[$which]['status'], $type = 'available'); + } + + // Check if any one database support exists. + // Update the status of database availability + $this->mDatabaseStatus = in_array('1', $status); + } + + + /** + * CheckPreStatus + * we proceed or not ? + * @access public + * @return bool + */ + public function checkPreStatus() + { + $this->mProceed = ($this->mDatabaseStatus && $this->mPhpVersionStatus && $this->xmlStatus); + + return $this->mProceed; + } + + + /** + * Function to check the version of PHP available compared to the + * Applications requirements + * @param void + * @return string An html formatted boolean answer + */ + public function checkPhpCompatibility() + { + // Check the PHP version. + $this->mPhpVersionStatus = version_compare(PHP_VERSION, $this->mPhpRequired, '>='); + + // Return an html formated Yes/No string + return $this->returnStatus($this->mPhpVersionStatus, $type = 'yes'); + } + + /** + * Function to check the posted data from forms. + * @param array $expectedFields Array of field names which needs processing + * If the array of filed names don't exist in the Posted data, then this function + * will accumulate error messages in the $_SESSION[PASS_PHRASE]['page_message'] array. + * return boolean/array $data will be returned if successful + */ + public function checkPostedData($expectedFields, $pageHeading) + { + if(!is_array($expectedFields)){ + $expectedFields = array(); + } + + // Grab the posted data and trim it. + $data = array_filter($_POST, array(&$this, "trimArgs")); + + + // Loop through the required values and check data + foreach($expectedFields as $key => $value) + { + + // If the data is Required and is empty or not set + if (!isset($data[$key]) || empty($data[$key])) + { + if ($expectedFields[$key][2] == true) + { + // accumulate error messages + $_SESSION['page_message'][] = "<strong>{$expectedFields[$key][0]}</strong> is required"; + } + } + // Check for variable types + elseif (!$this->verifyVariableTypes($expectedFields[$key][1], $data[$key])) + { + $_SESSION['page_message'][] = "<strong>{$expectedFields[$key][0]}</strong> has to be a {$expectedFields[$key][1]}"; + } + } + + // If there were messages, return false + if (isset($_SESSION['page_message'])) + { + $_SESSION['page_heading'] = $pageHeading; + return false; + } + else + { + return $data; + } + } + + public function displayAdministration() + { + // Trim the empty values in the $_POST array + $data = array_filter($_POST, array($this, "trimArgs")); + + $templates = + array( + 'admin_body' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'administration.tpl', + 'vars' => array( + 'product_name' => $this->mProductName, + 'message' => $this->getPageMessage(), + 'admin_email' => $this->getParamValue($data, 'admin_email', ''), + 'pass_phrase' => $this->getParamValue($data, 'pass_phrase', ''), + 'admin_username' => $this->getParamValue($data, 'admin_username', ''), + 'admin_realname' => $this->getParamValue($data, 'admin_realname', ''), + 'admin_xmpp' => $this->getParamValue($data, 'admin_xmpp', ''), + 'admin_password' => $this->getParamValue($data, 'admin_password', substr(md5(mt_rand()), 0, $this->mMinPasswordLength)), + 'db_type' => $this->getParamValue($data, 'db_type', ''), + 'db_hostname' => $this->getParamValue($data, 'db_hostname', ''), + 'db_username' => $this->getParamValue($data, 'db_username', ''), + 'db_password' => $this->getParamValue($data, 'db_password', ''), + 'db_name' => $this->getParamValue($data, 'db_name', ''), + 'db_prefix' => $this->getParamValue($data, 'db_prefix', ''), + 'daemonise' => $this->getReminderDaemonSelection($this->getParamValue($data, 'reminder_daemon', '0')), + ), + ), + + 'structure' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'structure.tpl', + 'vars' => array( + 'title' => 'Administration setup for', + 'headers' => '', + 'index' => APPLICATION_SETUP_INDEX, + 'version' => $this->version, + ), + 'block' => array('body' => 'admin_body') + ) + ); + + // Output the final template. + $this->outputPage($templates); + } + + + public function displayCompletion() + { + // Trim the empty values in the $_POST array + $data = array_filter($_POST, array($this, "trimArgs")); + + $templates = + array( + 'complete_body' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'complete_install.tpl', + 'vars' => array( + 'product_name' => $this->mProductName, + 'message' => $this->getPageMessage(), + 'config_writeable' => $this->mWriteStatus['flyspray.conf.php'], + 'config_text' => $this->mConfigText, + 'admin_username' => $this->mAdminUsername, + 'admin_password' => $this->mAdminPassword, + 'site_index' => dirname($_SERVER['REQUEST_URI']) . '/../', + 'complete_action' => 'index.php', + 'daemonise' => true, + ), + ), + + 'structure' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'structure.tpl', + 'vars' => array( + 'title' => 'Setup confirmation for', + 'headers' => '', + 'index' => APPLICATION_SETUP_INDEX, + 'version' => $this->version, + ), + 'block' => array('body' => 'complete_body') + ) + ); + + // Output the final template. + $this->outputPage($templates); + } + + public function displayDatabaseSetup() + { + + // Trim the empty values in the $_POST array + $data = array_filter($_POST, array($this, "trimArgs")); + $this->checkDatabaseSupport(); + + // Make sure that the user can't choose a DB which is not supported + foreach ($this->mSupportedDatabases as $db => $arr) { + if (!$this->mAvailableDatabases[$db]['supported']) { + unset($this->mSupportedDatabases[$db]); + } + } + + $templates = + array( + 'database_body' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'database.tpl', + 'vars' => array( + 'product_name' => $this->mProductName, + 'message' => $this->getPageMessage(), + 'databases' => $this->mSupportedDatabases, + 'db_type' => $this->getParamValue($data, 'db_type', ''), + 'db_hostname' => $this->getParamValue($data, 'db_hostname', 'localhost'), + 'db_username' => $this->getParamValue($data, 'db_username', ''), + 'db_password' => $this->getParamValue($data, 'db_password', ''), + 'db_name' => $this->getParamValue($data, 'db_name', ''), + 'db_prefix' => $this->getParamValue($data, 'db_prefix', 'flyspray_'), + 'version' => $this->version, + ), + ), + 'structure' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'structure.tpl', + 'vars' => array( + 'title' => 'Database setup for', + 'headers' => '', + 'index' => APPLICATION_SETUP_INDEX, + 'version' => $this->version, + ), + 'block' => array('body' => 'database_body') + ) + ); + + // Output the final template. + $this->outputPage($templates); + } + + + public function displayPreInstall() + { + // Check the Database support on the server. + $this->checkDatabaseSupport(); + + $templates = + array( + 'index_body' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'pre_install.tpl', + 'vars' => array( + 'product_name' => $this->mProductName, + 'required_php' => $this->mPhpRequired, + 'php_output' => $this->checkPhpCompatibility(), + 'database_output' => $this->getDatabaseOutput(), + 'config_output' => $this->checkWriteability('flyspray.conf.php'), + 'cache_output' => $this->checkWriteability('cache'), + 'att_output' => $this->checkWriteability('attachments'), + 'ava_output' => $this->checkWriteability('avatars'), + 'config_status' => $this->mWriteStatus['flyspray.conf.php'], + 'xmlStatus' => $this->xmlStatus, + 'sapiStatus' => $this->sapiStatus, + 'php_settings' => $this->getPhpSettings(), + 'status' => $this->checkPreStatus(), + 'message' => $this->getPageMessage(), + ), + ), + + 'structure' => array( + 'path' => TEMPLATE_FOLDER, + 'template' => 'structure.tpl', + 'vars' => array( + 'title' => 'Pre-Installation Check for', + 'headers' => '', + 'index' => APPLICATION_SETUP_INDEX, + 'version' => $this->version, + ), + 'block' => array('body' => 'index_body') + ) + ); + + // Output the final template. + $this->outputPage($templates); + } + + public function getDatabaseOutput() + { + $output = ''; + // Loop through the supported databases array + foreach ($this->mSupportedDatabases as $which => $database) + { + $output .= " + <tr> + <td> - $which support</td> + <td align=\"left\"><strong>{$this->mAvailableDatabases[$which]['status_output']}</strong></td> + <td align=\"center\"><strong>". $this->returnStatus($this->mAvailableDatabases[$which]['supported'], $type = 'support') . "</strong></td> + </tr>"; + + } + // Return the html formatted results + return $output; + } + + + /** + * Function to get the php ini config values + * @param string $option The ini setting name to check the status for + * @return string The status of the setting either "On" or "OFF" + */ + public function getIniSetting($option) + { + return (ini_get($option) == '1' ? L('on') : L('off')); + } + + /** + * Function to get the error messages and generate an error output for the template + * @string $heading The value for the Error message heading + * The error message is stored in the $_SESSION Global array + * $_SESSION[PASS_PHRASE]['page_message']. If there is no value in + * this array, then there will be no error message outputed. + * @return string $message The message which needs outputting + */ + public function getPageMessage() + { + // If there is an error + if (isset($_SESSION['page_message']) || isset($_SESSION['page_heading'])) + { + $message = ''; + if (isset($_SESSION['page_heading'])) { + $message = '<h1 class="error">' . $_SESSION['page_heading'] . '</h1>'; + } + + if (isset($_SESSION['page_message'])) { + // Get an html formated list + $message .= '<div class="box"><div class="shade">' . $this->outputHtmlList($_SESSION['page_message'],'ul') . '</div></div>'; + } + + + // Destroy the session value + unset($_SESSION['page_heading']); + unset($_SESSION['page_message']); + + return $message; + } + else + { + return ''; + } + } + + + /** + * Utility function to return a value from a named array or a specified default + * @param array &$arr The array to get the values from + * @param string $name The name of the key to check the value for + * @param string $default The default value if the value is not set with the array + * @return string $value The value to be returned + */ + public function getParamValue(&$arr, $name, $default=null ) + { + $value = isset($arr[$name]) ? $arr[$name] : $default; + return $value; + } + + /** + * Function to get a listing of recommended and actual settings + * for php. + * @param void + * @return string $output HTML formatted string. + */ + public function getPhpSettings() + { + // Array of the setting name, php ini name and the recommended value + $test_settings = + array( + //array ('Safe Mode','safe_mode', L('off')), # removed since PHP5.4 + array ('File Uploads','file_uploads', L('on')), + //array ('Magic Quotes GPC','magic_quotes_gpc', L('off')), # removed since PHP5.4 + //array ('Register Globals','register_globals', L('off')), # removed since PHP5.4 + //array ('Output Buffering','output_buffering', L('off')), + ); + + if (substr(php_sapi_name(), 0, 3) == 'cgi') { + $test_settings[] = array('CGI fix pathinfo','cgi.fix_pathinfo', L('on')); + } + + $output = ''; + + foreach ($test_settings as $recommended) + { + $actual_setting = $this->getIniSetting($recommended[1]); + + $result = ($actual_setting == $recommended[2] ) + ? '<span class="green"><strong>' . $recommended[2] . '</strong></span>' + : '<span class="red"><strong>' . $actual_setting . '</strong></span>'; + + $output .= + " + <tr> + <td>{$recommended[0]}</td><td align=\"center\"><strong>{$recommended[2]}</strong></td><td align=\"center\">{$result}</td> + </tr> + "; + } + return $output; + } + + public function getReminderDaemonSelection($value) + { + $selection = '<input type="radio" id="schedyes" name="reminder_daemon" value="1"'.($value==1 ? ' checked="checked"':'').' /> <label for="schedyes">'.L('enable').'</label>'; + $selection .= '<input type="radio" id="schedno" name="reminder_daemon" value="0"'.($value==0 ? ' checked="checked"':'').' /> <label for="schedno">'.L('disable').'</label>'; + return $selection; + } + + + /** + * Function to check if a particular folder/file is writeable. + * @param string $fileSystem Path to check + * $return boolean true/false + */ + public function isWriteable($fileSystem) + { + // Clear the cache + clearstatcache(); + + // Return the status of the permission + return is_writable($fileSystem); + } + + /** + * Function to Output an Ordered/Un-ordered list from an array. Default list type is un-ordered. + * @param array() $list_array An array list of data to be made into a list. + * @return string $list An HTML list + */ + public function outputHtmlList($list_array = array(), $list_type = 'ul') + { + $list = "<$list_type>"; + foreach ($list_array as $list_item) + { + $list .= '<li>' . $list_item .'</li>'; + } + $list .= "</$list_type>"; + + return $list; + } + + + /** + * Function to act on all the actions during Flyspray Setup + * The Post variables are extracted for deciding which function to call. + */ + public function processActions() + { + $action = 'index'; + $what = ''; + + extract($_POST); + + switch($action) + { + case 'database': + $this->displayDatabaseSetup(); + break; + + case 'administration': + // Prepare the required data + $required_data = + array( + 'db_hostname' => array('Database hostname', 'string', true), + 'db_type' => array('Database type', 'string', true), + 'db_username' => array('Database username', 'string', true), + 'db_password' => array('Database password', 'string', false), + 'db_name' => array('Database name', 'string', true), + 'db_prefix' => array('Table prefix', 'string', false), + ); + if ($data = $this->checkPostedData($required_data, $message = 'Configuration Error')) + { + // Process the database checks and install tables + if ($this->processDatabaseSetup($data)) + { + // Proceed to Administration part + $this->displayAdministration(); + } + else + { + $_POST['action'] = 'database'; + $this->displayDatabaseSetup(); + } + } + else + { + $_POST['action'] = 'database'; + $this->displayDatabaseSetup(); + } + break; + + case 'complete': + // Prepare the required data + $required_data = array( + 'db_hostname' => array('Database hostname', 'string', true), + 'db_type' => array('Database type', 'string', true), + 'db_username' => array('Database username', 'string', true), + 'db_password' => array('Database password', 'string', false), + 'db_name' => array('Database name', 'string', true), + 'db_prefix' => array('Table prefix', 'string', false), + 'admin_username' => array('Administrator\'s username', 'string', true), + 'admin_realname' => array('Administrator\'s realname', 'string', false), + 'admin_password' => array("Administrator's Password must be minimum {$this->mMinPasswordLength} characters long and", 'password', true), + 'admin_email' => array('Administrator\'s email address', 'email address', true), + 'admin_xmpp' => array('Administrator\'s jabber/xmpp address', 'xmpp address', false), + 'syntax_plugin' => array('Syntax', 'option', true), + 'reminder_daemon' => array('Reminder Daemon', 'option', false), + ); + if ($data = $this->checkPostedData($required_data, $message = 'Missing config values')) { + // Set a page heading in case of errors. + $_SESSION['page_heading'] = 'Administration Processing'; + + if ($this->processAdminConfig($data)) { + $this->displayCompletion($data); + } else { + $_POST['action'] = 'administration'; + $this->displayAdministration(); + } + } else { + $_POST['action'] = 'administration'; + $this->displayAdministration(); + } + break; + + default: + $this->displayPreInstall(); + break; + } + } + + + + public function processAdminConfig($data) + { + // Extract the variables to local namespace + extract($data); + + if(!isset($db_password)) { + $db_password = ''; + } + if(!isset($admin_xmpp)) { + $admin_xmpp = ''; + } + if(!isset($admin_realname)) { + $admin_realname = ''; + } + + if(!isset($syntax_plugin)) { + $syntax_plugin = ''; + } + + $config_intro = + "; <?php die( 'Do not access this page directly.' ); ?> + + ; This is the Flysplay configuration file. It contains the basic settings + ; needed for Flyspray to operate. All other preferences are stored in the + ; database itself and are managed directly within the Flyspray admin interface. + ; You should consider putting this file somewhere that isn't accessible using + ; a web browser, and editing header.php to point to wherever you put this file.\n"; + $config_intro = str_replace("\t", "", $config_intro); + + // Create a random cookie salt + $cookiesalt = md5(uniqid(mt_rand(), true)); + + // check to see if to enable the Reminder Daemon. + $daemonise = ( (isset($data['reminder_daemon'])) && ($data['reminder_daemon'] == 1) ) + ? 1 + : 0; + $db_prefix = (isset($data['db_prefix']) ? $data['db_prefix'] : ''); + + $config = array(); + $config[] = "[database]"; + $config[] = "dbtype = \"$db_type\" ; Type of database (\"mysql\", \"mysqli\" or \"pgsql\" are currently supported)"; + $config[] = "dbhost = \"$db_hostname\" ; Name or IP of your database server"; + $config[] = "dbname = \"$db_name\" ; The name of the database"; + $config[] = "dbuser = \"$db_username\" ; The user to access the database"; + $config[] = "dbpass = \"$db_password\" ; The password to go with that username above"; + $config[] = "dbprefix = \"$db_prefix\" ; The prefix to the {$this->mProductName} tables"; + $config[] = "\n"; + $config[] = '[general]'; + $config[] = "cookiesalt = \"$cookiesalt\" ; Randomisation value for cookie encoding"; + $config[] = 'output_buffering = "on" ; Available options: "on" or "gzip"'; + $config[] = 'passwdcrypt = "" ; Available options: "" which chooses best default (coming FS1.0: using crypt/password_hash() with blowfish), "crypt" (auto salted md5), "md5", "sha1" Note: md5 and sha1 are considered insecure for hashing passwords, avoid if possible.'; + $config[] = "dot_path = \"\" ; Path to the dot executable (for graphs either dot_public or dot_path must be set)"; + $config[] = "dot_format = \"png\" ; \"png\" or \"svg\""; + $config[] = "reminder_daemon = \"$daemonise\" ; Boolean. 0 = off, 1 = on (cron job), 2 = on (PHP)."; + $config[] = "doku_url = \"http://en.wikipedia.org/wiki/\" ; URL to your external wiki for [[dokulinks]] in FS"; + $config[] = 'syntax_plugin = "'.$syntax_plugin.'" ; dokuwiki, none, or html'; + $config[] = "update_check = \"1\" ; Boolean. 0=off, 1=on"; + $config[] = "\n"; + $config[] = "[attachments]"; + $config[] = "zip = \"application/zip\" ; MIME-type for ZIP files"; + $config[] = "\n"; + $config[] = "[oauth]"; + $config[] = "; These are only needed if you plan to use them. You can turn them on in the admin panel."; + $config[] = "\n"; + $config[] = 'github_secret = ""'; + $config[] = 'github_id = ""'; + $config[] = 'github_redirect = "YOURDOMAIN/index.php?do=oauth&provider=github"'; + $config[] = 'google_secret = ""'; + $config[] = 'google_id = ""'; + $config[] = 'google_redirect = "YOURDOMAIN/index.php?do=oauth&provider=google"'; + $config[] = 'facebook_secret = ""'; + $config[] = 'facebook_id = ""'; + $config[] = 'facebook_redirect = "YOURDOMAIN/index.php?do=oauth&provider=facebook"'; + $config[] = 'microsoft_secret = ""'; + $config[] = 'microsoft_id = ""'; + $config[] = 'microsoft_redirect = "YOURDOMAIN/index.php"'; + + $config_text = $config_intro . implode( "\n", $config ); + + if (is_writable('../flyspray.conf.php') && ($fp = fopen('../flyspray.conf.php', "wb"))) + { + fputs($fp, $config_text, strlen($config_text)); + fclose($fp); + $this->mWriteStatus['flyspray.conf.php'] = true; + } + else + { + $this->mConfigText = $config_text; + $this->mWriteStatus['flyspray.conf.php'] = false; + } + + + // Setting the database for the ADODB connection + require_once($this->mAdodbPath); + + # 20160408 peterdd: hack to enable database socket usage with adodb-5.20.3 . For instance on german 1und1 managed linux servers ( e.g. $db_hostname ='localhost:/tmp/mysql5.sock' ) + if( $db_type=='mysqli' && 'localhost:/'==substr($db_hostname,0,11) ){ + $dbsocket=substr($db_hostname,10); + $db_hostname='localhost'; + ini_set( 'mysqli.default_socket', $dbsocket ); + } + + $this->mDbConnection = ADONewConnection(strtolower($db_type)); + $this->mDbConnection->connect($db_hostname, $db_username, $db_password, $db_name); + $this->mDbConnection->setCharSet('utf8'); + + // Get the users table name. + $users_table = (isset($db_prefix) ? $db_prefix : '') . 'users'; + + $sql = "SELECT * FROM $users_table WHERE user_id = '1'"; + + // Check if we already have an Admin user. + $result = $this->mDbConnection->execute($sql); + if ($result) + { + // If the record exists, we update it. + $row = $result->fetchRow(); + $this->mAdminUsername = $row['user_name']; + $this->mAdminPassword = $row['user_pass']; + } + + $pwhash= Flyspray::cryptPassword($admin_password); + $update_user = " + UPDATE + $users_table + SET + user_name = ?, + user_pass = ?, + email_address = ?, + jabber_id = ?, + real_name = ? + WHERE + user_id = '1'"; + + $update_params = array($admin_username, $pwhash, $admin_email, $admin_xmpp, $admin_realname); + + $result = $this->mDbConnection->execute($update_user, $update_params); + + if (!$result) + { + $errorno = $this->mDbConnection->metaError(); + $_SESSION['page_heading'] = 'Failed to update Admin users details.'; + $_SESSION['page_message'][] = ucfirst($this->mDbConnection->metaErrorMsg($errorno)) . ': '. $this->mDbConnection->errorMsg($errorno); + return false; + } + else + { + $this->mAdminUsername = $admin_username; + $this->mAdminPassword = $admin_password; + } + + return true; + } + + + public function processDatabaseSetup($data) + { + require_once($this->mAdodbPath); + + // Perform a number of fatality checks, then die gracefully + if (!defined('_ADODB_LAYER')) + { + trigger_error('ADODB Libraries missing or not correct version'); + } + + # 20160408 peterdd: hack to enable database socket usage with adodb-5.20.3 . For instance on german 1und1 managed linux servers ( e.g. $data['db_hostname'] ='localhost:/tmp/mysql5.sock' ) + if( strtolower($data['db_type'])=='mysqli' && 'localhost:/'==substr($data['db_hostname'],0,11) ){ + $dbsocket=substr($data['db_hostname'],10); + $data['db_hostname']='localhost'; + ini_set( 'mysqli.default_socket', $dbsocket ); + } + + // Setting the database type for the ADODB connection + $this->mDbConnection = ADONewConnection(strtolower($data['db_type'])); + if (!$this->mDbConnection->connect(array_get($data, 'db_hostname'), array_get($data, 'db_username'), array_get($data, 'db_password'), array_get($data, 'db_name'))) + { + $_SESSION['page_heading'] = 'Database Processing'; + switch($error_number = $this->mDbConnection->metaError()) + { + case '-1': + // We are using the unknown error code(-1) because ADOdb library may not have the error defined. + // It could be totally some weird error. + $_SESSION['page_message'][] = $this->mDbConnection->errorMsg(); + return false; + break; + + case '-24': + // Could not connect to database with the hostname provided + $_SESSION['page_message'][] = ucfirst($this->mDbConnection->metaErrorMsg($error_number)) . ': ' . ucfirst($this->mDbConnection->ErrorMsg($error_number)); + $_SESSION['page_message'][] = 'Usually the database host name is "localhost". In some occassions, it maybe an internal ip-address or another host name to your webserver.'; + $_SESSION['page_message'][] = 'Double check with your hosting provider or System Administrator.'; + return false; + break; + + case '-25': + // Database does not exist, try to create one + $this->mDbConnection = ADONewConnection(strtolower($data['db_type'])); + $this->mDbConnection->connect(array_get($data, 'db_hostname'), array_get($data, 'db_username'), array_get($data, 'db_password')); + $dict = NewDataDictionary($this->mDbConnection); + + # if possible set correct default character set for mysql. + # MySQL below 5.5.3 only supports 1,2,3 byte chars of utf8. But some language's chars or emojis(argh) are defined as 4byte chars + $mysqldbcharset='DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci'; # default for mysql for compat + if( $data['db_type']=='mysqli' || $data['db_type']=='mysql' ) { + $dbinfo=$this->mDbConnection->serverInfo(); # provides 'description' and 'version' + if( version_compare($dbinfo['version'], '5.5.3') >=0 ){ + $mysqldbcharset='DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'; + $this->mDbConnection->setCharSet('utf8mb4'); + }else{ + $mysqldbcharset='DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci'; + $this->mDbConnection->setCharSet('utf8'); + $_SESSION['page_message'][]='Your MySQL server '.$dbinfo['version'].' < 5.5.3, so database has limited utf8 support (no unicode emojis for instance). Upgrading your MySQL server to 5.5.3 or newer is suggested.'; + } + }else{ + # postgresql + $this->mDbConnection->setCharSet('utf8'); + } + + $sqlarray = $dict->createDatabase(array_get($data, 'db_name'), array('mysql'=>$mysqldbcharset) ); + + if (!$dict->executeSQLArray($sqlarray)) { + $_SESSION['page_message'][] = ucfirst($this->mDbConnection->metaErrorMsg($error_number)) . ': ' . ucfirst($this->mDbConnection->ErrorMsg($error_number)); + $_SESSION['page_message'][] = 'Your database does not exist and could not be created. Either create the database yourself, choose an existing database or + use a database user with sufficient permissions to create a database.'; + return false; + } else { + $this->mDbConnection->selectDB(array_get($data, 'db_name')); + unset($_SESSION['page_heading']); + break; + } + + case '-26': + // Username passwords don't match for the hostname provided + $_SESSION['page_message'][] = ucfirst($this->mDbConnection->metaErrorMsg($error_number)) . ': ' . ucfirst($this->mDbConnection->ErrorMsg($error_number)); + $_SESSION['page_message'][] = "Apparently you haven't set up the right permissions for the database hostname provided."; + $_SESSION['page_message'][] = 'Double check the provided credentials or contact your System Administrator for further assistance.'; + return false; + break; + + default: + $_SESSION['page_message'][] = "Please verify your username/password/database details (error=$error_number)" . $this->mDbConnection->MetaErrorMsg($error_number); + return false; + break; + } + } + // Check that table prefix is OK, some DBs don't like it + $prefix = array_get($data, 'db_prefix'); + if (strlen($prefix) > 0 && is_numeric($prefix[0])) { + $_SESSION['page_heading'] = 'Database Processing'; + $_SESSION['page_message'][] = 'The table prefix may not start with a number.'; + return false; + } + + // Setting the Fetch mode of the database connection. + $this->mDbConnection->setFetchMode(ADODB_FETCH_BOTH); + + if( $data['db_type']=='mysqli') { + $dbinfo=$this->mDbConnection->serverInfo(); # provides 'description' and 'version' + if( version_compare($dbinfo['version'], '5.5.3') >=0 ){ + $this->mDbConnection->setCharSet('utf8mb4'); + }else{ + $this->mDbConnection->setCharSet('utf8'); + } + }else{ + $this->mDbConnection->setCharSet('utf8'); + } + + //creating the datadict object for further operations + $this->mDataDict = NewDataDictionary($this->mDbConnection); + + include_once dirname($this->mAdodbPath) . '/adodb-xmlschema03.inc.php'; + + $this->mXmlSchema = new adoSchema($this->mDbConnection); + + // Populate the database with the new tables and return the result (boolean) + if (!$this->populateDb($data)) + { + return false; + } + + return true; + } + + /** + * Function to populate the database with the sql constructs which is in an sql file + * @param array $data The actual database configuration values + * @return boolean + */ + + public function populateDb($data) + { + // Check available upgrade scripts, use the script of very latest version + $folders = glob_compat(BASEDIR . '/upgrade/[0-9]*'); + usort($folders, 'version_compare'); // start with lowest version + $folders = array_reverse($folders); // start with highest version + $sql_file = APPLICATION_PATH . '/setup/upgrade/' . reset($folders) . '/flyspray-install.xml'; + + $upgradeInfo = APPLICATION_PATH . '/setup/upgrade/' . reset($folders) . '/upgrade.info'; + $upgradeInfo = parse_ini_file($upgradeInfo, true); + + // Check if the install/upgrade file exists + if (!is_readable($sql_file)) { + + $_SESSION['page_message'][] = 'SQL file required for importing structure and data is missing.'; + return false; + } + + // Extract the variables to local namespace + extract($data); + if (!isset($db_prefix)) { + $db_prefix = ''; + } + + if(is_numeric($db_prefix)) { + $_SESSION['page_message'][] = 'database prefix cannot be numeric only'; + return false; + } + + // Set the prefix for database objects ( before parsing) + $this->mXmlSchema->setPrefix( (isset($db_prefix) ? $db_prefix : ''), false); + $this->mXmlSchema->parseSchema($sql_file); + + $this->mXmlSchema->executeSchema(); + + // Last but not least global prefs update + if (isset($upgradeInfo['fsprefs'])) { + $existing = $this->mDbConnection->getCol("SELECT pref_name FROM {$db_prefix}prefs"); + // Add what is missing + foreach ($upgradeInfo['fsprefs'] as $name => $value) { + if (!in_array($name, $existing)) { + $this->mDbConnection->execute("INSERT INTO {$db_prefix}prefs (pref_name, pref_value) VALUES (?, ?)", array($name, $value)); + } + } + // Delete what is too much + foreach ($existing as $name) { + if (!isset($upgradeInfo['fsprefs'][$name])) { + $this->mDbConnection->execute("DELETE FROM {$db_prefix}prefs WHERE pref_name = ?", array($name)); + } + } + } + + $this->mDbConnection->execute("UPDATE {$db_prefix}prefs SET pref_value = ? WHERE pref_name = 'fs_ver'", array($this->version)); + + if (($error_no = $this->mDbConnection->metaError())) + { + $_SESSION['page_heading'] = 'Database Processing'; + switch ($error_no) + { + case '-5': + // If there are tables with the same name + $_SESSION['page_message'][] = 'Table ' .$this->mDbConnection->metaErrorMsg($this->mDbConnection->metaError()); + $_SESSION['page_message'][] = 'There probably are tables in the database which have the same prefix you provided.'; + $_SESSION['page_message'][] = 'It is advised to change the prefix provided or you can drop the existing tables if you don\'t need them. Make a backup if you are not certain.'; + return false; + break; + + case '-1': + // We are using the unknown error code(-1) because ADOdb library may not have the error defined. + $_SESSION['page_message'][] = $this->mDbConnection->errorMsg(); + return false; + break; + + default: + $_SESSION['page_message'][] = $this->mDbConnection->errorMsg() . ': ' . $this->mDbConnection->errorNo(); + $_SESSION['page_message'][] = 'Unknown error, please notify Developer quoting the error number'; + return false; + break; + } + } + + return true; + } + + + + + /** + * Function to return status of boolean results in html format + * @param boolean $boolean The status of the result in True/False form + * @param string $type The type of html format to return + * @return string Depending on the type of format to return + */ + public static function returnStatus($boolean, $type = 'yes') + { + // Do a switch on the type of status + switch($type) + { + case 'yes': + return ($boolean) + ? '<span class="green">'.L('yes').'</span>' + : '<span class="red">'.L('no').'</span>'; + break; + + case 'available': + return ($boolean) + ? '<span class="green">'.L('available').'</span>' + : '<span class="red">'.L('missing').'</span>'; + break; + + case 'writeable': + return ($boolean) + ? '<span class="green">'.L('writeable').'</span>' + : '<span class="red">'.L('unwriteable').'</span>'; + break; + + case 'on': + return ($boolean) + ? '<span class="green">'.L('on').'</span>' + : '<span class="red">'.L('off').'</span>'; + break; + case 'support': + return ($boolean) + ? '<span class="green">'.L('supported').'</span>' + : '<span class="red">'.L('x').'</span>'; + break; + default: + return ($boolean) + ? '<span class="green">'.L('true').'</span>' + : '<span class="red">'.L('false').'</span>'; + break; + } + } + + /** + * To verify if a string was empty or not. + * + * Usually used to validate user input. If the user has inputted empty data + * or just blank spaces, we need to trim of such empty data and see if + * anything else is left after trimming. If there is data remaining, then + * the return value will be greater than 0 else it will be 0 (zero) which + * equates to a true/false scenario + * + * @param string $arg The data to be checked + * + * @return The result of the check. + */ + public function trimArgs($arg) + { + return strlen(trim($arg)); + } + + public function verifyVariableTypes($type, $value) + { + $message = ''; + switch($type) + { + case 'string': + return is_string($value); + break; + + case 'number': + return is_numeric($value); + break; + + case 'xmpp address': + case 'email address': + return filter_var($value, FILTER_VALIDATE_EMAIL); + break; + + case 'boolean': + return (bool) $value; + break; + + case 'password': + return (strlen($value) >= $this->mMinPasswordLength); + break; + + case 'folder': + return is_dir($value); + break; + + default: + return true; + break; + } + } + + /** + * Function to output the templates + * @param array $templates The collection of templates with their associated variables + * + */ + public function outputPage($templates = array()) + { + if (sizeof($templates) == 0) + { + trigger_error("Templates not configured properly", E_USER_ERROR); + } + + // Define a set of common variables which plugin to the structure template. + $page = new Tpl; + $body = ''; + + // Loop through the templates array to dynamically create objects and assign variables to them. + /// XXX: this is not a common way to use our template class, but I didn't want to rewrite + /// the whole setup only to change the templating engine + foreach($templates as $name => $module) + { + foreach ($module['vars'] as $var_name => $value) { + $page->assign($var_name, $value); + } + + if ($name == 'structure') { + $page->assign('body', $body); + $page->display('structure.tpl'); + } else { + $body .= $page->fetch($module['template']); + } + } + } +} + +//start the installer, it handles the rest inside the class +new Setup(); |