Added support for alternate port numbers on database servers. Also in install-cli, merged in new sysreqs functionality.
authorDan
Sat, 14 Mar 2009 14:06:02 -0400
changeset 857 f3a5a276208c
parent 856 0b7ff06aad13
child 858 bf60e27806e4
Added support for alternate port numbers on database servers. Also in install-cli, merged in new sysreqs functionality.
includes/dbal.php
includes/functions.php
includes/sessions.php
install/includes/cli-core.php
install/includes/libenanoinstall.php
install/includes/payload.php
install/includes/stages/database_mysql.php
install/includes/stages/database_post.php
install/includes/stages/database_postgresql.php
install/includes/stages/install.php
install/includes/stages/login.php
install/includes/stages/sysreqs.php
install/schemas/postgresql_stage2.sql
language/english/install.json
--- a/includes/dbal.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/includes/dbal.php	Sat Mar 14 14:06:02 2009 -0400
@@ -131,7 +131,7 @@
     return $internal_text;
   }
   
-  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false)
+  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
   {
     if ( !defined('ENANO_SQL_CONSTANTS') )
     {
@@ -153,9 +153,11 @@
       {
         @include(ENANO_ROOT.'/config.php');
       }
-        
+      
       if ( isset($crypto_key) )
         unset($crypto_key); // Get this sucker out of memory fast
+      if ( empty($dbport) )
+        $dbport = 3306;
       
       if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
       {
@@ -188,7 +190,15 @@
       }
     }
     
-    $this->_conn = @mysql_connect($dbhost, $dbuser, $dbpasswd);
+    if ( !$dbport )
+      $dbport = 3306;
+    
+    if ( $dbhost && !empty($dbport) && $dbport != 3306 )
+      $dbhost = '127.0.0.1';
+    
+    $host_line = ( preg_match('/^:/', $dbhost) ) ? $dbhost : "{$dbhost}:{$dbport}";
+    
+    $this->_conn = @mysql_connect($host_line, $dbuser, $dbpasswd);
     unset($dbuser);
     unset($dbpasswd); // Security
     
@@ -839,7 +849,7 @@
     return $internal_text;
   }
   
-  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false)
+  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
   {
     if ( !defined('ENANO_SQL_CONSTANTS') )
     {
@@ -864,6 +874,8 @@
         
       if ( isset($crypto_key) )
         unset($crypto_key); // Get this sucker out of memory fast
+      if ( empty($dbport) )
+        $dbport = 5432;
       
       if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
       {
@@ -890,7 +902,11 @@
         exit;
       }
     }
-    $this->_conn = @pg_connect("host=$dbhost port=5432 dbname=$dbname user=$dbuser password=$dbpasswd");
+    
+    if ( empty($dbport) )
+      $dbport = 5432;
+    
+    $this->_conn = @pg_connect("host=$dbhost port=$dbport dbname=$dbname user=$dbuser password=$dbpasswd");
     unset($dbuser);
     unset($dbpasswd); // Security
     
@@ -951,7 +967,7 @@
     }
     
     $time_start = microtime_float();
-    $r = pg_query($q);
+    $r = @pg_query($q);
     $this->query_times[$q] = microtime_float() - $time_start;
     $this->latest_result = $r;
     return $r;
--- a/includes/functions.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/includes/functions.php	Sat Mar 14 14:06:02 2009 -0400
@@ -5033,4 +5033,58 @@
   return false;
 }
 
-?>
+/**
+ * Properly test a file or directory for writability. Used in various places around installer.
+ * @param string File or directory to test
+ * @return bool
+ */
+
+function write_test($filename)
+{
+  // We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
+  // true on Windows/IIS servers. Don't ask me why.
+  
+  $file = ENANO_ROOT . '/' . $filename;
+  if ( is_dir($file) )
+  {
+    $file = rtrim($file, '/') . '/' . 'enanoinstalltest.txt';
+    if ( file_exists($file) )
+    {
+      $fp = @fopen($file, 'a+');
+      if ( !$fp )
+        return false;
+      fclose($fp);
+      unlink($file);
+      return true;
+    }
+    else
+    {
+      $fp = @fopen($file, 'w');
+      if ( !$fp )
+        return false;
+      fclose($fp);
+      unlink($file);
+      return true;
+    }
+  }
+  else
+  {
+    if ( file_exists($file) )
+    {
+      $fp = @fopen($file, 'a+');
+      if ( !$fp )
+        return false;
+      fclose($fp);
+      return true;
+    }
+    else
+    {
+      $fp = @fopen($file, 'w');
+      if ( !$fp )
+        return false;
+      fclose($fp);
+      return true;
+    }
+  }
+}
+
--- a/includes/sessions.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/includes/sessions.php	Sat Mar 14 14:06:02 2009 -0400
@@ -1143,7 +1143,7 @@
                       . "  u.style,u.signature, u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_title, k.salt, k.source_ip,\n"
                       . "  k.time, k.auth_level, k.key_type, COUNT(p.message_id) AS num_pms, u.user_timezone, u.user_dst, x.*";
     
-    $columns_groupby = "u.user_id, u.username, u.password, u.email, u.real_name, u.user_level, u.theme, u.style, u.signature,\n"
+    $columns_groupby = "u.user_id, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme, u.style, u.signature,\n"
                       . "           u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_timezone, u.user_title, u.user_dst,\n"
                       . "           k.salt, k.source_ip, k.time, k.auth_level, k.key_type, x.user_id, x.user_aim, x.user_yahoo, x.user_msn,\n"
                       . "           x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public,\n"
