--- install.php.orig 2006-02-18 17:01:52.000000000 -0500 +++ install.php 2006-02-21 16:37:16.000000000 -0500 @@ -0,0 +1,6 @@ + --- includes/install.inc.orig 2006-02-16 09:18:41.000000000 -0500 +++ includes/install.inc 2006-02-21 16:34:08.000000000 -0500 @@ -4,15 +4,43 @@ define('SCHEMA_UNINSTALLED', -1); define('SCHEMA_INSTALLED', 0); +define('DRUPAL_MINIMUM_PHP', '4.3.3'); +define('DRUPAL_MINIMUM_MEMORY', '8M'); +define('DRUPAL_MINIMUM_MYSQL', '3.23'); // if using MySQL +define('DRUPAL_MINIMUM_PGSQL', '7.3'); // if using PostgreSQL +define('DRUPAL_MINIMUM_APACHE', '1.3'); // if using Apache -// The system module (Drupal core) is currently a special case -include_once './database/updates.inc'; +define('FILE_EXIST', 1); +define('FILE_READABLE', 2); +define('FILE_WRITABLE', 4); +define('FILE_WRITEABLE', 4); +define('FILE_EXECUTABLE', 8); +define('FILE_NOT_EXIST', 16); +define('FILE_NOT_READABLE', 32); +define('FILE_NOT_WRITABLE', 64); +define('FILE_NOT_WRITEABLE', 64); +define('FILE_NOT_EXECUTABLE', 128); -// Include install files for each module -foreach (module_list() as $module) { - $install_file = './'. drupal_get_path('module', $module) .'/'. $module .'.install'; - if (is_file($install_file)) { - include_once $install_file; +define('INSTALL_BOOTSTRAP_ENVIRONMENT', 0); +define('INSTALL_BOOTSTRAP_SETTINGS', 1); +define('INSTALL_BOOTSTRAP_DATABASE', 2); +define('INSTALL_PROFILES', 3); +define('INSTALL_COMPLETE', 4); + +if (!$install) { + // The system module (Drupal core) is currently a special case + include_once './database/updates.inc'; + + // Include install files for each module + foreach (module_list() as $module) { + $install_file = './'. drupal_get_path('module', $module) .'/'. $module .'.install'; + if (is_file($install_file)) { + include_once $install_file; + $func = $module .'_requirements_pre'; + if (function_exists($func)) { + drupal_verify_install($func()); + } + } } } @@ -79,3 +107,627 @@ function drupal_set_installed_schema_version($module, $version) { db_query("UPDATE {system} SET schema_version = %d WHERE name = '%s'", $version, $module); } + +/** + * The Drupal installation happens in a series of steps. We begin by verifying + * that the current environment meets our minimum requirements. We then go + * on to verify that settings.php is properly configured. From there we + * connect to the configured database and verify that it meets our minimum + * requirements. Finally we can allow the user to select an installation + * profile and complete the installation process. + * + * @param $phase + * The installation phase we should proceed to. + */ +function drupal_install($phase) { + static $phases = array(INSTALL_BOOTSTRAP_ENVIRONMENT, INSTALL_BOOTSTRAP_SETTINGS, INSTALL_BOOTSTRAP_DATABASE, INSTALL_PROFILES, INSTALL_COMPLETE); + + while (!is_null($current_phase = array_shift($phases))) { + _drupal_install($current_phase); + if ($phase == $current_phase) { + return; + } + } +} + +/** + * The individual Drupal installation phase definitions. + * + * @param $phase + * The current phase. + */ +function _drupal_install($phase) { + switch ($phase) { + case INSTALL_BOOTSTRAP_ENVIRONMENT: + // initial install environment tests + require_once './includes/install.inc'; + require_once './includes/bootstrap.inc'; + require_once './modules/system.install'; + drupal_verify_install(system_requirements_bootstrap()); + if (_install_errors()) { + drupal_maintenance_theme(); + drupal_set_title('Unsupported install environment'); + print theme('install_page', '
The errors below must be resolved before you can install Drupal. Find additional information about Drupal\'s minimum system requirements here.
'); + exit; + } + break; + + case INSTALL_BOOTSTRAP_SETTINGS: + // configure/verify settings.php + drupal_verify_settings(); + if (_install_errors()) { + drupal_maintenance_theme(); + drupal_set_title('Site configuration'); + print theme('install_page', 'Please fill out the following information to configure your Drupal site.
'. drupal_install_settings_form()); + exit; + } + + break; + + case INSTALL_BOOTSTRAP_DATABASE: + // establish connection to the database + break; + + case INSTALL_PROFILES: + // select from install profiles and/or specific modules/themes + // (gets instructions from *.install and *.profile files) + break; + + case INSTALL_COMPLETE: + // installation finished, display friendly message with instructions of + // how to access/administer the site. + drupal_maintenance_theme(); + drupal_set_title('Drupal installation complete'); + print theme('install_page', 'Drupal has been succesfully installed.'); + break; + } +} + +/** + * Check if there are any errors at this point in the installation process. + * + * @return + * An array of errors, if any. + */ +function _install_errors() { + $messages = drupal_set_message(); + return $messages['error']; +} + +/** + * Auto detect the base_url with PHP predefined variables. + * + * @param $file + * The name of the file calling this function so we can strip it out of + * the URI when generating the base_url. + * + * @return + * The auto-detected $base_url that should be configured in settings.php + */ +function drupal_detect_baseurl($file = 'install.php') { + $proto = $_SERVER['HTTPS'] ? 'https://' : 'http://'; + $host = $_SERVER['SERVER_NAME']; + $port = ($_SERVER['SERVER_PORT'] == 80 ? '' : ':'. $_SERVER['SERVER_PORT']); + $dir = preg_replace("$/$file$", '', $_SERVER['REQUEST_URI']); + return "$proto$host$port$dir"; +} + +/** + * Detect all databases supported by Drupal that are compiled into the current + * PHP installation. + * + * @param $type + * Optionally specify a type if wish to check if specific database type is + * available. + * + * @return + * An array of database types compiled into PHP, or TRUE/FALSE if a database + * type was specified when calling the function. + */ +function drupal_detect_database_types($type = NULL) { + $databases = array(); + + if (function_exists(mysql_connect)) { + if ($type == 'mysql') { + return TRUE; + } + $databases[] = 'mysql'; + } + if (function_exists(mysqli_connect)) { + if ($type == 'mysqli') { + return TRUE; + } + $databases[] = 'mysqli'; + } + if (function_exists(pg_connect)) { + if ($type == 'pgsql') { + return TRUE; + } + $databases[] = 'pgsql'; + } + + return $databases; +} + +/** + * + */ +function drupal_rewrite_settings($settings = array()) { + $settings_file = './'. conf_init() .'/settings.php'; + + // TODO: form validation? + // TODO: preserve original settings.php? + + $buffer = NULL; + if($fp = @fopen($settings_file, 'r+')) { + while (!feof($fp)) { + $line = fgets($fp); + if (substr($line, 0, 1) == '$') { + preg_match('/\$(.*) /U', $line, $variable); + switch ($variable[1]) { + case 'db_url': + $buffer .= '$db_url = \''. $settings['db_type'] .'://'. $settings['db_user'] .($settings['db_pass'] ? ':'. $settings['db_pass'] : '') .'@'. ($settings['db_host'] ? $settings['db_host'] : 'localhost') .'/'. $settings['db_path'] ."';\n"; + break; + case 'db_prefix': + $buffer .= "\$db_prefix = '". $settings['db_prefix'] ."';\n"; + break; + case 'base_url': + if (!empty($settings['base_url'])) { + $base_url = preg_replace("'/$'", '', $settings['base_url']); + $buffer .= "\$base_url = '". $base_url ."'; // NO trailing slash!\n"; + } + else { + $buffer .= $line; + } + break; + } + } + else { + $buffer .= $line; + } + } + fclose($fp); + $fp = fopen($settings_file, 'w'); + if ($fp && fwrite($fp, $buffer) === FALSE) { + drupal_set_message('Failed to modify settings.php, please verify the file permissions.', 'error'); + } + } + else { + drupal_set_message('Failed to open settings.php, please verify the file permissions.', 'error'); + } + +} + +/** + * + */ +function drupal_verify_settings() { + global $db_url, $db_prefix, $base_url, $db_type; + $settings_file = './'. conf_init() .'/settings.php'; + + if (isset($_POST['save'])) { + drupal_rewrite_settings($_POST); + } + + require_once $settings_file; + + // verify base_url + $detected_base_url = drupal_detect_baseurl(); + if ($detected_base_url != $base_url) { + } + + // verify database settings + $db_type = substr($db_url, 0, strpos($db_url, '://')); + $databases = drupal_detect_database_types(); + if (!in_array($db_type, $databases)) { + drupal_set_message("In your $settings_file file you have configured Drupal to use a $db_type database server, however your PHP installation currently does not support this database type.", 'error'); + } + $url = parse_url($db_url); + $db_user = urldecode($url['user']); + $db_pass = urldecode($url['pass']); + if ($db_user == 'username' && $db_pass == 'password') { + drupal_set_message('Your settings.php file is configured to use the default username and password. This is not allowed for security reasons.', 'error'); + } + +} + +function drupal_install_settings_form() { + global $base_url, $db_url, $db_type; + if (!isset($base_url)) { + $base_url = drupal_detect_baseurl(); + } + $url = parse_url($db_url); + $db_user = urldecode($url['user']); + $db_pass = urldecode($url['pass']); + $db_host = urldecode($url['host']); + $db_path = preg_replace("$^/$", '', urldecode($url['path'])); + + $form = '
'; + + $form .= ''; + + $form .= ''; + + return ''; +} + +/** + * Call the appropriate installation function. If the function is not + * available, attempt to load the appropriate include file then try again. + * + * @param $settings + * An array of settings options. + */ +function drupal_verify_install($settings = array()) { + foreach ($settings as $type => $data) { + $function = "drupal_verify_install_$type"; + if (function_exists("$function")) { + $function($data); + } + else { + $include = './includes/install.'. $type .'.inc'; + if (file_exists($include)) { + require_once($include); + if (function_exists("$function")) { + $function($data); + } + else { + drupal_set_message(strtr('Installation error: function "%function" does not exist.', array('%function' => $function)), 'error'); + } + } + else { + drupal_set_message(strtr('Installation error: unable to find file "%file", function "%function" does not exist.', array('%file' => $include, '%function' => $function)), 'error'); + } + } + } +} + +/** + * Some php.ini options are written in shorthand (ie 1K instead of 1024 bytes). + * This function converts php.ini shorthand into bytes. + * + * @param $shorthand + * A memory string in php.ini shorthand notation (ie 1K, 8M, 10G) + * + * @return + * The shorthand value converted to bytes. + */ +function drupal_shorthand_to_bytes($shorthand) { + $result = trim($shorthand); + $modifier = strtolower($result{strlen($result)-1}); + switch ($modifier) { + case 'g': + $result *= 1024; + case 'm': + $result *= 1024; + case 'k': + $result *= 1024; + } + return $result; +} + +/** + * Verify that the installed version of PHP meets our minimum requirements. + * + * @param $version + * An array with some or all of the following directives: + * 'required_version' = the required version of PHP + * 'message' = message to display if version is invalid + * ("%required_version" and "%installed_version" will be + * automatically replaced if included in the message + * text.) + * 'message_type' = message type (ie, 'error', 'status') + */ +function drupal_verify_version_php($version = array()) { + if (!function_exists(version_compare) || + version_compare(phpversion(), $version['required_version'], '<')) { + // version_compare() was added in PHP 4.1. + $message_type = $version['message_type'] ? $version['message_type'] : 'status'; + drupal_set_message(strtr($version['message'], array('%required_version' => $version['required_version'], '%installed_version' => phpversion())), $message_type); + } +} + +/** + * Verify the value of the specified configuration option as found in php.ini. + * + * @param $setting + * An array with some or all of the following directives: + * 'required_value' = the required value of the configuration option + * 'shorthand' = TRUE/FALSE whether the option is in shorthand notation + * 'operator' = optional comparison operator + * 'message' = message to display if comparison fails + * ("%required_value" and "%current_value" will be + * automatically replaced if included in the message + * text.) + * 'message_type' = message type (ie, 'error', 'status') + */ +function drupal_verify_install_php($settings = array()) { + foreach ($settings as $setting => $data) { + // handle special case (not from php.ini) + if ($setting == 'version') { + drupal_verify_version_php($data); + } + else { + if ($data['shorthand']) { + $required = drupal_shorthand_to_bytes($data['required_value']); + $current = drupal_shorthand_to_bytes(ini_get($setting)); + } + else { + $required = $data['required_value']; + $current = ini_get($setting); + } + + switch ($data['operator']) { + case '<': + $valid = ($current < $required); + break; + case '<=': + $valid = ($current <= $required); + break; + case '==': + default: + $valid = ($current == $required); + break; + case '>=': + $valid = ($current >= $required); + break; + case '>': + $valid = ($current > $required); + break; + case '!=': + case '<>': + $valid = ($current != $required); + break; + case '===': + $valid = ($current === $required); + break; + case '!==': + $valid = ($current !== $required); + break; + } + + if (!$valid && isset($data['message'])) { + $message_type = $data['message_type'] ? $data['message_type'] : 'status'; + drupal_set_message(strtr($data['message'], array('%current_value' => ini_get($setting), '%required_value' => $data['required_value'])), $message_type); + } + } + } +} + +/** + * Verify the state of the specified file. + * + * @param $setting + * An array with some or all of the following directives: + * 'required' = whether or not this file is required + * 'mask' = permissions information about the file + * 'type' = file type (file, directory, link) + * 'message' = message to display if something is wrong with file + * ("%required_value" and "%current_value" will be + * automatically replaced if included in the message + * text.) + * 'message_type' = message type (ie, 'error', 'status') + */ +function drupal_verify_install_file($settings = array()) { + foreach ($settings as $file => $data) { + // handle special case files (ie settings.php) + switch ($file) { + case '%settings.php': + $file = './'. conf_init() .'/settings.php'; + break; + } + + $err = FALSE; + $message_type = ($data['required'] ? 'error' : 'status'); + // check for files that shouldn't be there + if (isset($data['mask']) && $data['mask'] & FILE_NOT_EXIST) { + if (file_exists($file)) { + drupal_set_message(strtr('%type "%file" exists but should not.', array('%type' => $data['type'], '%file' => $file)), $message_type); + $err = TRUE; + } + } + else { + // verfiy that the file is the type of file it is supposed to be + if (isset($data['type']) && file_exists($file)) { + switch ($data['type']) { + case 'file': + if (!is_file($file)) { + $err = TRUE; + } + break; + case 'directory': + if (!is_dir($file)) { + $err = TRUE; + } + break; + case 'link': + if (!is_link($file)) { + $err = TRUE; + } + break; + } + if ($err) { + drupal_set_message(strtr('"%file" exists but is not a %type.', array('%type' => $data['type'], '%file' => $file)), $message_type); + } + } + + // verify file permissions + if (isset($data['mask'])) { + $filetype = ucfirst($data['type']); + $masks = array(FILE_EXIST, FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_EXIST, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE); + foreach ($masks as $mask) { + if ($data['mask'] & $mask && !$err) { + switch ($mask) { + case FILE_EXIST: + if (!file_exists($file)) { + drupal_set_message(strtr('%type "%file" does not exist.', array('%type' => $filetype, '%file' => $file)), $message_type); + $err = TRUE; + } + break; + case FILE_READABLE: + if (!is_readable($file) && !drupal_install_fix_file($file, $data['mask'])) { + drupal_set_message(strtr('%type "%file" is not readable.', array('%type' => $filetype, '%file' => $file)), $message_type); + $err = TRUE; + } + break; + case FILE_WRITABLE: + if (!is_writable($file) && !drupal_install_fix_file($file, $data['mask'])) { + drupal_set_message(strtr('%type "%file" is not writeable.', array('%type' => $filetype, '%file' => $file)), $message_type); + $err = TRUE; + } + break; + case FILE_EXECUTABLE: + if (!is_executable($file) && !drupal_install_fix_file($file, $data['mask'])) { + drupal_set_message(strtr('%type "%file" is not executable.', array('%type' => $filetype, '%file' => $file)), $message_type); + $err = TRUE; + } + break; + case FILE_NOT_READABLE: + if (is_readable($file) && !drupal_install_fix_file($file, $data['mask'])) { + drupal_set_message(strtr('%type "%file" is readable but should not be.', array('%type' => $filetype, '%file' => $file)), $message_type); + $err = TRUE; + } + break; + case FILE_NOT_WRITABLE: + if (is_writeable($file) && !drupal_install_fix_file($file, $data['mask'])) { + drupal_set_message(strtr('%type "%file" is writeable but should not be.', array('%type' => $filetype, '%file' => $file)), $message_type); + $err = TRUE; + } + break; + case FILE_NOT_EXECUTABLE: + if (is_executable($file) && !drupal_install_fix_file($file, $data['mask'])) { + drupal_set_message(strtr('%type "%file" is executable but should not be.', array('%type' => $filetype, '%file' => $file)), $message_type); + $err = TRUE; + } + break; + } + } + } + } + } + if ($err && isset($data['message'])) { + $message_type = $data['message_type'] ? $data['message_type'] : 'status'; + drupal_set_message($data['message'], $message_type); + } + } +} + +/** + * Attempt to fix file permissions. + * + * @param $file + * The name of the file with permissions to fix. + * @param $mask + * The desired permissions for the file. + * + * @return + * TRUE/FALSE whether or not we were able to fix the file's permissions. + */ +function drupal_install_fix_file($file, $mask) { + $mod = substr(sprintf('%o', fileperms($file)), -4); + $prefix = substr($mod, 0, 1); + $mod = substr($mod, 1 ,4); + $masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE); + foreach ($masks as $m) { + if ($mask & $m) { + switch ($m) { + case FILE_READABLE: + if (!is_readable($file)) { + $mod += 444; + } + break; + case FILE_WRITABLE: + if (!is_writable($file)) { + $mod += 222; + } + break; + case FILE_EXECUTABLE: + if (!is_executable($file)) { + $mod += 111; + } + break; + case FILE_NOT_READABLE: + if (is_readable($file)) { + $mod -= 444; + } + break; + case FILE_NOT_WRITABLE: + if (is_writable($file)) { + $mod -= 222; + } + break; + case FILE_NOT_EXECUTABLE: + if (is_executable($file)) { + $mod -= 111; + } + break; + } + } + } + + if (@chmod($file, intval("$prefix$mod", 8))) { + drupal_set_message("Automatically fixed the permissions of file '$file'.", 'status'); + return TRUE; + } + else { + drupal_set_message("Failed to automatically fix permissions of file '$file', insufficient privileges.", 'status'); + return FALSE; + } +} + +/** + * Verify that a function exists. + * + * @param $setting + * An array with some or all of the following directives: + * 'message' = message to display if the function does not exist. + * 'message_type' = message type (ie, 'error', 'status') + */ +function drupal_verify_install_function($settings = array()) { + foreach ($settings as $function => $data) { + if (!function_exists($function)) { + $message_type = $data['message_type'] ? $data['message_type'] : 'status'; + drupal_set_message($data['message'], $message_type); + } + } +} + +?> --- includes/theme.inc.orig 2006-02-19 20:37:04.000000000 -0500 +++ includes/theme.inc 2006-02-20 15:12:43.000000000 -0500 @@ -450,17 +450,62 @@ return $output; } +function theme_install_page($content) { + drupal_set_header('Content-Type: text/html; charset=utf-8'); + theme('add_style', 'misc/maintenance.css'); + drupal_set_html_head(''); + $output = "\n"; + $output .= ''; + $output .= ''; + $output .= '