--- a/install/includes/cli-core.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/cli-core.php	Sat Mar 14 14:06:02 2009 -0400
@@ -39,7 +39,7 @@
 }
 
 // parse command line args
-foreach ( array('silent', 'driver', 'dbhost', 'dbuser', 'dbpasswd', 'dbname', 'db_prefix', 'user', 'pass', 'email', 'sitename', 'sitedesc', 'copyright', 'urlscheme', 'lang_id', 'scriptpath') as $var )
+foreach ( array('silent', 'driver', 'dbhost', 'dbport', 'dbuser', 'dbpasswd', 'dbname', 'db_prefix', 'user', 'pass', 'email', 'sitename', 'sitedesc', 'copyright', 'urlscheme', 'lang_id', 'scriptpath') as $var )
 {
   if ( !isset($$var) )
   {
@@ -62,6 +62,10 @@
     case '-h':
       $dbhost = @$argv[++$i];
       break;
+    case '--db-port':
+    case '-o':
+      $dbport = @$argv[++$i];
+      break;
     case '--db-user':
     case '-u':
       $dbuser = @$argv[++$i];
@@ -126,6 +130,7 @@
   -q                Quiet mode (minimal output)
   -b, --db-driver   Database driver (mysql or postgresql)
   -h, --db-host     Hostname of database server
+  -o, --db-port     TCP port on which to connect to database server
   -u, --db-user     Username to use on database server
   -p, --db-pass     Password to use on database server
   -d, --db-name     Name of database
@@ -218,6 +223,7 @@
 $defaults = array(
   'driver'  => 'mysql',
   'dbhost'    => 'localhost',
+  'dbport'    => 3306,
   'dbuser'    => false,
   'dbpasswd'  => false,
   'dbname'    => false,
@@ -235,6 +241,7 @@
 $terms = array(
   'driver'  => $lang->get('cli_prompt_driver'),
   'dbhost'    => $lang->get('cli_prompt_dbhost'),
+  'dbport'    => $lang->get('cli_prompt_dbport'),
   'dbuser'    => $lang->get('cli_prompt_dbuser'),
   'dbpasswd'  => $lang->get('cli_prompt_dbpasswd'),
   'dbname'    => $lang->get('cli_prompt_dbname'),
@@ -249,7 +256,9 @@
   'scriptpath'=> $lang->get('cli_prompt_scriptpath')
 );
 
-foreach ( array('driver', 'dbhost', 'dbuser', 'dbpasswd', 'dbname', 'db_prefix', 'scriptpath', 'user', 'pass', 'email', 'sitename', 'sitedesc', 'copyright', 'urlscheme') as $var )
+$defaults['dbport'] = ( strtolower($driver) == 'postgresql' ) ? 5432 : 3306;
+
+foreach ( array('driver', 'dbhost', 'dbport', 'dbuser', 'dbpasswd', 'dbname', 'db_prefix', 'scriptpath', 'user', 'pass', 'email', 'sitename', 'sitedesc', 'copyright', 'urlscheme') as $var )
 {
   if ( empty($$var) )
   {
@@ -258,6 +267,10 @@
       default:
         $$var = cli_prompt($terms[$var], $defaults[$var]);
         break;
+      case 'driver':
+        $$var = cli_prompt($terms[$var], $defaults[$var]);
+        $defaults['dbport'] = ( strtolower($driver) == 'postgresql' ) ? 5432 : 3306;
+        break;
       case 'pass':
       case 'dbpasswd':
         if ( @file_exists('/bin/stty') && @is_executable('/bin/stty') )
@@ -295,6 +308,14 @@
           $$var = cli_prompt($terms[$var], $defaults[$var]);
         }
         break;
+      case 'dbport':
+        $$var = cli_prompt($terms[$var], strval($defaults[$var]));
+        while ( !preg_match('/^[0-9]*$/', $$var) )
+        {
+          $$var = cli_prompt($terms[$var], $defaults[$var]);
+        }
+        $$var = intval($$var);
+        break;
     }
   }
 }
@@ -310,7 +331,7 @@
 if ( !$silent )
   echo parse_shellcolor_string($lang->get('cli_msg_testing_db'));
 
-$result = $dbal->connect(true, $dbhost, $dbuser, $dbpasswd, $dbname);
+$result = $dbal->connect(true, $dbhost, $dbuser, $dbpasswd, $dbname, $dbport);
 if ( !$result )
 {
   if ( !$silent )
@@ -333,21 +354,157 @@
   echo parse_shellcolor_string($lang->get('cli_stage_sysreqs'));
 }
 
-$test_failed = false;
+$failed = false;
+$warnings = array();
+
+// Test: PHP
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_php') . ': ';
+if ( version_compare(PHP_VERSION, '5.2.0', '>=') )
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+}
+else if ( version_compare(PHP_VERSION, '5.0.0', '>=') )
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_vwarn')) . "\n";
+  $warnings[] = $lang->get('sysreqs_req_help_php', array('php_version' => PHP_VERSION));
+}
+else
+{
+  $failed = true;
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+}
+
+// Test: MySQL
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_mysql') . ': ';
+$req_mysql = function_exists('mysql_connect');
+if ( $req_mysql )
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+  $have_dbms = true;
+}
+else
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+}
+
+// Test: PostgreSQL
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_postgresql') . ': ';
+$req_pgsql = function_exists('pg_connect');
+if ( $req_pgsql )
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+  $have_dbms = true;
+}
+else
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+}
+
+if ( !$have_dbms )
+  $failed = true;
+
+// Test: Safe Mode
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_safemode') . ': ';
+$req_safemode = !intval(@ini_get('safe_mode'));
+if ( !$req_safemode )
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+  $warnings[] = $lang->get('sysreqs_req_help_safemode');
+  $failed = true;
+}
+else
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+}
 
-run_test('return version_compare(\'5.2.0\', PHP_VERSION, \'<=\');', $lang->get('sysreqs_req_php5'), $lang->get('sysreqs_req_desc_php5'), true);
-run_test('return function_exists(\'mysql_connect\');', $lang->get('sysreqs_req_mysql'), $lang->get('sysreqs_req_desc_mysql'), true);
-run_test('return function_exists(\'pg_connect\');', $lang->get('sysreqs_req_postgres'), $lang->get('sysreqs_req_desc_postgres'), true);
-run_test('return @ini_get(\'file_uploads\');', $lang->get('sysreqs_req_uploads'), $lang->get('sysreqs_req_desc_uploads') );
-run_test('return config_write_test();', $lang->get('sysreqs_req_config'), $lang->get('sysreqs_req_desc_config') );
-run_test('return file_exists(\'/usr/bin/convert\');', $lang->get('sysreqs_req_magick'), $lang->get('sysreqs_req_desc_magick'), true);
-run_test('return is_writable(ENANO_ROOT.\'/cache/\');', $lang->get('sysreqs_req_cachewriteable'), $lang->get('sysreqs_req_desc_cachewriteable'), true);
-run_test('return is_writable(ENANO_ROOT.\'/files/\');', $lang->get('sysreqs_req_fileswriteable'), $lang->get('sysreqs_req_desc_fileswriteable'), true);
+// Test: File uploads
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_uploads') . ': ';
+$req_uploads = intval(@ini_get('file_uploads'));
+if ( $req_uploads )
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+}
+else
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_warn')) . "\n";
+}
+
+// Test: ctype validation
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_ctype') . ': ';
+$req_ctype = function_exists('ctype_digit');
+if ( $req_ctype )
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+}
+else
+{
+  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+  $failed = true;
+}
+
+// Write tests
+$req_config_w = write_test('config.new.php');
+$req_htaccess_w = write_test('.htaccess.new');
+$req_files_w = write_test('files');
+$req_cache_w = write_test('cache');
+
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_config_writable') . ': ' . parse_shellcolor_string($lang->get($req_config_w ? 'cli_test_pass' : 'cli_test_fail')) . "\n";
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_htaccess_writable') . ': ' . parse_shellcolor_string($lang->get($req_htaccess_w ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_files_writable') . ': ' . parse_shellcolor_string($lang->get($req_files_w ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_cache_writable') . ': ' . parse_shellcolor_string($lang->get($req_cache_w ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+
+if ( !$req_config_w || !$req_htaccess_w || !$req_files_w || !$req_cache_w )
+  $warnings[] = $lang->get('sysreqs_req_help_writable');
+
+if ( !$req_config_w )
+  $failed = true;
+      
+// Extension test: GD
+$req_gd = function_exists('imagecreatefrompng') && function_exists('getimagesize') && function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled');
+if ( !$req_gd )
+  $warnings[] = $lang->get('sysreqs_req_help_gd2');
+
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_gd2') . ': ' . parse_shellcolor_string($lang->get($req_gd ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+
+// FS test: ImageMagick
+$req_imagick = which('convert');
+if ( !$req_imagick )
+  $warnings[] = $lang->get('sysreqs_req_help_imagemagick');
+
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_imagemagick') . ': ' . parse_shellcolor_string($lang->get($req_imagick ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+
+// Extension test: GMP
+$req_gmp = function_exists('gmp_init');
+if ( !$req_gmp )
+  $warnings[] = $lang->get('sysreqs_req_help_gmp');
+
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_gmp') . ': ' . parse_shellcolor_string($lang->get($req_gmp ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+
+// Extension test: Big_Int
+$req_bigint = function_exists('bi_from_str');
+if ( !$req_bigint && !$req_gmp )
+  $warnings[] = $lang->get('sysreqs_req_help_bigint');
+
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_bigint') . ': ' . parse_shellcolor_string($lang->get($req_bigint ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+
+// Extension test: BCMath
+$req_bcmath = function_exists('bcadd');
+if ( !$req_bcmath && !$req_bigint && !$req_gmp )
+  $warnings[] = $lang->get('sysreqs_req_help_bcmath');
+
+if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_bcmath') . ': ' . parse_shellcolor_string($lang->get($req_bcmath ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
+
+if ( !empty($warnings) && !$silent )
+{
+  echo parse_shellcolor_string($lang->get('cli_msg_test_warnings')) . "\n";
+  echo "  " . implode("\n  ", $warnings) . "\n";
+}
+
 if ( !function_exists('mysql_connect') && !function_exists('pg_connect') )
 {
   installer_fail($lang->get('cli_err_no_drivers'));
 }
-if ( $test_failed )
+if ( $failed )
 {
   installer_fail($lang->get('cli_err_sysreqs_fail'));
 }
--- a/install/includes/libenanoinstall.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/libenanoinstall.php	Sat Mar 14 14:06:02 2009 -0400
@@ -247,4 +247,3 @@
   }
 }
 
-?>
--- a/install/includes/payload.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/payload.php	Sat Mar 14 14:06:02 2009 -0400
@@ -168,12 +168,13 @@
 
 function stg_write_config()
 {
-  global $dbhost, $dbuser, $dbpasswd, $dbname, $dbdriver;
+  global $dbhost, $dbuser, $dbpasswd, $dbname, $dbdriver, $dbport;
   $db_data = array(
       'host' => str_replace("'", "\\'", $dbhost),
       'user' => str_replace("'", "\\'", $dbuser),
       'pass' => str_replace("'", "\\'", $dbpasswd),
       'name' => str_replace("'", "\\'", $dbname),
+      'port' => intval($dbport),
       'tp' => table_prefix,
       'drv' => $dbdriver
     );
@@ -219,6 +220,9 @@
 // Hostname of your database server, probably localhost
 \$dbhost = '{$db_data['host']}';
 
+// Port number for database server, probably 3306 (MySQL) or 5432 (PostgreSQL)
+\$dbport = '{$db_data['port']}';
+
 // Username used to connect to the database
 \$dbuser = '{$db_data['user']}';
 // Database password
--- a/install/includes/stages/database_mysql.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/stages/database_mysql.php	Sat Mar 14 14:06:02 2009 -0400
@@ -21,7 +21,7 @@
 {
   $allow_go = true;
   // Do we have everything? If so, continue with installation.
-  foreach ( array('db_host', 'db_name', 'db_user', 'db_pass') as $field )
+  foreach ( array('db_host', 'db_port', 'db_name', 'db_user', 'db_pass') as $field )
   {
     if ( empty($_POST[$field]) )
     {
@@ -73,8 +73,20 @@
       )));
   }
   
+  if ( preg_match('/^:/', $info['db_host']) && !@file_exists(substr($info['db_host'], 1)) )
+  {
+    $return['host_good'] = false;
+    echo enano_json_encode($return);
+    exit();
+  }
+  
+  if ( $info['db_host'] == 'localhost' && !empty($info['db_port']) && $info['db_port'] != 3306 )
+    $info['db_host'] = '127.0.0.1';
+  
+  $dbhost = ( preg_match('/^:/', $info['db_host']) ) ? $info['db_host'] : "{$info['db_host']}:{$info['db_port']}";
+  
   // Try to connect as the normal user
-  $test = @mysql_connect($info['db_host'], $info['db_user'], $info['db_pass']);
+  $test = @mysql_connect($dbhost, $info['db_user'], $info['db_pass']);
   if ( !$test )
   {
     $return['creating_user'] = true;
@@ -89,7 +101,7 @@
       // Log in with root rights and if that works, tell 'em we'll reset the password or create
       // the account if it doesn't exist already. This is done with GRANT ALL PRIVILEGES ON enano_db.*
       // etc etc, a little hackish but known to work with MySQL >= 4.1.
-      $test_root = @mysql_connect($info['db_host'], $info['db_root_user'], $info['db_root_pass']);
+      $test_root = @mysql_connect($dbhost, $info['db_root_user'], $info['db_root_pass']);
       if ( $test_root )
       {
         // We logged in with root rights, assume that we have appropriate permissions.
@@ -152,7 +164,7 @@
       if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
       {
         // Log in with root rights and if that works, tell 'em we'll create the database.
-        $test_root = @mysql_connect($info['db_host'], $info['db_root_user'], $info['db_root_pass']);
+        $test_root = @mysql_connect($dbhost, $info['db_root_user'], $info['db_root_pass']);
         if ( $test_root )
         {
           // We logged in with root rights, assume that we have appropriate permissions.
@@ -240,6 +252,7 @@
     // List of fields
     var fields = {
       db_host: frm.db_host,
+      db_port: frm.db_port,
       db_name: frm.db_name,
       db_user: frm.db_user,
       db_pass: frm.db_pass,
@@ -251,11 +264,18 @@
     // Main validation
     if ( field == fields.db_host || !field )
     {
-      var matches = fields.db_host.value.match(/^([a-z0-9_-]+)((\.([a-z0-9_-]+))*)?$/);
+      var matches = fields.db_host.value.match(/^(([a-z0-9_-]+)((\.([a-z0-9_-]+))*)|:[A-z0-9_:\.\/-]+)$/);
       document.getElementById('s_db_host').src = ( matches ) ? img_neu : img_bad;
       if ( !matches )
         passed = false;
     }
+    if ( field == fields.db_port || !field )
+    {
+      var matches = fields.db_port.value.match(/^[0-9]+$/);
+      document.getElementById('s_db_port').src = ( matches ) ? img_neu : img_bad;
+      if ( !matches )
+        passed = false;
+    }
     if ( field == fields.db_name || !field )
     {
       var matches = fields.db_name.value.match(/^[A-z0-9_-]+$/);
@@ -302,6 +322,7 @@
     var frm = document.forms.database_info;
     var connection_info = 'info=' + ajaxEscape(toJSONString({
         db_host: frm.db_host.value,
+        db_port: frm.db_port.value,
         db_name: frm.db_name.value,
         db_user: frm.db_user.value,
         db_pass: frm.db_pass.value,
@@ -402,12 +423,25 @@
   </tr>
   <tr>
     <td>
+      <b><?php echo $lang->get('dbmysql_field_port_title'); ?></b>
+      <br /><?php echo $lang->get('dbmysql_field_port_body'); ?>
+      <br /><span style="color: #993300" id="e_db_port"></span>
+    </td>
+    <td>
+      <input onkeyup="verify(this);" tabindex="2" name="db_port" size="5" type="text" value="3306" />
+    </td>
+    <td>
+      <img id="s_db_port" alt="Good/bad icon" src="../images/checkbad.png" />
+    </td>
+  </tr>
+  <tr>
+    <td>
       <b><?php echo $lang->get('dbmysql_field_dbname_title'); ?></b><br />
       <?php echo $lang->get('dbmysql_field_dbname_body'); ?><br />
       <span style="color: #993300" id="e_db_name"></span>
     </td>
     <td>
-      <input onkeyup="verify(this);" tabindex="2" name="db_name" size="30" type="text" />
+      <input onkeyup="verify(this);" tabindex="3" name="db_name" size="30" type="text" />
     </td>
     <td>
       <img id="s_db_name" alt="Good/bad icon" src="../images/checkbad.png" />
@@ -420,9 +454,9 @@
       <span style="color: #993300" id="e_db_auth"></span>
     </td>
     <td>
-      <input onkeyup="verify(this);" tabindex="3" name="db_user" size="30" type="text" /><br />
+      <input onkeyup="verify(this);" tabindex="4" name="db_user" size="30" type="text" /><br />
       <br />
-      <input name="db_pass" tabindex="4" size="30" type="password" />
+      <input name="db_pass" tabindex="5" size="30" type="password" />
     </td>
     <td>
       <img id="s_db_auth" alt="Good/bad icon" src="../images/checkbad.png" />
@@ -439,7 +473,7 @@
       <?php echo $lang->get('dbmysql_field_tableprefix_body'); ?>
     </td>
     <td>
-      <input onkeyup="verify(this);" tabindex="5" name="table_prefix" size="30" type="text" />
+      <input onkeyup="verify(this);" tabindex="6" name="table_prefix" size="30" type="text" />
     </td>
     <td>
       <img id="s_table_prefix" alt="Good/bad icon" src="../images/check.png" />
@@ -452,9 +486,9 @@
       <span style="color: #993300" id="e_db_root"></span>
     </td>
     <td>
-      <input onkeyup="verify(this);" tabindex="6" name="db_root_user" size="30" type="text" /><br />
+      <input onkeyup="verify(this);" tabindex="7" name="db_root_user" size="30" type="text" /><br />
       <br />
-      <input onkeyup="verify(this);" tabindex="7" name="db_root_pass" size="30" type="password" />
+      <input onkeyup="verify(this);" tabindex="8" name="db_root_pass" size="30" type="password" />
     </td>
     <td>
       <img id="s_db_root" alt="Good/bad icon" src="../images/check.png" />
@@ -477,12 +511,12 @@
       <?php echo $lang->get('dbmysql_field_droptables_body'); ?>
     </td>
     <td colspan="2">
-      <input type="checkbox" tabindex="8" name="drop_tables" id="dtcheck" />  <label for="dtcheck"><?php echo $lang->get('dbmysql_field_droptables_lbl'); ?></label>
+      <input type="checkbox" tabindex="9" name="drop_tables" id="dtcheck" />  <label for="dtcheck"><?php echo $lang->get('dbmysql_field_droptables_lbl'); ?></label>
     </td>
   </tr>
   <tr>
     <td colspan="3" style="text-align: center">
-      <input type="button" tabindex="9" value="<?php echo $lang->get('dbmysql_btn_testconnection'); ?>" onclick="ajaxTestConnection();" />
+      <input type="button" tabindex="10" value="<?php echo $lang->get('dbmysql_btn_testconnection'); ?>" onclick="ajaxTestConnection();" />
       <div id="verify_error"></div>
     </td>
   </tr>
@@ -492,7 +526,7 @@
 <table border="0">
   <tr>
     <td>
-      <input type="submit" tabindex="10" value="<?php echo $lang->get('meta_btn_continue'); ?>" onclick="return verify();" name="_cont" />
+      <input type="submit" tabindex="11" value="<?php echo $lang->get('meta_btn_continue'); ?>" onclick="return verify();" name="_cont" />
     </td>
     <td>
       <p>
--- a/install/includes/stages/database_post.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/stages/database_post.php	Sat Mar 14 14:06:02 2009 -0400
@@ -22,6 +22,7 @@
 require( ENANO_ROOT . '/includes/sql_parse.php' );
 $dbal = new $driver();
 $db_host =& $_POST['db_host'];
+$db_port =& $_POST['db_port'];
 $db_user =& $_POST['db_user'];
 $db_pass =& $_POST['db_pass'];
 $db_name =& $_POST['db_name'];
@@ -36,7 +37,7 @@
   return true;
 }
 
-$result = $dbal->connect(true, $db_host, $db_user, $db_pass, $db_name);
+$result = $dbal->connect(true, $db_host, $db_user, $db_pass, $db_name, $db_port);
 
 // If connection failed, we have the root login, AND we're on MySQL, try to force our way in
 if ( !$result && !empty($_POST['db_root_user']) && !empty($_POST['db_root_pass']) && $driver == 'mysql' )
@@ -45,7 +46,7 @@
   switch ( 'foo' ) { case 'foo':
       
     // Try to connect to the DB as root
-    $result_root = $dbal->connect(true, $db_host, $db_root_user, $db_root_pass, 'mysql');
+    $result_root = $dbal->connect(true, $db_host, $db_root_user, $db_root_pass, 'mysql', $db_port);
     if ( !$result_root )
       break;
     
@@ -68,7 +69,7 @@
       break;
     
     $dbal->close();
-    $result = $dbal->connect(true, $db_host, $db_user, $db_pass, $db_name);
+    $result = $dbal->connect(true, $db_host, $db_user, $db_pass, $db_name, $db_port);
       
     break;
   }
@@ -112,11 +113,17 @@
     echo '<p>That table prefix isn\'t going to work.</p>';
     return true;
   }
+  if ( !preg_match('/^[0-9]*$/', $db_port) )
+  {
+    echo '<p>That port isn\'t going to work.</p>';
+    return true;
+  }
   fwrite($ch, "<?php
 // Enano temporary configuration file, will be OVERWRITTEN after installation.
 
 \$dbdriver = '$driver';
 \$dbhost = '$db_host';
+\$dbport = $db_port;
 \$dbname = '$db_name';
 \$dbuser = '$db_user';
 \$dbpasswd = '$db_pass';
--- a/install/includes/stages/database_postgresql.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/stages/database_postgresql.php	Sat Mar 14 14:06:02 2009 -0400
@@ -21,7 +21,7 @@
 {
   $allow_go = true;
   // Do we have everything? If so, continue with installation.
-  foreach ( array('db_host', 'db_name', 'db_user', 'db_pass') as $field )
+  foreach ( array('db_host', 'db_name', 'db_user', 'db_pass', 'db_port') as $field )
   {
     if ( empty($_POST[$field]) )
     {
@@ -73,9 +73,18 @@
       )));
   }
   
+  if ( !is_int($info['db_port']) )
+  {
+    $return['host_good'] = false;
+    echo enano_json_encode($return);
+    exit;
+  }
+  
+  $port = $info['db_port'] ? $info['db_port'] : 5432;
+  
   // Try to connect as the normal user
   // generate connection string
-  $conn_string = "dbname = '" . addslashes($info['db_name']) . "' port = '5432' host = '" . addslashes($info['db_host']) . "' " . 
+  $conn_string = "dbname = '" . addslashes($info['db_name']) . "' port = '$port' host = '" . addslashes($info['db_host']) . "' " . 
                  "user= '" . addslashes($info['db_user']) . "' password = '" . addslashes($info['db_pass']) . "'";
   $test = @pg_connect($conn_string);
   if ( !$test )
@@ -85,7 +94,7 @@
     $return['creating_user'] = true;
     if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
     {
-      $conn_string_root = "dbname = '" . addslashes($info['db_name']) . "' port = '5432' host = '" . addslashes($info['db_host']) . "' " . 
+      $conn_string_root = "dbname = '" . addslashes($info['db_name']) . "' port = '$port' host = '" . addslashes($info['db_host']) . "' " . 
                           "user= '" . addslashes($info['db_root_user']) . "' password = '" . addslashes($info['db_root_pass']) . "'";
       // Attempt connection as root
       $test_root = @pg_connect($conn_string_root);
@@ -181,6 +190,7 @@
     // List of fields
     var fields = {
       db_host: frm.db_host,
+      db_port: frm.db_port,
       db_name: frm.db_name,
       db_user: frm.db_user,
       db_pass: frm.db_pass,
@@ -197,6 +207,13 @@
       if ( !matches )
         passed = false;
     }
+    if ( field == fields.db_port || !field )
+    {
+      var matches = fields.db_port.value.match(/^[0-9]+$/);
+      document.getElementById('s_db_port').src = ( matches ) ? img_neu : img_bad;
+      if ( !matches )
+        passed = false;
+    }
     if ( field == fields.db_name || !field )
     {
       var matches = fields.db_name.value.match(/^[A-z0-9_-]+$/);
@@ -243,6 +260,7 @@
     var frm = document.forms.database_info;
     var connection_info = 'info=' + ajaxEscape(toJSONString({
         db_host: frm.db_host.value,
+        db_port: parseInt(frm.db_port.value),
         db_name: frm.db_name.value,
         db_user: frm.db_user.value,
         db_pass: frm.db_pass.value,
@@ -309,7 +327,10 @@
             }
             if ( !response.version.good )
             {
-              document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_err_version', { pg_version: response.version.version });
+              if ( response.version.version == 'indeterminate' )
+                document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_warn_pg_version');
+              else
+                document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_err_version', { pg_version: response.version.version });
               document.getElementById('s_pgsql_version').src = img_bad;
             }
           }
@@ -344,6 +365,19 @@
   </tr>
   <tr>
     <td>
+      <b><?php echo $lang->get('dbpgsql_field_port_title'); ?></b>
+      <br /><?php echo $lang->get('dbpgsql_field_port_body'); ?>
+      <br /><span style="color: #993300" id="e_db_port"></span>
+    </td>
+    <td>
+      <input onkeyup="verify(this);" tabindex="2" name="db_port" size="5" type="text" value="5432" />
+    </td>
+    <td>
+      <img id="s_db_port" alt="Good/bad icon" src="../images/checkbad.png" />
+    </td>
+  </tr>
+  <tr>
+    <td>
       <b><?php echo $lang->get('dbpgsql_field_dbname_title'); ?></b><br />
       <?php echo $lang->get('dbpgsql_field_dbname_body'); ?><br />
       <span style="color: #993300" id="e_db_name"></span>
--- a/install/includes/stages/install.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/stages/install.php	Sat Mar 14 14:06:02 2009 -0400
@@ -31,7 +31,7 @@
 }
 
 $db = new $dbdriver();
-$result = $db->connect(true, $dbhost, $dbuser, $dbpasswd, $dbname);
+$result = $db->connect();
 if ( !$result )
 {
   $ui->show_header();
--- a/install/includes/stages/login.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/stages/login.php	Sat Mar 14 14:06:02 2009 -0400
@@ -29,7 +29,7 @@
   die('Config file is corrupt');
 }
 $db = new $dbdriver();
-$result = $db->connect(true, $dbhost, $dbuser, $dbpasswd, $dbname);
+$result = $db->connect();
 if ( !$result )
   die('DB privileges were revoked');
 
--- a/install/includes/stages/sysreqs.php	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/includes/stages/sysreqs.php	Sat Mar 14 14:06:02 2009 -0400
@@ -48,55 +48,6 @@
   return $r;
 }
 
-function write_test($filename)
-{
-  // We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
-  // true on Windows/IIS servers. Don't ask me why.
-  
-  $file = ENANO_ROOT . '/' . $filename;
-  if ( is_dir($file) )
-  {
-    $file = rtrim($file, '/') . '/' . 'enanoinstalltest.txt';
-    if ( file_exists($file) )
-    {
-      $fp = @fopen($file, 'a+');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      unlink($file);
-      return true;
-    }
-    else
-    {
-      $fp = @fopen($file, 'w');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      unlink($file);
-      return true;
-    }
-  }
-  else
-  {
-    if ( file_exists($file) )
-    {
-      $fp = @fopen($file, 'a+');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      return true;
-    }
-    else
-    {
-      $fp = @fopen($file, 'w');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      return true;
-    }
-  }
-}
-
 $warnings = array();
 $failed = false;
 $have_dbms = false;
@@ -120,6 +71,7 @@
   $req_php = 'bad';
 }
 
+// Test: Safe Mode
 $req_safemode = !intval(@ini_get('safe_mode'));
 if ( !$req_safemode )
 {
@@ -143,6 +95,11 @@
 // Test: File uploads
 $req_uploads = intval(@ini_get('file_uploads'));
 
+// Test: ctype validation
+$req_ctype = function_exists('ctype_digit');
+if ( !$req_ctype )
+  $failed = true;
+
 // Writability test: config
 $req_config_w = write_test('config.new.php');
 
@@ -242,24 +199,6 @@
  
 <table border="0" cellspacing="0" cellpadding="0" class="sysreqs">
 
-<?php
-/*
-  
-  </div>
-<?php
-}
-else
-{
-  if ( $failed )
-  {
-    echo '<div class="pagenav"><table border="0" cellspacing="0" cellpadding="0">';
-    run_test('return false;', $lang->get('sysreqs_summary_fail_title'), $lang->get('sysreqs_summary_fail_body'));
-    echo '</table></div>';
-  }
-}
-*/
-?>
-
 <tr>
   <th colspan="2"><?php echo $lang->get('sysreqs_heading_serverenv'); ?></th>
 </tr>
@@ -303,6 +242,17 @@
 </tr>
 
 <tr>
+  <td><?php echo $lang->get('sysreqs_req_ctype'); ?></td>
+  <?php
+  if ( $req_ctype ):
+    echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
+  else:
+    echo '<td class="bad">' . $lang->get('sysreqs_req_unsupported') . '</td>';
+  endif;
+  ?>
+</tr>
+
+<tr>
   <th colspan="2"><?php echo $lang->get('sysreqs_heading_dbms'); ?></th>
 </tr>
 
--- a/install/schemas/postgresql_stage2.sql	Fri Mar 06 11:19:55 2009 -0500
+++ b/install/schemas/postgresql_stage2.sql	Sat Mar 14 14:06:02 2009 -0400
@@ -350,34 +350,18 @@
     initcond = '{}'
 );
 
+-- The default config. Kind of important.
+-- P.S. the allowed_mime_types value is a compressed bitfield. Source for the (rather simple) algo is in functions.php.
 
 INSERT INTO {{TABLE_PREFIX}}config(config_name, config_value) VALUES
   ('site_name', '{{SITE_NAME}}'),
-  ('main_page', 'Main_Page'),
   ('site_desc', '{{SITE_DESC}}'),
   ('wiki_mode', '{{WIKI_MODE}}'),
-  ('wiki_edit_notice', '0'),
-  ('sflogo_enabled', '0'),
-  ('sflogo_groupid', ''),
-  ('sflogo_type', '1'),
-  ('w3c_vh32', '0'),
-  ('w3c_vh40', '0'),
-  ('w3c_vh401', '0'),
-  ('w3c_vxhtml10', '0'),
-  ('w3c_vxhtml11', '0'),
-  ('w3c_vcss', '0'),
-  ('approve_comments', '0'),
-  ('enable_comments', '1'),
-  ('plugin_SpecialAdmin.php', '1'),
-  ('plugin_SpecialPageFuncs.php', '1'),
-  ('plugin_SpecialUserFuncs.php', '1'),
-  ('plugin_SpecialCSS.php', '1'),
   ('copyright_notice', '{{COPYRIGHT}}'),
-  ('wiki_edit_notice_text', '== Why can I edit this page? ==\n\nEveryone can edit almost any page in this website. This concept is called a wiki. It gives everyone the opportunity to make a change for the best. While some spam and vandalism may occur, it is believed that most contributions will be legitimate and helpful.\n\nFor security purposes, a history of all page edits is kept, and administrators are able to restore vandalized or spammed pages with just a few clicks.'),
   ('cache_thumbs', '{{ENABLE_CACHE}}'),
-  ('max_file_size', '256000'),('enano_version', '{{VERSION}}'),( 'allowed_mime_types', 'cbf:len=185;crc=55fb6f14;data=0[1],1[4],0[3],1[1],0[22],1[1],0[16],1[3],0[16],1[1],0[1],1[2],0[6],1[1],0[1],1[1],0[4],1[2],0[3],1[1],0[48],1[2],0[2],1[1],0[4],1[1],0[37]|end' ),
   ('contact_email', '{{ADMIN_EMAIL}}'),
-  ('powered_btn', '1');
+  ('allowed_mime_types', 'cbf2:7414a6b80184038102810b810781098106830a810282018101820683018102840182038104821a850682028104810a82018116'),
+  ('enano_version', '{{VERSION}}');
 
 INSERT INTO {{TABLE_PREFIX}}page_text(page_id, namespace, page_text, char_tag) VALUES
   ('Main_Page', 'Article', E'{{MAIN_PAGE_CONTENT}}', '');
--- a/language/english/install.json	Fri Mar 06 11:19:55 2009 -0500
+++ b/language/english/install.json	Sat Mar 14 14:06:02 2009 -0400
@@ -87,6 +87,7 @@
       req_postgresql: 'PostgreSQL database support',
       req_safemode: 'Safe Mode',
       req_uploads: 'PHP file upload support',
+      req_ctype: 'PHP ctype_* validation functions',
       req_config_writable: 'Configuration file: config.new.php',
       req_htaccess_writable: 'Apache rewrite rules: .htaccess.new',
       req_files_writable: 'File storage directory: files/',
@@ -182,7 +183,9 @@
       table_title: 'Database information',
       
       field_hostname_title: 'Database hostname',
-      field_hostname_body: 'This is the hostname (or sometimes the IP address) of your MySQL server. In many cases, this is "localhost".',
+      field_hostname_body: 'This is the hostname (or sometimes the IP address) of your MySQL server. In many cases, this is "localhost". To connect through a Unix socket, use the format ":/path/to/mysql.sock".',
+      field_port_title: 'Database port',
+      field_port_body: 'The TCP port Enano will use to connect to MySQL. This is 3306 on the vast majority of servers. This isn\'t relevant if you use a socket file.',
       field_dbname_title: 'Database name',
       field_dbname_body: 'The name of the actual database. If you don\'t already have a database, you can create one here, if you have the username and password of a MySQL user with administrative rights.',
       field_dbauth_title: 'Database login',
@@ -218,6 +221,8 @@
       
       field_hostname_title: 'Database hostname',
       field_hostname_body: 'This is the hostname (or sometimes the IP address) of your PostgreSQL server. In many cases, this is "localhost".',
+      field_port_title: 'Database port',
+      field_port_body: 'The TCP port Enano will use to connect to PostgreSQL. This is 5432 on the vast majority of servers.',
       field_dbname_title: 'Database name',
       field_dbname_body: 'The name of the actual database. If you don\'t already have a database, you can create one here, if you have the username and password of a PostgreSQL user with administrative rights.',
       field_dbauth_title: 'Database login',
@@ -438,6 +443,7 @@
       
       prompt_dbdriver: 'Database type (mysql or postgresql)',
       prompt_dbhost: 'Hostname of database server',
+      prompt_dbport: 'Database server port',
       prompt_dbuser: 'Database username',
       prompt_dbpasswd: 'Database password',
       prompt_dbname: 'Database name',
@@ -461,9 +467,11 @@
       
       stage_sysreqs: '<c 34;1>Checking system.</c>\n',
       test_pass: '<c 1;32>PASS</c>',
-      test_warn: '<c 1;33>FAIL</c>',
+      test_vwarn: '<c 1;33>WARNING</c>',
+      test_warn: '<c 0;31>FAIL</c><c 1> (optional)</c>',
       test_fail: '<c 1;31>FAIL</c>',
       
+      msg_test_warnings: '<c 1;33>The following warnings were generated during the check:</c>',
       msg_tests_passed: '<c 32><c 1>All tests passed.</c> Installing Enano.</c>\n',
       msg_installing_db_stage1: '<c 1>Installing database, stage 1...</c>',
       msg_parsing_schema: '<c 1>Preparing installation payload...</c>',