Many changes. Installer with PostgreSQL is broken badly and will be for some time.
authorDan
Tue, 18 Dec 2007 23:44:55 -0500
changeset 322 5f1cd51bf1be
parent 320 112debff64bd
child 323 a82133ce2d76
Many changes. Installer with PostgreSQL is broken badly and will be for some time.
ajax.php
includes/captcha.php
includes/clientside/jsres.php
includes/comment.php
includes/common.php
includes/constants.php
includes/dbal.php
includes/email.php
includes/functions.php
includes/graphs.php
includes/js-compressor.php
includes/pageprocess.php
includes/pageutils.php
includes/paths.php
includes/plugins.php
includes/render.php
includes/search.php
includes/sessions.php
includes/stats.php
includes/tagcloud.php
includes/template.php
includes/wikiengine/Tables.php
index.php
install.php
plugins/SpecialAdmin.php
plugins/SpecialGroups.php
plugins/SpecialPageFuncs.php
plugins/SpecialUserFuncs.php
plugins/SpecialUserPrefs.php
plugins/admin/SecurityLog.php
themes/oxygen/css/bleu.css
upgrade.php
--- a/ajax.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/ajax.php	Tue Dec 18 23:44:55 2007 -0500
@@ -97,13 +97,22 @@
       echo PageUtils::checkusername($_GET['name']);
       break;
     case "getsource":
-      $p = ( isset($_GET['pagepass']) ) ? $_GET['pagepass'] : false;
-      echo PageUtils::getsource($paths->page, $p);
+      $password = ( isset($_GET['pagepass']) ) ? $_GET['pagepass'] : false;
+      $page = new PageProcessor($paths->page_id, $paths->namespace);
+      $page->password = $password;
+      if ( $src = $page->fetch_source() )
+      {
+        echo $src;
+      }
+      else
+      {
+        echo 'err_access_denied';
+      }
       break;
     case "getpage":
       // echo PageUtils::getpage($paths->page, false, ( (isset($_GET['oldid'])) ? $_GET['oldid'] : false ));
       $revision_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
-      $page = new PageProcessor( $paths->cpage['urlname_nons'], $paths->namespace, $revision_id );
+      $page = new PageProcessor( $paths->page_id, $paths->namespace, $revision_id );
       
       $pagepass = ( isset($_REQUEST['pagepass']) ) ? $_REQUEST['pagepass'] : '';
       $page->password = $pagepass;
@@ -113,10 +122,10 @@
     case "savepage":
       $summ = ( isset($_POST['summary']) ) ? $_POST['summary'] : '';
       $minor = isset($_POST['minor']);
-      $e = PageUtils::savepage($paths->cpage['urlname_nons'], $paths->namespace, $_POST['text'], $summ, $minor);
+      $e = PageUtils::savepage($paths->page_id, $paths->namespace, $_POST['text'], $summ, $minor);
       if($e=='good')
       {
-        $page = new PageProcessor($paths->cpage['urlname_nons'], $paths->namespace);
+        $page = new PageProcessor($paths->page_id, $paths->namespace);
         $page->send();
       }
       else
@@ -125,16 +134,16 @@
       }
       break;
     case "protect":
-      echo PageUtils::protect($paths->cpage['urlname_nons'], $paths->namespace, (int)$_POST['level'], $_POST['reason']);
+      echo PageUtils::protect($paths->page_id, $paths->namespace, (int)$_POST['level'], $_POST['reason']);
       break;
     case "histlist":
-      echo PageUtils::histlist($paths->cpage['urlname_nons'], $paths->namespace);
+      echo PageUtils::histlist($paths->page_id, $paths->namespace);
       break;
     case "rollback":
       echo PageUtils::rollback( (int)$_GET['id'] );
       break;
     case "comments":
-      $comments = new Comments($paths->cpage['urlname_nons'], $paths->namespace);
+      $comments = new Comments($paths->page_id, $paths->namespace);
       if ( isset($_POST['data']) )
       {
         $comments->process_json($_POST['data']);
@@ -145,37 +154,37 @@
       }
       break;
     case "rename":
-      echo PageUtils::rename($paths->cpage['urlname_nons'], $paths->namespace, $_POST['newtitle']);
+      echo PageUtils::rename($paths->page_id, $paths->namespace, $_POST['newtitle']);
       break;
     case "flushlogs":
-      echo PageUtils::flushlogs($paths->cpage['urlname_nons'], $paths->namespace);
+      echo PageUtils::flushlogs($paths->page_id, $paths->namespace);
       break;
     case "deletepage":
       $reason = ( isset($_POST['reason']) ) ? $_POST['reason'] : false;
       if ( empty($reason) )
         die('Please enter a reason for deleting this page.');
-      echo PageUtils::deletepage($paths->cpage['urlname_nons'], $paths->namespace, $reason);
+      echo PageUtils::deletepage($paths->page_id, $paths->namespace, $reason);
       break;
     case "delvote":
-      echo PageUtils::delvote($paths->cpage['urlname_nons'], $paths->namespace);
+      echo PageUtils::delvote($paths->page_id, $paths->namespace);
       break;
     case "resetdelvotes":
-      echo PageUtils::resetdelvotes($paths->cpage['urlname_nons'], $paths->namespace);
+      echo PageUtils::resetdelvotes($paths->page_id, $paths->namespace);
       break;
     case "getstyles":
       echo PageUtils::getstyles($_GET['id']);
       break;
     case "catedit":
-      echo PageUtils::catedit($paths->cpage['urlname_nons'], $paths->namespace);
+      echo PageUtils::catedit($paths->page_id, $paths->namespace);
       break;
     case "catsave":
-      echo PageUtils::catsave($paths->cpage['urlname_nons'], $paths->namespace, $_POST);
+      echo PageUtils::catsave($paths->page_id, $paths->namespace, $_POST);
       break;
     case "setwikimode":
-      echo PageUtils::setwikimode($paths->cpage['urlname_nons'], $paths->namespace, (int)$_GET['mode']);
+      echo PageUtils::setwikimode($paths->page_id, $paths->namespace, (int)$_GET['mode']);
       break;
     case "setpass":
-      echo PageUtils::setpass($paths->cpage['urlname_nons'], $paths->namespace, $_POST['password']);
+      echo PageUtils::setpass($paths->page_id, $paths->namespace, $_POST['password']);
       break;
     case "fillusername":
       break;
@@ -230,7 +239,7 @@
       if(!$id1 || !$id2) { echo '<p>Invalid request.</p>'; $template->footer(); break; }
       if(!preg_match('#^([0-9]+)$#', (string)$_GET['diff1']) ||
          !preg_match('#^([0-9]+)$#', (string)$_GET['diff2']  )) { echo '<p>SQL injection attempt</p>'; $template->footer(); break; }
-      echo PageUtils::pagediff($paths->cpage['urlname_nons'], $paths->namespace, $id1, $id2);
+      echo PageUtils::pagediff($paths->page_id, $paths->namespace, $id1, $id2);
       break;
     case "jsres":
       die('// ERROR: this section is deprecated and has moved to includes/clientside/static/enano-lib-basic.js.');
@@ -278,7 +287,7 @@
       $q = $db->sql_query('SELECT t.tag_id, t.tag_name, pg.pg_target IS NOT NULL AS used_in_acl, t.user_id FROM '.table_prefix.'tags AS t
         LEFT JOIN '.table_prefix.'page_groups AS pg
           ON ( ( pg.pg_type = ' . PAGE_GRP_TAGGED . ' AND pg.pg_target=t.tag_name ) OR ( pg.pg_type IS NULL AND pg.pg_target IS NULL ) )
-        WHERE t.page_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND t.namespace=\'' . $db->escape($paths->namespace) . '\';');
+        WHERE t.page_id=\'' . $db->escape($paths->page_id) . '\' AND t.namespace=\'' . $db->escape($paths->namespace) . '\';');
       if ( !$q )
         $db->_die();
       
@@ -338,7 +347,7 @@
       }
       
       // check if tag is already on page
-      $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND namespace=\'' . $db->escape($paths->namespace) . '\' AND tag_name=\'' . $tag . '\';');
+      $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $db->escape($paths->namespace) . '\' AND tag_name=\'' . $tag . '\';');
       if ( !$q )
         $db->_die();
       if ( $db->numrows() > 0 )
@@ -362,7 +371,7 @@
       $db->free_result();
       
       // we're good
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'tags(tag_name,page_id,namespace,user_id) VALUES(\'' . $tag . '\', \'' . $db->escape($paths->cpage['urlname_nons']) . '\', \'' . $db->escape($paths->namespace) . '\', ' . $session->user_id . ');');
+      $q = $db->sql_query('INSERT INTO '.table_prefix.'tags(tag_name,page_id,namespace,user_id) VALUES(\'' . $tag . '\', \'' . $db->escape($paths->page_id) . '\', \'' . $db->escape($paths->namespace) . '\', ' . $session->user_id . ');');
       if ( !$q )
         $db->_die();
       
@@ -392,7 +401,7 @@
       $row = $db->fetchrow();
       $db->free_result();
       
-      if ( $row['page_id'] == $paths->cpage['urlname_nons'] && $row['namespace'] == $paths->namespace )
+      if ( $row['page_id'] == $paths->page_id && $row['namespace'] == $paths->namespace )
         $perms =& $session;
       else
         $perms = $session->fetch_page_acl($row['page_id'], $row['namespace']);
--- a/includes/captcha.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/captcha.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  * captcha.php - visual confirmation system used during registration
  *
--- a/includes/clientside/jsres.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/clientside/jsres.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  * jsres.php - the Enano client-side runtime, a.k.a. AJAX on steroids
  *
@@ -40,7 +40,7 @@
 {
   echo "/*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * [Aggressively compressed] Javascript client code
  * Copyright (C) 2006-2007 Dan Fuhry
  * Enano is Free Software, licensed under the GNU General Public License; see http://enanocms.org/ for details.
--- a/includes/comment.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/comment.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  *
  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
@@ -54,7 +54,7 @@
     global $db, $session, $paths, $template, $plugins; // Common objects
     
     // Initialize permissions
-    if ( $page_id == $paths->cpage['urlname_nons'] && $namespace == $paths->namespace )
+    if ( $page_id == $paths->page_id && $namespace == $paths->namespace )
       $this->perms =& $GLOBALS['session'];
     else
       $this->perms = $session->fetch_page_acl($page_id, $namespace);
--- a/includes/common.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/common.php	Tue Dec 18 23:44:55 2007 -0500
@@ -152,7 +152,7 @@
 // even be installed. If this connection attempt fails and it's because of a missing or corrupt config file, the
 // user will be redirected (intelligently) to install.php.
 
-require(ENANO_ROOT . '/config.php');
+@include(ENANO_ROOT . '/config.php');
 unset($dbuser, $dbpasswd);
 if ( !isset($dbdriver) )
   $dbdriver = 'mysql';
@@ -328,7 +328,7 @@
   // If the site is disabled, bail out, unless we're trying to log in or administer the site
   if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
   {
-    if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->cpage['urlname_nons'] == 'CSS' || $paths->cpage['urlname_nons'] == 'Administration' || $paths->cpage['urlname_nons'] == 'Login' ) ) )
+    if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->page_id == 'CSS' || $paths->page_id == 'Administration' || $paths->page_id == 'Login' ) ) )
     {
       // do nothing; allow execution to continue
     }
--- a/includes/constants.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/constants.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  * constants.php - important defines used Enano-wide
  *
--- a/includes/dbal.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/dbal.php	Tue Dec 18 23:44:55 2007 -0500
@@ -120,7 +120,7 @@
     return $internal_text;
   }
   
-  function connect()
+  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false)
   {
     $this->enable_errorhandler();
     
@@ -130,42 +130,46 @@
     define('ENANO_SQL_BOOLEAN_TRUE', 'true');
     define('ENANO_SQL_BOOLEAN_FALSE', 'false');
     
-    if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
-    {
-      @include(ENANO_ROOT.'/config.new.php');
-    }
-    else
+    if ( !$manual_credentials )
     {
-      @include(ENANO_ROOT.'/config.php');
-    }
+      if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
+      {
+        @include(ENANO_ROOT.'/config.new.php');
+      }
+      else
+      {
+        @include(ENANO_ROOT.'/config.php');
+      }
+        
+      if ( isset($crypto_key) )
+        unset($crypto_key); // Get this sucker out of memory fast
       
-    if ( isset($crypto_key) )
-      unset($crypto_key); // Get this sucker out of memory fast
-    
-    if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
-    {
-      // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
-      if ( !defined('scriptPath') )
+      if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
       {
-        if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
+        // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
+        if ( !defined('scriptPath') )
         {
-          $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
-        }
-        if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
-        {
-          // user requested http://foo/enano as opposed to http://foo/enano/index.php
-          $_SERVER['REQUEST_URI'] .= '/index.php';
+          if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
+          {
+            $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
+          }
+          if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
+          {
+            // user requested http://foo/enano as opposed to http://foo/enano/index.php
+            $_SERVER['REQUEST_URI'] .= '/index.php';
+          }
+          $sp = dirname($_SERVER['REQUEST_URI']);
+          if($sp == '/' || $sp == '\\') $sp = '';
+          define('scriptPath', $sp);
+          define('contentPath', "$sp/index.php?title=");
         }
-        $sp = dirname($_SERVER['REQUEST_URI']);
-        if($sp == '/' || $sp == '\\') $sp = '';
-        define('scriptPath', $sp);
-        define('contentPath', "$sp/index.php?title=");
+        $loc = scriptPath . '/install.php';
+        // header("Location: $loc");
+        redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
+        exit;
       }
-      $loc = scriptPath . '/install.php';
-      // header("Location: $loc");
-      redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
-      exit;
     }
+    
     $this->_conn = @mysql_connect($dbhost, $dbuser, $dbpasswd);
     unset($dbuser);
     unset($dbpasswd); // Security
@@ -871,7 +875,7 @@
     return $internal_text;
   }
   
-  function connect()
+  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false)
   {
     $this->enable_errorhandler();
     
@@ -881,41 +885,44 @@
     define('ENANO_SQL_BOOLEAN_TRUE', '1');
     define('ENANO_SQL_BOOLEAN_FALSE', '0');
     
-    if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
-    {
-      @include(ENANO_ROOT.'/config.new.php');
-    }
-    else
+    if ( !$manual_credentials )
     {
-      @include(ENANO_ROOT.'/config.php');
-    }
+      if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
+      {
+        @include(ENANO_ROOT.'/config.new.php');
+      }
+      else
+      {
+        @include(ENANO_ROOT.'/config.php');
+      }
+        
+      if ( isset($crypto_key) )
+        unset($crypto_key); // Get this sucker out of memory fast
       
-    if ( isset($crypto_key) )
-      unset($crypto_key); // Get this sucker out of memory fast
-    
-    if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
-    {
-      // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
-      if ( !defined('scriptPath') )
+      if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
       {
-        if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
+        // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
+        if ( !defined('scriptPath') )
         {
-          $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
-        }
-        if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
-        {
-          // user requested http://foo/enano as opposed to http://foo/enano/index.php
-          $_SERVER['REQUEST_URI'] .= '/index.php';
+          if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
+          {
+            $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
+          }
+          if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
+          {
+            // user requested http://foo/enano as opposed to http://foo/enano/index.php
+            $_SERVER['REQUEST_URI'] .= '/index.php';
+          }
+          $sp = dirname($_SERVER['REQUEST_URI']);
+          if($sp == '/' || $sp == '\\') $sp = '';
+          define('scriptPath', $sp);
+          define('contentPath', "$sp/index.php?title=");
         }
-        $sp = dirname($_SERVER['REQUEST_URI']);
-        if($sp == '/' || $sp == '\\') $sp = '';
-        define('scriptPath', $sp);
-        define('contentPath', "$sp/index.php?title=");
+        $loc = scriptPath . '/install.php';
+        // header("Location: $loc");
+        redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
+        exit;
       }
-      $loc = scriptPath . '/install.php';
-      // header("Location: $loc");
-      redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
-      exit;
     }
     $this->_conn = @pg_connect("host=$dbhost port=5432 dbname=$dbname user=$dbuser password=$dbpasswd");
     unset($dbuser);
--- a/includes/email.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/email.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  *
  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
--- a/includes/functions.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/functions.php	Tue Dec 18 23:44:55 2007 -0500
@@ -565,7 +565,7 @@
     $q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c
                            LEFT JOIN '.table_prefix.'pages AS p
                              ON ( p.urlname = c.page_id AND p.namespace = c.namespace )
-                           WHERE c.category_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\'
+                           WHERE c.category_id=\'' . $db->escape($paths->page_id) . '\'
                            ORDER BY is_category DESC, p.name ASC;');
     if ( !$q )
     {
@@ -677,7 +677,7 @@
     echo '</div>';
     echo '<div id="mdgCatBox">Categories: ';
     
-    $where = '( c.page_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND c.namespace=\'' . $db->escape($paths->namespace) . '\' )';
+    $where = '( c.page_id=\'' . $db->escape($paths->page_id) . '\' AND c.namespace=\'' . $db->escape($paths->namespace) . '\' )';
     $prefix = table_prefix;
     $sql = <<<EOF
 SELECT c.category_id FROM {$prefix}categories AS c
@@ -729,11 +729,11 @@
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
   if($paths->namespace != 'File') return null; // Prevent unnecessary work
-  $selfn = $paths->cpage['urlname_nons']; // substr($paths->page, strlen($paths->nslist['File']), strlen($paths->cpage));
-  if(substr($paths->cpage['name'], 0, strlen($paths->nslist['File']))==$paths->nslist['File']) $selfn = substr($paths->cpage['urlname_nons'], strlen($paths->nslist['File']), strlen($paths->cpage['urlname_nons']));
+  $selfn = $paths->page_id; // substr($paths->page, strlen($paths->nslist['File']), strlen($paths->cpage));
+  if(substr($paths->cpage['name'], 0, strlen($paths->nslist['File']))==$paths->nslist['File']) $selfn = substr($paths->page_id, strlen($paths->nslist['File']), strlen($paths->page_id));
   $q = $db->sql_query('SELECT mimetype,time_id,size FROM '.table_prefix.'files WHERE page_id=\''.$selfn.'\' ORDER BY time_id DESC;');
   if(!$q) $db->_die('The file type could not be fetched.');
-  if($db->numrows() < 1) { echo '<div class="mdg-comment" style="margin-left: 0;"><h3>Uploaded file</h3><p>There are no files uploaded with this name yet. <a href="'.makeUrlNS('Special', 'UploadFile/'.$paths->cpage['urlname_nons']).'">Upload a file...</a></p></div><br />'; return; }
+  if($db->numrows() < 1) { echo '<div class="mdg-comment" style="margin-left: 0;"><h3>Uploaded file</h3><p>There are no files uploaded with this name yet. <a href="'.makeUrlNS('Special', 'UploadFile/'.$paths->page_id).'">Upload a file...</a></p></div><br />'; return; }
   $r = $db->fetchrow();
   $mimetype = $r['mimetype'];
   $datestring = date('F d, Y h:i a', (int)$r['time_id']);
@@ -2418,7 +2418,6 @@
 
 function sanitize_page_id($page_id)
 {
-
   // Remove character escapes
   $page_id = dirtify_page_id($page_id);
 
@@ -2473,11 +2472,17 @@
   $page_id = str_replace(' ', '_', $page_id);
 
   // Exception for userpages for IP addresses
-  if ( preg_match('/^' . preg_quote($paths->nslist['User']) . '/', $page_id) )
+  if ( isset($paths->nslist['User']) )
   {
-    $ip = preg_replace('/^' . preg_quote($paths->nslist['User']) . '/', '', $page_id);
-    if ( is_valid_ip($ip) )
-      return $page_id;
+    if ( preg_match('/^' . preg_quote($paths->nslist['User']) . '/', $page_id) )
+    {
+      $ip = preg_replace('/^' . preg_quote($paths->nslist['User']) . '/', '', $page_id);
+      if ( is_valid_ip($ip) )
+      {
+        die('valid IP');
+        return $page_id;
+      }
+    }
   }
 
   preg_match_all('/\.[A-Fa-f0-9][A-Fa-f0-9]/', $page_id, $matches);
--- a/includes/graphs.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/graphs.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  *
  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
--- a/includes/js-compressor.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/js-compressor.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  * Javascript compression library - used to compact the client-side Javascript code (all 72KB of it!) to save some bandwidth
  *
--- a/includes/pageprocess.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/pageprocess.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * pageprocess.php - intelligent retrieval of pages
  * Copyright (C) 2006-2007 Dan Fuhry
  *
@@ -15,6 +15,7 @@
 
 /**
  * Class to handle fetching page text (possibly from a cache) and formatting it.
+ * As of 1.0.4, this also handles the fetching and editing of certain data for pages.
  * @package Enano
  * @subpackage UI
  * @copyright 2007 Dan Fuhry
@@ -113,6 +114,13 @@
     );
   
   /**
+   * The list of errors raised in the class.
+   * @var array
+   */
+  
+  var $_errors = array();
+  
+  /**
    * Constructor.
    * @param string The page ID (urlname) of the page
    * @param string The namespace of the page
@@ -312,6 +320,173 @@
   }
   
   /**
+   * Fetches the wikitext or HTML source for the page.
+   * @return string
+   */
+  
+  function fetch_source()
+  {
+    if ( !$this->perms->get_permissions('view_source') )
+    {
+      return false;
+    }
+    return $this->fetch_text();
+  }
+  
+  /**
+   * Updates the content of the page.
+   * @param string The new text for the page
+   * @param string A summary of edits made to the page.
+   * @return bool True on success, false on failure
+   */
+  
+  function update_page($text, $edit_summary = false)
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    // Create the page if it doesn't exist
+    if ( !$this->page_exists )
+    {
+      if ( !$this->create_page() )
+      {
+        return false;
+      }
+    }
+      
+    //
+    // Validation
+    //
+    
+    $page_id = $db->escape($this->page_id);
+    $namespace = $db->escape($this->namespace);
+    
+    $q = $db->sql_query('SELECT protected FROM ' . table_prefix . "pages WHERE urlname='$page_id' AND namespace='$namespace';");
+    if ( !$q )
+      $db->_die('PageProcess updating page content');
+    if ( $db->numrows() < 1 )
+    {
+      $this->raise_error('Page doesn\'t exist in the database');
+      return false;
+    }
+    
+    // Do we have permission to edit the page?
+    if ( !$this->perms->get_permissions('edit_page') )
+    {
+      $this->raise_error('You do not have permission to edit this page.');
+      return false;
+    }
+    
+    list($protection) = $db->fetchrow_num();
+    $db->free_result();
+    
+    if ( $protection == 1 )
+    {
+      // The page is protected - do we have permission to edit protected pages?
+      if ( !$this->perms->get_permissions('even_when_protected') )
+      {
+        $this->raise_error('This page is protected, and you do not have permission to edit protected pages.');
+        return false;
+      }
+    }
+    else if ( $protection == 2 )
+    {
+      // The page is semi-protected.
+      if (
+           ( !$session->user_logged_in || // Is the user logged in?
+             ( $session->user_logged_in && $session->reg_time + ( 4 * 86400 ) >= time() ) ) // If so, have they been registered for 4 days?
+           && !$this->perms->get_permissions('even_when_protected') ) // And of course, is there an ACL that overrides semi-protection?
+      {
+        $this->raise_error('This page is protected, and you do not have permission to edit protected pages.');
+        return false;
+      }
+    }
+    
+    // Protection validated
+    
+  }
+  
+  /**
+   * Creates the page if it doesn't already exist.
+   * @return bool True on success, false on failure.
+   */
+  
+  function create_page()
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    // Do we have permission to create the page?
+    if ( !$this->perms->get_permissions('create_page') )
+    {
+      $this->raise_error('You do not have permission to create this page.');
+      return false;
+    }
+    
+    // Does it already exist?
+    if ( $this->page_exists )
+    {
+      $this->raise_error('The page already exists.');
+      return false;
+    }
+    
+    // It's not in there. Perform validation.
+    
+    // We can't create special, admin, or external pages.
+    if ( $this->namespace == 'Special' || $this->namespace == 'Admin' || $this->namespace == 'Anonymous' )
+    {
+      $this->raise_error('You cannot create Special or Admin pages - they can\'t be stored in the database.');
+      return false;
+    }
+    
+    // Guess the proper title
+    $name = dirtify_page_id($this->page_id);
+    
+    // Check for the restricted Project: prefix
+    if ( substr($this->page_id, 0, 8) == 'Project:' )
+    {
+      $this->raise_error('The prefix "Project:" is reserved for internal links and can\'t be used on a page name.');
+      return false;
+    }
+    
+    // Validation successful - insert the page
+    
+    $metadata = array(
+        'urlname' => $this->page_id,
+        'namespace' => $this->namespace,
+        'name' => $name,
+        'special' => 0,
+        'visible' => 1,
+        'comments_on' => 1,
+        'protected' => ( $this->namespace == 'System' ? 1 : 0 ),
+        'delvotes' => 0,
+        'delvote_ips' => serialize(array()),
+        'wiki_mode' => 2
+      );
+    
+    $paths->add_page($metadata);
+    
+    $page_id = $db->escape($this->page_id);
+    $namespace = $db->escape($this->namespace);
+    $name = $db->escape($name);
+    $protect = ( $this->namespace == 'System' ) ? '1' : '0';
+    $blank_array = $db->escape(serialize(array()));
+    
+    // Query 1: Metadata entry
+    $q = $db->sql_query('INSERT INTO ' . table_prefix . "pages(name, urlname, namespace, protected, delvotes, delvote_ips, wiki_mode)\n"
+                        . "VALUES ( '$name', '$page_id', '$namespace', $protect, 0, '$blank_array', 2 );");
+    if ( !$q )
+      $db->_die('PageProcessor page creation - metadata stage');
+    
+    // Query 2: Text insertion
+    $q = $db->sql_query('INSERT INTO ' . table_prefix . "page_text(page_id, namespace, page_text)\n"
+                        . "VALUES ( '$page_id', '$namespace', '' );");
+    if ( !$q )
+      $db->_die('PageProcessor page creation - text stage');
+    
+    // Page created. We're good!
+    return true;
+  }
+  
+  /**
    * Sets internal variables.
    * @access private
    */
@@ -336,7 +511,7 @@
     }
     
     // Does the page "exist"?
-    if ( $paths->cpage['urlname_nons'] == $page_id && $paths->namespace == $namespace && !$paths->page_exists && ( $this->namespace != 'Admin' || ($this->namespace == 'Admin' && !function_exists($fname) ) ) )
+    if ( $paths->page_id == $page_id && $paths->namespace == $namespace && !$paths->page_exists && ( $this->namespace != 'Admin' || ($this->namespace == 'Admin' && !function_exists($fname) ) ) )
     {
       $this->page_exists = false;
     }
@@ -354,7 +529,7 @@
     {
       $page_id = str_replace('.2e', '.', $page_id);
       
-      if ( $paths->cpage['urlname_nons'] == $page_id && $paths->namespace == $namespace && !$paths->page_exists && ( $this->namespace != 'Admin' || ($this->namespace == 'Admin' && !function_exists($fname) ) ) )
+      if ( $paths->page_id == $page_id && $paths->namespace == $namespace && !$paths->page_exists && ( $this->namespace != 'Admin' || ($this->namespace == 'Admin' && !function_exists($fname) ) ) )
       {
         $this->page_exists = false;
       }
@@ -630,7 +805,7 @@
     global $email;
     
     $page_urlname = dirtify_page_id($this->page_id);
-    if ( $this->page_id == $paths->cpage['urlname_nons'] && $this->namespace == $paths->namespace )
+    if ( $this->page_id == $paths->page_id && $this->namespace == $paths->namespace )
     {
       $page_name = ( isset($paths->cpage['name']) ) ? $paths->cpage['name'] : $this->page_id;
     }
@@ -1246,6 +1421,30 @@
     
   }
   
+  /**
+   * Raises an error.
+   * @param string Error string
+   */
+   
+  function raise_error($string)
+  {
+    if ( !is_string($string) )
+      return false;
+    $this->_errors[] = $string;
+  }
+  
+  /**
+   * Retrieves the latest error from the error stack and returns it ('pops' the error stack)
+   * @return string
+   */
+  
+  function pop_error()
+  {
+    if ( count($this->_errors) < 1 )
+      return false;
+    return array_pop($this->_errors);
+  }
+  
 } // class PageProcessor
 
 ?>
--- a/includes/pageutils.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/pageutils.php	Tue Dec 18 23:44:55 2007 -0500
@@ -202,7 +202,7 @@
         else echo ' Return to the <a href="'.makeUrl(getConfig('main_page')).'">homepage</a>.</p>';
         if ( $session->get_permissions('history_rollback') )
         {
-          $e = $db->sql_query('SELECT * FROM ' . table_prefix.'logs WHERE action=\'delete\' AND page_id=\'' . $paths->cpage['urlname_nons'] . '\' AND namespace=\'' . $pid[1] . '\' ORDER BY time_id DESC;');
+          $e = $db->sql_query('SELECT * FROM ' . table_prefix.'logs WHERE action=\'delete\' AND page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $pid[1] . '\' ORDER BY time_id DESC;');
           if ( !$e )
           {
             $db->_die('The deletion log could not be selected.');
@@ -379,7 +379,7 @@
     $msg = $db->escape($message);
     
     $minor = $minor ? ENANO_SQL_BOOLEAN_TRUE : ENANO_SQL_BOOLEAN_FALSE;
-    $q='INSERT INTO ' . table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \'' . $paths->cpage['urlname_nons'] . '\', \'' . $paths->namespace . '\', ' . ENANO_SQL_MULTISTRING_PRFIX . '\'' . $msg . '\', \'' . $uid . '\', \'' . $session->username . '\', \'' . $db->escape(htmlspecialchars($summary)) . '\', ' . $minor . ');';
+    $q='INSERT INTO ' . table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \'' . $paths->page_id . '\', \'' . $paths->namespace . '\', ' . ENANO_SQL_MULTISTRING_PRFIX . '\'' . $msg . '\', \'' . $uid . '\', \'' . $session->username . '\', \'' . $db->escape(htmlspecialchars($summary)) . '\', ' . $minor . ');';
     if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.');
     
     $q = 'UPDATE ' . table_prefix.'page_text SET page_text=' . ENANO_SQL_MULTISTRING_PRFIX . '\'' . $msg . '\',char_tag=\'' . $uid . '\' WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';';
@@ -654,7 +654,7 @@
     }
     $db->free_result();
     echo '<h3>Other changes:</h3>';
-    $q = 'SELECT time_id,action,date_string,page_id,namespace,author,edit_summary,minor_edit FROM ' . table_prefix.'logs WHERE log_type=\'page\' AND action!=\'edit\' AND page_id=\'' . $paths->cpage['urlname_nons'] . '\' AND namespace=\'' . $paths->namespace . '\' ORDER BY time_id DESC;';
+    $q = 'SELECT time_id,action,date_string,page_id,namespace,author,edit_summary,minor_edit FROM ' . table_prefix.'logs WHERE log_type=\'page\' AND action!=\'edit\' AND page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\' ORDER BY time_id DESC;';
     if(!$db->sql_query($q)) $db->_die('The history data for the page "' . $paths->cpage['name'] . '" could not be selected.');
     if($db->numrows() < 1) echo 'No history entries in this category.';
     else {
@@ -1371,7 +1371,7 @@
     }
     if( ( $session->get_permissions('rename') && ( ( $prot && $session->get_permissions('even_when_protected') ) || !$prot ) ) && ( $paths->namespace != 'Special' && $paths->namespace != 'Admin' ))
     {
-      $e = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'rename\', \'' . $db->escape($paths->cpage['urlname_nons']) . '\', \'' . $paths->namespace . '\', \'' . $db->escape($session->username) . '\', \'' . $db->escape($paths->cpage['name']) . '\')');
+      $e = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,edit_summary) VALUES('.time().', \''.date('d M Y h:i a').'\', \'page\', \'rename\', \'' . $db->escape($paths->page_id) . '\', \'' . $paths->namespace . '\', \'' . $db->escape($session->username) . '\', \'' . $db->escape($paths->cpage['name']) . '\')');
       if ( !$e )
       {
         $db->_die('The page title could not be updated.');
@@ -1597,7 +1597,7 @@
     global $db, $session, $paths, $template, $plugins; // Common objects
     ob_start();
     $_ob = '';
-    $e = $db->sql_query('SELECT category_id FROM ' . table_prefix.'categories WHERE page_id=\'' . $paths->cpage['urlname_nons'] . '\' AND namespace=\'' . $paths->namespace . '\'');
+    $e = $db->sql_query('SELECT category_id FROM ' . table_prefix.'categories WHERE page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\'');
     if(!$e) jsdie('Error selecting category information for current page: '.mysql_error());
     $cat_current = Array();
     while($r = $db->fetchrow())
@@ -2183,7 +2183,7 @@
               <p><label><input name="data[scope]" value="entire_site" type="radio" /> The entire site</p>
               <div style="margin: 0 auto 0 0; text-align: right;">
                 <input name="data[mode]" value="seltarget" type="hidden" />
-                <input type="hidden" name="data[page_id]" value="' . $paths->cpage['urlname_nons'] . '" />
+                <input type="hidden" name="data[page_id]" value="' . $paths->page_id . '" />
                 <input type="hidden" name="data[namespace]" value="' . $paths->namespace . '" />
                 <input type="submit" value="Next &gt;" />
               </div>';
--- a/includes/paths.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/paths.php	Tue Dec 18 23:44:55 2007 -0500
@@ -18,7 +18,7 @@
  */
  
 class pathManager {
-  var $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $anonymous_page;
+  var $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $page_id, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $anonymous_page;
   function __construct()
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
@@ -293,6 +293,7 @@
     {
       $this->page_exists = true;
       $this->cpage = $this->pages[$this->page];
+      $this->page_id =& $this->cpage['urlname_nons'];
       $this->namespace = $this->cpage['namespace'];
       if(!isset($this->cpage['wiki_mode'])) $this->cpage['wiki_mode'] = 2;
       
--- a/includes/plugins.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/plugins.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  *
  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
--- a/includes/render.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/render.php	Tue Dec 18 23:44:55 2007 -0500
@@ -44,7 +44,7 @@
     
     $perms =& $session;
     
-    if ( $page_id != $paths->cpage['urlname_nons'] || $namespace != $paths->namespace )
+    if ( $page_id != $paths->page_id || $namespace != $paths->namespace )
     {
       unset($perms);
       unset($perms); // PHP <5.1.5 Zend bug
--- a/includes/search.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/search.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  * search.php - algorithm used to search pages
  *
--- a/includes/sessions.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/sessions.php	Tue Dec 18 23:44:55 2007 -0500
@@ -1378,11 +1378,11 @@
       {
         $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
               . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
-              . "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' LIKE ban_value ) OR \n"
+              . "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value ) OR \n"
               . "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
-              . "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' LIKE ban_value ) OR \n"
+              . "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' ~ ban_value ) OR \n"
               . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
-              . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' LIKE ban_value ) \n"
+              . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' ~ ban_value ) \n"
               . "  ORDER BY ban_type ASC;";
       }
       $q = $this->sql($sql);
@@ -1426,7 +1426,7 @@
       {
         $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
                   ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
-                  ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' LIKE ban_value )
+                  ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value )
                 ORDER BY ban_type ASC;";
       }
       $q = $this->sql($sql);
@@ -2274,7 +2274,7 @@
     }
     
     // PAGE group info
-    $pg_list = $paths->get_page_groups($paths->cpage['urlname_nons'], $paths->namespace);
+    $pg_list = $paths->get_page_groups($paths->page_id, $paths->namespace);
     $pg_info = '';
     foreach ( $pg_list as $g_id )
     {
@@ -2294,7 +2294,7 @@
     }
     // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
     // permissions to override group permissions.
-    $bs .= implode(" OR\n    ", $q) . " )\n  AND (" . $pg_info . ' ( page_id=\''.$db->escape($paths->cpage['urlname_nons']).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) )     
+    $bs .= implode(" OR\n    ", $q) . " )\n  AND (" . $pg_info . ' ( page_id=\''.$db->escape($paths->page_id).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) )     
       ORDER BY target_type ASC, page_id ASC, namespace ASC;';
     $q = $this->sql($bs);
     if ( $row = $db->fetchrow() )
--- a/includes/stats.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/stats.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  * stats.php - handles statistics for pages (disablable in the admin CP)
  *
@@ -22,7 +22,7 @@
   {
     if(!$page_id || !$namespace)
     {
-      $page_id = $paths->cpage['urlname_nons'];
+      $page_id = $paths->page_id;
       $namespace = $paths->namespace;
     }
     if($namespace == 'Special' || $namespace == 'Admin') 
--- a/includes/tagcloud.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/tagcloud.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  *
  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
--- a/includes/template.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/template.php	Tue Dec 18 23:44:55 2007 -0500
@@ -259,7 +259,7 @@
     if ( $session->get_permissions('read') && getConfig('enable_comments')=='1' && $paths->namespace != 'Special' && $paths->namespace != 'Admin' && $paths->cpage['comments_on'] == 1 )
     {
       
-      $e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\';');
+      $e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$paths->page_id.'\' AND namespace=\''.$paths->namespace.'\';');
       if ( !$e )
       {
         $db->_die();
@@ -625,7 +625,7 @@
     
     $this->tpl_bool['stupid_mode'] = false;
     
-    $this->tpl_bool['in_admin'] = ( ( $paths->cpage['urlname_nons'] == 'Administration' && $paths->namespace == 'Special' ) || $paths->namespace == 'Admin' );
+    $this->tpl_bool['in_admin'] = ( ( $paths->page_id == 'Administration' && $paths->namespace == 'Special' ) || $paths->namespace == 'Admin' );
     
     $p = ( isset($_GET['printable']) ) ? '/printable' : '';
     
@@ -741,7 +741,7 @@
       'ADMIN_LINK'=>$admin_link,
       'THEME_LINK'=>$theme_link,
       'SEARCH_ACTION'=>makeUrlNS('Special', 'Search'),
-      'INPUT_TITLE'=>( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->nslist[$paths->namespace] . $paths->cpage['urlname_nons'] ) . '" />' : ''),
+      'INPUT_TITLE'=>( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->nslist[$paths->namespace] . $paths->page_id ) . '" />' : ''),
       'INPUT_AUTH'=>( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : ''),
       'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme,
       'THEME_ID'=>$this->theme,
@@ -1703,7 +1703,7 @@
   function notify_unread_pms()
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( ( $paths->cpage['urlname_nons'] == 'PrivateMessages' || $paths->cpage['urlname_nons'] == 'Preferences' ) && $paths->namespace == 'Special' )
+    if ( ( $paths->page_id == 'PrivateMessages' || $paths->page_id == 'Preferences' ) && $paths->namespace == 'Special' )
     {
       return '';
     }
--- a/includes/wikiengine/Tables.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/includes/wikiengine/Tables.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  *
  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
--- a/index.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/index.php	Tue Dec 18 23:44:55 2007 -0500
@@ -64,7 +64,7 @@
     case 'view':
       // echo PageUtils::getpage($paths->page, true, ( (isset($_GET['oldid'])) ? $_GET['oldid'] : false ));
       $rev_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
-      $page = new PageProcessor( $paths->cpage['urlname_nons'], $paths->namespace, $rev_id );
+      $page = new PageProcessor( $paths->page_id, $paths->namespace, $rev_id );
       $page->send_headers = true;
       $pagepass = ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '';
       $page->password = $pagepass;
@@ -79,7 +79,7 @@
         default:
           $act = ( isset ($_GET['action']) ) ? $_GET['action'] : false;
           $id = ( isset ($_GET['id']) ) ? intval($_GET['id']) : -1;
-          echo PageUtils::comments_html($paths->cpage['urlname_nons'], $paths->namespace, $act, Array('id'=>$id));
+          echo PageUtils::comments_html($paths->page_id, $paths->namespace, $act, Array('id'=>$id));
           break;
         case 'postcomment':
           if(empty($_POST['name']) ||
@@ -88,8 +88,8 @@
              ) { echo 'Invalid request'; break; }
           $cid = ( isset($_POST['captcha_id']) ) ? $_POST['captcha_id'] : false;
           $cin = ( isset($_POST['captcha_input']) ) ? $_POST['captcha_input'] : false;
-          PageUtils::addcomment($paths->cpage['urlname_nons'], $paths->namespace, $_POST['name'], $_POST['subj'], $_POST['text'], $cin, $cid); // All filtering, etc. is handled inside this method
-          echo PageUtils::comments_html($paths->cpage['urlname_nons'], $paths->namespace);
+          PageUtils::addcomment($paths->page_id, $paths->namespace, $_POST['name'], $_POST['subj'], $_POST['text'], $cin, $cid); // All filtering, etc. is handled inside this method
+          echo PageUtils::comments_html($paths->page_id, $paths->namespace);
           break;
         case 'editcomment':
           if(!isset($_GET['id']) || ( isset($_GET['id']) && !preg_match('#^([0-9]+)$#', $_GET['id']) )) { echo '<p>Invalid comment ID</p>'; break; }
@@ -107,16 +107,16 @@
           break;
         case 'savecomment':
           if(empty($_POST['subj']) || empty($_POST['text'])) { echo '<p>Invalid request</p>'; break; }
-          $r = PageUtils::savecomment_neater($paths->cpage['urlname_nons'], $paths->namespace, $_POST['subj'], $_POST['text'], (int)$_POST['id']);
+          $r = PageUtils::savecomment_neater($paths->page_id, $paths->namespace, $_POST['subj'], $_POST['text'], (int)$_POST['id']);
           if($r != 'good') { echo "<pre>$r</pre>"; break; }
-          echo PageUtils::comments_html($paths->cpage['urlname_nons'], $paths->namespace);
+          echo PageUtils::comments_html($paths->page_id, $paths->namespace);
           break;
         case 'deletecomment':
           if(!empty($_GET['id']))
           {
-            PageUtils::deletecomment_neater($paths->cpage['urlname_nons'], $paths->namespace, (int)$_GET['id']);
+            PageUtils::deletecomment_neater($paths->page_id, $paths->namespace, (int)$_GET['id']);
           }
-          echo PageUtils::comments_html($paths->cpage['urlname_nons'], $paths->namespace);
+          echo PageUtils::comments_html($paths->page_id, $paths->namespace);
           break;
       }
       $template->footer();
@@ -129,7 +129,7 @@
       }
       if(isset($_POST['_save']))
       {
-        $e = PageUtils::savepage($paths->cpage['urlname_nons'], $paths->namespace, $_POST['page_text'], $_POST['edit_summary'], isset($_POST['minor']));
+        $e = PageUtils::savepage($paths->page_id, $paths->namespace, $_POST['page_text'], $_POST['edit_summary'], isset($_POST['minor']));
         if ( $e == 'good' )
         {
           redirect(makeUrl($paths->page), 'Changes saved', 'Your changes to this page have been saved. Redirecting...', 3);
@@ -141,7 +141,7 @@
         $text = $_POST['page_text'];
         echo PageUtils::genPreview($_POST['page_text']);
       }
-      else $text = RenderMan::getPage($paths->cpage['urlname_nons'], $paths->namespace, 0, false, false, false, false);
+      else $text = RenderMan::getPage($paths->page_id, $paths->namespace, 0, false, false, false, false);
       echo '
         <form action="'.makeUrl($paths->page, 'do=edit').'" method="post" enctype="multipart/form-data">
         <br />
@@ -166,7 +166,7 @@
       break;
     case 'viewsource':
       $template->header();
-      $text = RenderMan::getPage($paths->cpage['urlname_nons'], $paths->namespace, 0, false, false, false, false);
+      $text = RenderMan::getPage($paths->page_id, $paths->namespace, 0, false, false, false, false);
       echo '
         <form action="'.makeUrl($paths->page, 'do=edit').'" method="post">
         <br />
@@ -178,7 +178,7 @@
       $template->footer();
       break;
     case 'history':
-      $hist = PageUtils::histlist($paths->cpage['urlname_nons'], $paths->namespace);
+      $hist = PageUtils::histlist($paths->page_id, $paths->namespace);
       $template->header();
       echo $hist;
       $template->footer();
@@ -195,7 +195,7 @@
       if(isset($_POST['__enanoSaveButton']))
       {
         unset($_POST['__enanoSaveButton']);
-        $val = PageUtils::catsave($paths->cpage['urlname_nons'], $paths->namespace, $_POST);
+        $val = PageUtils::catsave($paths->page_id, $paths->namespace, $_POST);
         if($val == 'GOOD')
         {
           header('Location: '.makeUrl($paths->page)); echo '<html><head><title>Redirecting...</title></head><body>If you haven\'t been redirected yet, <a href="'.makeUrl($paths->page).'">click here</a>.'; break;
@@ -208,7 +208,7 @@
         header('Location: '.makeUrl($paths->page)); echo '<html><head><title>Redirecting...</title></head><body>If you haven\'t been redirected yet, <a href="'.makeUrl($paths->page).'">click here</a>.'; break;
       }
       $template->header();
-      $c = PageUtils::catedit_raw($paths->cpage['urlname_nons'], $paths->namespace);
+      $c = PageUtils::catedit_raw($paths->page_id, $paths->namespace);
       echo $c[1];
       $template->footer();
       break;
@@ -222,7 +222,7 @@
       if(!empty($_POST['reason']))
       {
         if(!preg_match('#^([0-2]*){1}$#', $_POST['level'])) die_friendly('Error protecting page', '<p>Request validation failed</p>');
-        PageUtils::protect($paths->cpage['urlname_nons'], $paths->namespace, intval($_POST['level']), $_POST['reason']);
+        PageUtils::protect($paths->page_id, $paths->namespace, intval($_POST['level']), $_POST['reason']);
         die_friendly('Page protected', '<p>The protection setting has been applied. <a href="'.makeUrl($paths->page).'">Return to the page</a>.</p>');
       }
       $template->header();
@@ -256,7 +256,7 @@
     case 'rename':
       if(!empty($_POST['newname']))
       {
-        $r = PageUtils::rename($paths->cpage['urlname_nons'], $paths->namespace, $_POST['newname']);
+        $r = PageUtils::rename($paths->page_id, $paths->namespace, $_POST['newname']);
         die_friendly('Page renamed', '<p>'.nl2br($r).' <a href="'.makeUrl($paths->page).'">Return to the page</a>.</p>');
       }
       $template->header();
@@ -275,7 +275,7 @@
       if(isset($_POST['_downthejohn']))
       {
         $template->header();
-          $result = PageUtils::flushlogs($paths->cpage['urlname_nons'], $paths->namespace);
+          $result = PageUtils::flushlogs($paths->page_id, $paths->namespace);
           echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">Return to the page</a>.</p>';
         $template->footer();
         break;
@@ -296,7 +296,7 @@
       if(isset($_POST['_ballotbox']))
       {
         $template->header();
-        $result = PageUtils::delvote($paths->cpage['urlname_nons'], $paths->namespace);
+        $result = PageUtils::delvote($paths->page_id, $paths->namespace);
         echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">Return to the page</a>.</p>';
         $template->footer();
         break;
@@ -320,7 +320,7 @@
       if(isset($_POST['_youmaylivealittlelonger']))
       {
         $template->header();
-          $result = PageUtils::resetdelvotes($paths->cpage['urlname_nons'], $paths->namespace);
+          $result = PageUtils::resetdelvotes($paths->page_id, $paths->namespace);
           echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">Return to the page</a>.</p>';
         $template->footer();
         break;
@@ -344,7 +344,7 @@
         else
         {
           $template->header();
-            $result = PageUtils::deletepage($paths->cpage['urlname_nons'], $paths->namespace, $reason);
+            $result = PageUtils::deletepage($paths->page_id, $paths->namespace, $reason);
             echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">Return to the page</a>.</p>';
           $template->footer();
           break;
@@ -373,7 +373,7 @@
         {
           die_friendly('Invalid request', '<p>Level not specified</p>');
         }
-        $q = $db->sql_query('UPDATE '.table_prefix.'pages SET wiki_mode=' . $level . ' WHERE urlname=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND namespace=\'' . $paths->namespace . '\';');
+        $q = $db->sql_query('UPDATE '.table_prefix.'pages SET wiki_mode=' . $level . ' WHERE urlname=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $paths->namespace . '\';');
         if ( !$q )
           $db->_die();
         redirect(makeUrl($paths->page), htmlspecialchars($paths->cpage['name']), 'Wiki mode for this page has been set. Redirecting you to the page...', 2);
@@ -413,7 +413,7 @@
       if(!$id1 || !$id2) { echo '<p>Invalid request.</p>'; $template->footer(); break; }
       if(!preg_match('#^([0-9]+)$#', (string)$_GET['diff1']) ||
          !preg_match('#^([0-9]+)$#', (string)$_GET['diff2']  )) { echo '<p>SQL injection attempt</p>'; $template->footer(); break; }
-      echo PageUtils::pagediff($paths->cpage['urlname_nons'], $paths->namespace, $id1, $id2);
+      echo PageUtils::pagediff($paths->page_id, $paths->namespace, $id1, $id2);
       $template->footer();
       break;
     case 'detag':
@@ -425,7 +425,7 @@
       {
         die_friendly('Invalid request', '<p>The detag action is only valid for pages that have been deleted in the past.</p>');
       }
-      $q = $db->sql_query('DELETE FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND namespace=\'' . $paths->namespace . '\';');
+      $q = $db->sql_query('DELETE FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $paths->namespace . '\';');
       if ( !$q )
         $db->_die('Detag query, index.php:'.__LINE__);
       die_friendly('Page detagged', '<p>All stale tags have been removed from this page.</p>');
@@ -436,7 +436,7 @@
       break;
     case 'sql_report':
       $rev_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
-      $page = new PageProcessor( $paths->cpage['urlname_nons'], $paths->namespace, $rev_id );
+      $page = new PageProcessor( $paths->page_id, $paths->namespace, $rev_id );
       $page->send_headers = true;
       $pagepass = ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '';
       $page->password = $pagepass;
--- a/install.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/install.php	Tue Dec 18 23:44:55 2007 -0500
@@ -41,6 +41,8 @@
 global $_starttime, $this_page, $sideinfo;
 $_starttime = microtime(true);
 
+global $db;
+
 // Determine directory (special case for development servers)
 if ( strpos(__FILE__, '/repo/') && file_exists('.enanodev') )
 {
@@ -176,6 +178,9 @@
 
 function stg_mysql_connect($act_get = false)
 {
+  global $db;
+  $db = new mysql();
+  
   static $conn = false;
   if ( $act_get )
     return $conn;
@@ -300,15 +305,114 @@
       return false;
     }
   }
+  // initialize DBAL
+  $db->connect(true, $_POST['db_host'], $db_user, $db_pass, $db_name);
+  // connected and database exists
+  return true;
+}
+
+function stg_pgsql_connect($act_get = false)
+{
+  global $db;
+  $db = new postgresql();
+  
+  static $conn = false;
+  if ( $act_get )
+    return $conn;
+  
+  $db_user =& $_POST['db_user'];
+  $db_pass =& $_POST['db_pass'];
+  $db_name =& $_POST['db_name'];
+  
+  if ( !preg_match('/^[a-z0-9_-]+$/', $db_name) )
+  {
+    $db_name = htmlspecialchars($db_name);
+    die("<p>SECURITY: malformed database name \"$db_name\"</p>");
+  }
+  
+  // First, try to connect using the normal credentials
+  $conn = @pg_connect("host={$_POST['db_host']} port=5432 user={$_POST['db_user']} password={$_POST['db_pass']}");
+  if ( !$conn )
+  {
+    // Connection failed. Do we have the root username and password?
+    if ( !empty($_POST['db_root_user']) && !empty($_POST['db_root_pass']) )
+    {
+      $conn_root = @pg_connect("host={$_POST['db_host']} port=5432 user={$_POST['db_root_user']} password={$_POST['db_root_pass']}");
+      if ( !$conn_root )
+      {
+        // Couldn't connect using either set of credentials. Bail out.
+        return false;
+      }
+      unset($db_user, $db_pass);
+      $db_user = pg_escape_string($_POST['db_user']);
+      $db_pass = pg_escape_string($_POST['db_pass']);
+      // Create the user account
+      $q = @pg_query("CREATE ROLE '$db_user' WITH NOSUPERUSER UNENCRYPTED PASSWORD '$db_pass';", $conn_root);
+      if ( !$q )
+      {
+        return false;
+      }
+      pg_close($conn_root);
+      $conn = @pg_connect("host={$_POST['db_host']} port=5432 user={$_POST['db_user']} password={$_POST['db_pass']}");
+      if ( !$conn )
+      {
+        // This should honestly never happen.
+        return false;
+      }
+    }
+  }
+  if ( !$q )
+  {
+    // access denied to the database; try the whole root schenanegan again
+    if ( !empty($_POST['db_root_user']) && !empty($_POST['db_root_pass']) )
+    {
+      $conn_root = @pg_connect("host={$_POST['db_host']} port=5432 user={$_POST['db_root_user']} password={$_POST['db_root_pass']}");
+      if ( !$conn_root )
+      {
+        // Couldn't connect as root; bail out
+        return false;
+      }
+      unset($db_user, $db_pass);
+      $db_user = pg_escape_string($_POST['db_user']);
+      $db_pass = pg_escape_string($_POST['db_pass']);
+      // create the database, if it doesn't exist
+      $q = @mysql_query("CREATE DATABASE $db_name WITH OWNER $db_user;", $conn_root);
+      if ( !$q )
+      {
+        // this really should never fail, so don't give any tolerance to it
+        return false;
+      }
+      // Setting the owner to $db_user should grant all the rights we need
+      pg_close($conn_root);
+      // grant tables have hopefully been flushed, kill and reconnect our regular user connection
+      pg_close($conn);
+      $conn = @pg_connect("host={$_POST['db_host']} port=5432 user={$_POST['db_user']} password={$_POST['db_pass']}");
+      if ( !$conn )
+      {
+        return false;
+      }
+    }
+    else
+    {
+      return false;
+    }
+    // try again
+    $q = @mysql_query("USE `$db_name`;", $conn);
+    if ( !$q )
+    {
+      // really failed this time; bail out
+      return false;
+    }
+  }
+  // initialize DBAL
+  $db->connect(true, $_POST['db_host'], $db_user, $db_pass, $db_name);
   // connected and database exists
   return true;
 }
 
 function stg_drop_tables()
 {
-  $conn = stg_mysql_connect(true);
-  if ( !$conn )
-    return false;
+  global $db;
   // Our list of tables included in Enano
   $tables = Array( 'categories', 'comments', 'config', 'logs', 'page_text', 'session_keys', 'pages', 'users', 'users_extra', 'themes', 'buddies', 'banlist', 'files', 'privmsgs', 'sidebar', 'hits', 'search_index', 'groups', 'group_members', 'acl', 'tags', 'page_groups', 'page_group_members' );
   
@@ -318,7 +422,7 @@
   {
     // Remember that table_prefix is sanitized.
     $table = "{$_POST['table_prefix']}$table";
-    @mysql_query("DROP TABLE $table;", $conn);
+    $db->sql_query("DROP TABLE $table;", $conn);
   }
   return true;
 }
@@ -370,6 +474,8 @@
   if ( $act_get )
     return $schema;
   
+  global $db;
+  
   $admin_pass = stg_decrypt_admin_pass(true);
   $key = stg_generate_aes_key(true);
   $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
@@ -380,16 +486,29 @@
   
   $admin_user = $_POST['admin_user'];
   $admin_user = str_replace('_', ' ', $admin_user);
-  $admin_user = mysql_real_escape_string($admin_user);
+  $admin_user = $db->escape($admin_user);
+  
+  switch ( $_POST['db_driver'] )
+  {
+    case 'mysql':
+      $schema_file = 'schema.sql';
+      break;
+    case 'postgresql':
+      $schema_file = 'schema-pg.sql';
+      break;
+  }
   
-  $schema = file_get_contents('schema.sql');
-  $schema = str_replace('{{SITE_NAME}}',    mysql_real_escape_string($_POST['sitename']   ), $schema);
-  $schema = str_replace('{{SITE_DESC}}',    mysql_real_escape_string($_POST['sitedesc']   ), $schema);
-  $schema = str_replace('{{COPYRIGHT}}',    mysql_real_escape_string($_POST['copyright']  ), $schema);
+  if ( !isset($schema_file) )
+    die('insanity');
+  
+  $schema = file_get_contents($schema_file);
+  $schema = str_replace('{{SITE_NAME}}',    $db->escape($_POST['sitename']   ), $schema);
+  $schema = str_replace('{{SITE_DESC}}',    $db->escape($_POST['sitedesc']   ), $schema);
+  $schema = str_replace('{{COPYRIGHT}}',    $db->escape($_POST['copyright']  ), $schema);
   $schema = str_replace('{{ADMIN_USER}}',   $admin_user                                    , $schema);
-  $schema = str_replace('{{ADMIN_PASS}}',   mysql_real_escape_string($admin_pass          ), $schema);
-  $schema = str_replace('{{ADMIN_EMAIL}}',  mysql_real_escape_string($_POST['admin_email']), $schema);
-  $schema = str_replace('{{ENABLE_CACHE}}', mysql_real_escape_string($cacheonoff          ), $schema);
+  $schema = str_replace('{{ADMIN_PASS}}',   $db->escape($admin_pass          ), $schema);
+  $schema = str_replace('{{ADMIN_EMAIL}}',  $db->escape($_POST['admin_email']), $schema);
+  $schema = str_replace('{{ENABLE_CACHE}}', $db->escape($cacheonoff          ), $schema);
   $schema = str_replace('{{REAL_NAME}}',    '',                                              $schema);
   $schema = str_replace('{{TABLE_PREFIX}}', $_POST['table_prefix'],                          $schema);
   $schema = str_replace('{{VERSION}}',      ENANO_VERSION,                                   $schema);
@@ -670,14 +789,14 @@
   if($val)
   {
     if($cv) $color='CCFFCC'; else $color='AAFFAA';
-    echo "<tr><td style='background-color: #$color; width: 500px;'>$desc</td><td style='padding-left: 10px;'><img alt='Test passed' src='images/good.gif' /></td></tr>";
+    echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc</td><td style='padding-left: 10px;'><img alt='Test passed' src='images/good.gif' /></td></tr>";
   } elseif(!$val && $warn) {
     if($cv) $color='FFFFCC'; else $color='FFFFAA';
-    echo "<tr><td style='background-color: #$color; width: 500px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test passed with warning' src='images/unknown.gif' /></td></tr>";
+    echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test passed with warning' src='images/unknown.gif' /></td></tr>";
     $warned = true;
   } else {
     if($cv) $color='FFCCCC'; else $color='FFAAAA';
-    echo "<tr><td style='background-color: #$color; width: 500px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test failed' src='images/bad.gif' /></td></tr>";
+    echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test failed' src='images/bad.gif' /></td></tr>";
     $failed = true;
   }
 }
@@ -814,6 +933,78 @@
     mysql_close($conn);
     die('good');
     break;
+  case 'pgsql_test':
+    error_reporting(0);
+    $dbhost     = rawurldecode($_POST['host']);
+    $dbname     = rawurldecode($_POST['name']);
+    $dbuser     = rawurldecode($_POST['user']);
+    $dbpass     = rawurldecode($_POST['pass']);
+    $dbrootuser = rawurldecode($_POST['root_user']);
+    $dbrootpass = rawurldecode($_POST['root_pass']);
+    if($dbrootuser != '')
+    {
+      $conn = @pg_connect("host=$dbhost port=5432 user=$dbuser password=$dbpass dbname=$dbname");
+      if(!$conn)
+      {
+        $e = pg_last_error();
+        if(strstr($e, "Lost connection"))
+          die('host'.$e);
+        else
+          die('root'.$e);
+      }
+      $rsp = 'good';
+      $q = mysql_query('USE `' . mysql_real_escape_string($dbname) . '`;', $conn);
+      if(!$q)
+      {
+        $e = mysql_error();
+        if(strstr($e, 'Unknown database'))
+        {
+          $rsp .= '_creating_db';
+        }
+      }
+      mysql_close($conn);
+      $conn = mysql_connect($dbhost, $dbuser, $dbpass);
+      if(!$conn)
+      {
+        $e = mysql_error();
+        if(strstr($e, "Lost connection"))
+          die('host'.$e);
+        else
+          $rsp .= '_creating_user';
+      }
+      mysql_close($conn);
+      die($rsp);
+    }
+    else
+    {
+      $conn = mysql_connect($dbhost, $dbuser, $dbpass);
+      if(!$conn)
+      {
+        $e = mysql_error();
+        if(strstr($e, "Lost connection"))
+          die('host'.$e);
+        else
+          die('auth'.$e);
+      }
+      $q = mysql_query('USE `' . mysql_real_escape_string($dbname) . '`;', $conn);
+      if(!$q)
+      {
+        $e = mysql_error();
+        if(strstr($e, 'Unknown database'))
+        {
+          die('name'.$e);
+        }
+        else
+        {
+          die('perm'.$e);
+        }
+      }
+    }
+    $v = mysql_get_server_info();
+    if(version_compare($v, '4.1.17', '<')) die('vers'.$v);
+    mysql_close($conn);
+    die('good');
+    break;  
   case 'pophelp':
     $topic = ( isset($_GET['topic']) ) ? $_GET['topic'] : 'invalid';
     switch($topic)
@@ -899,7 +1090,9 @@
               'welcome' => 'Welcome',
               'license' => 'License Agreement',
               'sysreqs' => 'Server requirements',
-              'database'=> 'Database information',
+              'database' => 'Select database driver',
+              'database_mysql'=> 'Database information',
+              'database_pgsql'=> 'Database information',
               'website' => 'Website configuration',
               'login'   => 'Administration login',
               'confirm' => 'Confirm installation',
@@ -999,7 +1192,8 @@
     <?php
     run_test('return version_compare(\'4.3.0\', PHP_VERSION, \'<\');', 'PHP Version >=4.3.0', 'It seems that the version of PHP that your server is running is too old to support Enano properly. If this is your server, please upgrade to the most recent version of PHP, remembering to use the --with-mysql configure option if you compile it yourself. If this is not your server, please contact your webhost and ask them if it would be possible to upgrade PHP. If this is not possible, you will need to switch to a different webhost in order to use Enano.');
     run_test('return version_compare(\'5.2.0\', PHP_VERSION, \'<\');', 'PHP 5.2.0 or later', 'Your server does not have support for PHP 5.2.0. While you may continue installing Enano, please be warned that as of December 31, 2007, all support for Enano on PHP 4 servers is discontinued. If you have at least PHP 5.0.0, support will still be available, but there are many security problems in PHP versions under 5.2.0 that Enano cannot effectively prevent.', true);
-    run_test('return function_exists(\'mysql_connect\');', 'MySQL extension for PHP', 'It seems that your PHP installation does not have the MySQL extension enabled. If this is your own server, you may need to just enable the "libmysql.so" extension in php.ini. If you do not have the MySQL extension installed, you will need to either use your distribution\'s package manager to install it, or you will have to compile PHP from source. If you compile PHP from source, please remember to use the "--with-mysql" configure option, and you will have to have the MySQL development files installed (they usually are). If this is not your server, please contact your hosting company and ask them to install the PHP MySQL extension.');
+    run_test('return function_exists(\'mysql_connect\');', 'MySQL extension for PHP', 'It seems that your PHP installation does not have the MySQL extension enabled. The MySQL database driver will be unavailable. In many cases this is OK if you have another supported database type available. If this is your own server, you may need to just enable the "libmysql.so" extension in php.ini. If you do not have the MySQL extension installed, you will need to either use your distribution\'s package manager to install it, or you will have to compile PHP from source. If you compile PHP from source, please remember to use the "--with-mysql" configure option, and you will have to have the MySQL development files installed (they usually are). If this is not your server, please contact your hosting company and ask them to install the PHP MySQL extension.', true);
+    run_test('return function_exists(\'pg_connect\');', 'PostgreSQL extension for PHP', 'It seems that your PHP installation does not have the PostgreSQL extension enabled. Because of this, you won\'t be able to use the PostgreSQL database driver. This is OK in the majority of cases. If you want to use PostgreSQL support, you\'ll need to either compile the PHP extension for Postgres or install the extension with your distribution\'s package manager. Windows administrators will need enable php_pgsql.dll in their php.ini.', true);
     run_test('return @ini_get(\'file_uploads\');', 'File upload support', 'It seems that your server does not support uploading files. Enano *requires* this functionality in order to work properly. Please ask your server administrator to set the "file_uploads" option in php.ini to "On".');
     run_test('return is_apache();', 'Apache HTTP Server', 'Apparently your server is running a web server other than Apache. Enano will work nontheless, but there are some known bugs with non-Apache servers, and the "fancy" URLs will not work properly. The "Standard URLs" option will be set on the website configuration page, only change it if you are absolutely certain that your server is running Apache.', true);
     //run_test('return function_exists(\'finfo_file\');', 'Fileinfo PECL extension', 'The MIME magic PHP extension is used to determine the type of a file by looking for a certain "magic" string of characters inside it. This functionality is used by Enano to more effectively prevent malicious file uploads. The MIME magic option will be disabled by default.', true);
@@ -1007,6 +1201,10 @@
     run_test('return file_exists(\'/usr/bin/convert\');', 'ImageMagick support', 'Enano uses ImageMagick to scale images into thumbnails. Because ImageMagick was not found on your server, Enano will use the width= and height= attributes on the &lt;img&gt; tag to scale images. This can cause somewhat of a performance increase, but bandwidth usage will be higher, especially if you use high-resolution images on your site.<br /><br />If you are sure that you have ImageMagick, you can set the location of the "convert" program using the administration panel after installation is complete.', true);
     run_test('return is_writable(ENANO_ROOT.\'/cache/\');', 'Cache directory writable', 'Apparently the cache/ directory is not writable. Enano will still work, but you will not be able to cache thumbnails, meaning the server will need to re-render them each time they are requested. In some cases, this can cause a significant slowdown.', true);
     run_test('return is_writable(ENANO_ROOT.\'/files/\');', 'File uploads directory writable', 'It seems that the directory where uploaded files are stored (' . ENANO_ROOT . '/files) cannot be written by the server. Enano will still function, but file uploads will not function, and will be disabled by default.', true);
+    if ( !function_exists('mysql_connect') && !function_exists('pg_connect') )
+    {
+      run_test('return false;', 'No database drivers are available.', 'You need to have at least one database driver working to install Enano. See the warnings on MySQL and PostgreSQL above for more information on installing these database drivers.', false);
+    }
     echo '</table>';
     if(!$failed)
     {
@@ -1044,6 +1242,76 @@
     <?php
     break;
   case "database":
+    echo '<h3>Choose a database driver</h3>';
+    echo '<p>The next step is to choose the database driver that Enano will use. In most cases this is MySQL, but there are certain
+             advantages to PostgreSQL, which is made available only experimentally.</p>';
+    if ( @file_exists('/etc/enano-is-virt-appliance') )
+    {
+      echo '<p><b>You\'re using the Enano virtual appliance.</b><br />Unless you configured the appliance manually, PostgreSQL support is not available. In 99% of cases you\'ll want to click MySQL below.</p>';
+    }
+    
+    $mysql_disable_reason = '';
+    $pgsql_disable_reason = '';
+    $mysql_disable = '';
+    $pgsql_disable = '';
+    if ( !function_exists('mysql_connect') )
+    {
+      $mysql_disable = ' disabled="disabled"';
+      $mysql_disable_reason = 'You don\'t have the MySQL PHP extension installed.';
+    }
+    if ( !function_exists('pg_connect') )
+    {
+      $pgsql_disable = ' disabled="disabled"';
+      $pgsql_disable_reason = 'You don\'t have the PostgreSQL PHP extensnion installed.';
+    }
+    if ( function_exists('pg_connect') && version_compare(PHP_VERSION, '5.0.0', '<') )
+    {
+      $pgsql_disable = ' disabled="disabled"';
+      $pgsql_disable_reason = 'You need to have at least PHP 5 to use the PostgreSQL database driver.';
+    }
+    
+    echo '<form action="install.php" method="get">';
+    ?>
+    <table border="0" cellspacing="5">
+      <tr>
+        <td>
+          <input type="image" name="mode" value="database_mysql" src="images/about-powered-mysql.png"<?php echo $mysql_disable; ?>/>
+        </td>
+        <td<?php if ( $mysql_disable ) echo ' style="opacity: 0.5; filter: alpha(opacity=50);"'; ?>>
+          <b>MySQL</b><br />
+          Click this button to use MySQL as the database backend for your site. Most web hosts support MySQL, and if you have
+          administrative access to your MySQL server, you can create a new database and user during this installation process if you
+          haven't done so already.
+          <?php
+          if ( $mysql_disable )
+          {
+            echo "<br /><br /><b>$mysql_disable_reason</b>";
+          }
+          ?>
+        </td>
+      </tr>
+      <tr>
+        <td>
+          <input type="image" name="mode" value="database_pgsql" src="images/about-powered-pgsql.png"<?php echo $pgsql_disable; ?> />
+        </td>
+        <td<?php if ( $pgsql_disable ) echo ' style="opacity: 0.5; filter: alpha(opacity=50);"'; ?>>
+          <b>PostgreSQL</b><br />
+          Click this button to use PostgreSQL as the database backend for your site. While not as widely supported, PostgreSQL has more
+          liberal licensing conditions and when properly configured is faster than MySQL. Some plugins may not work with the PostgreSQL
+          driver.
+          <?php
+          if ( $pgsql_disable )
+          {
+            echo "<br /><br /><b>$pgsql_disable_reason</b>";
+          }
+          ?>
+        </td>
+      </tr>
+    </table>
+    <?php
+    echo '</form>';
+    break;
+  case "database_mysql":
     ?>
     <script type="text/javascript">
       function ajaxGet(uri, f) {
@@ -1251,6 +1519,7 @@
     }
     ?>
     <form name="dbinfo" action="install.php?mode=website" method="post">
+      <input type="hidden" name="db_driver" value="mysql" />
       <table border="0">
         <tr><td colspan="3" style="text-align: center"><h3>Database information</h3></td></tr>
         <tr><td><b>Database hostname</b><br />This is the hostname (or sometimes the IP address) of your MySQL server. In many cases, this is "localhost".<br /><span style="color: #993300" id="e_db_host"></span></td><td><input onkeyup="verify();" name="db_host" size="30" type="text" /></td><td><img id="s_db_host" alt="Good/bad icon" src="images/bad.gif" /></td></tr>
@@ -1275,6 +1544,231 @@
     </form>
     <?php
     break;
+  case "database_pgsql":
+    ?>
+    <script type="text/javascript">
+      function ajaxGet(uri, f) {
+        if (window.XMLHttpRequest) {
+          ajax = new XMLHttpRequest();
+        } else {
+          if (window.ActiveXObject) {           
+            ajax = new ActiveXObject("Microsoft.XMLHTTP");
+          } else {
+            alert('Enano client-side runtime error: No AJAX support, unable to continue');
+            return;
+          }
+        }
+        ajax.onreadystatechange = f;
+        ajax.open('GET', uri, true);
+        ajax.send(null);
+      }
+      
+      function ajaxPost(uri, parms, f) {
+        if (window.XMLHttpRequest) {
+          ajax = new XMLHttpRequest();
+        } else {
+          if (window.ActiveXObject) {           
+            ajax = new ActiveXObject("Microsoft.XMLHTTP");
+          } else {
+            alert('Enano client-side runtime error: No AJAX support, unable to continue');
+            return;
+          }
+        }
+        ajax.onreadystatechange = f;
+        ajax.open('POST', uri, true);
+        ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+        ajax.setRequestHeader("Content-length", parms.length);
+        ajax.setRequestHeader("Connection", "close");
+        ajax.send(parms);
+      }
+      function ajaxTestConnection()
+      {
+        v = verify();
+        if(!v)
+        {
+          alert('One or more of the form fields is incorrect. Please correct any information in the form that has an "X" next to it.');
+          return false;
+        }
+        var frm = document.forms.dbinfo;
+        db_host      = escape(frm.db_host.value.replace('+', '%2B'));
+        db_name      = escape(frm.db_name.value.replace('+', '%2B'));
+        db_user      = escape(frm.db_user.value.replace('+', '%2B'));
+        db_pass      = escape(frm.db_pass.value.replace('+', '%2B'));
+        db_root_user = escape(frm.db_root_user.value.replace('+', '%2B'));
+        db_root_pass = escape(frm.db_root_pass.value.replace('+', '%2B'));
+        
+        parms = 'host='+db_host+'&name='+db_name+'&user='+db_user+'&pass='+db_pass+'&root_user='+db_root_user+'&root_pass='+db_root_pass;
+        ajaxPost('<?php echo scriptPath; ?>/install.php?mode=pgsql_test', parms, function() {
+            if(ajax.readyState==4)
+            {
+              s = ajax.responseText.substr(0, 4);
+              t = ajax.responseText.substr(4, ajax.responseText.length);
+              if(s.substr(0, 4)=='good')
+              {
+                document.getElementById('s_db_host').src='images/good.gif';
+                document.getElementById('s_db_name').src='images/good.gif';
+                document.getElementById('s_db_auth').src='images/good.gif';
+                document.getElementById('s_db_root').src='images/good.gif';
+                if(t.match(/_creating_db/)) document.getElementById('e_db_name').innerHTML = '<b>Warning:<\/b> The database you specified does not exist. It will be created during installation.';
+                if(t.match(/_creating_user/)) document.getElementById('e_db_auth').innerHTML = '<b>Warning:<\/b> The specified regular user does not exist or the password is incorrect. The user will be created during installation. If the user already exists, the password will be reset.';
+                document.getElementById('s_mysql_version').src='images/good.gif';
+                document.getElementById('e_mysql_version').innerHTML = 'Your version of PostgreSQL meets Enano requirements.';
+              }
+              else
+              {
+                switch(s)
+                {
+                case 'host':
+                  document.getElementById('s_db_host').src='images/bad.gif';
+                  document.getElementById('s_db_name').src='images/unknown.gif';
+                  document.getElementById('s_db_auth').src='images/unknown.gif';
+                  document.getElementById('s_db_root').src='images/unknown.gif';
+                  document.getElementById('e_db_host').innerHTML = '<b>Error:<\/b> The database server "'+document.forms.dbinfo.db_host.value+'" couldn\'t be contacted.<br \/>'+t;
+                  document.getElementById('e_mysql_version').innerHTML = 'The MySQL version that your server is running could not be determined.';
+                  break;
+                case 'auth':
+                  document.getElementById('s_db_host').src='images/good.gif';
+                  document.getElementById('s_db_name').src='images/unknown.gif';
+                  document.getElementById('s_db_auth').src='images/bad.gif';
+                  document.getElementById('s_db_root').src='images/unknown.gif';
+                  document.getElementById('e_db_auth').innerHTML = '<b>Error:<\/b> Access to MySQL under the specified credentials was denied.<br \/>'+t;
+                  document.getElementById('e_mysql_version').innerHTML = 'The MySQL version that your server is running could not be determined.';
+                  break;
+                case 'perm':
+                  document.getElementById('s_db_host').src='images/good.gif';
+                  document.getElementById('s_db_name').src='images/bad.gif';
+                  document.getElementById('s_db_auth').src='images/good.gif';
+                  document.getElementById('s_db_root').src='images/unknown.gif';
+                  document.getElementById('e_db_name').innerHTML = '<b>Error:<\/b> Access to the specified database using those login credentials was denied.<br \/>'+t;
+                  document.getElementById('e_mysql_version').innerHTML = 'The MySQL version that your server is running could not be determined.';
+                  break;
+                case 'name':
+                  document.getElementById('s_db_host').src='images/good.gif';
+                  document.getElementById('s_db_name').src='images/bad.gif';
+                  document.getElementById('s_db_auth').src='images/good.gif';
+                  document.getElementById('s_db_root').src='images/unknown.gif';
+                  document.getElementById('e_db_name').innerHTML = '<b>Error:<\/b> The specified database does not exist<br \/>'+t;
+                  document.getElementById('e_mysql_version').innerHTML = 'The MySQL version that your server is running could not be determined.';
+                  break;
+                case 'root':
+                  document.getElementById('s_db_host').src='images/good.gif';
+                  document.getElementById('s_db_name').src='images/unknown.gif';
+                  document.getElementById('s_db_auth').src='images/unknown.gif';
+                  document.getElementById('s_db_root').src='images/bad.gif';
+                  document.getElementById('e_db_root').innerHTML = '<b>Error:<\/b> Access to MySQL under the specified credentials was denied.<br \/>'+t;
+                  document.getElementById('e_mysql_version').innerHTML = 'The MySQL version that your server is running could not be determined.';
+                  break;
+                case 'vers':
+                  document.getElementById('s_db_host').src='images/good.gif';
+                  document.getElementById('s_db_name').src='images/good.gif';
+                  document.getElementById('s_db_auth').src='images/good.gif';
+                  document.getElementById('s_db_root').src='images/good.gif';
+                  if(t.match(/_creating_db/)) document.getElementById('e_db_name').innerHTML = '<b>Warning:<\/b> The database you specified does not exist. It will be created during installation.';
+                  if(t.match(/_creating_user/)) document.getElementById('e_db_auth').innerHTML = '<b>Warning:<\/b> The specified regular user does not exist or the password is incorrect. The user will be created during installation. If the user already exists, the password will be reset.';
+                  
+                  document.getElementById('e_mysql_version').innerHTML = '<b>Error:<\/b> Your version of MySQL ('+t+') is older than 4.1.17. Enano will still work, but there is a known bug with the comment system and MySQL 4.1.11 that involves some comments not being displayed, due to an issue with the PHP function mysql_fetch_row().';
+                  document.getElementById('s_mysql_version').src='images/bad.gif';
+                default:
+                  alert(t);
+                  break;
+                }
+              }
+            }
+          });
+      }
+      function verify()
+      {
+        document.getElementById('e_db_host').innerHTML = '';
+        document.getElementById('e_db_auth').innerHTML = '';
+        document.getElementById('e_db_name').innerHTML = '';
+        document.getElementById('e_db_root').innerHTML = '';
+        var frm = document.forms.dbinfo;
+        ret = true;
+        if(frm.db_host.value != '')
+        {
+          document.getElementById('s_db_host').src='images/unknown.gif';
+        }
+        else
+        {
+          document.getElementById('s_db_host').src='images/bad.gif';
+          ret = false;
+        }
+        if(frm.db_name.value.match(/^([a-z0-9_-]+)$/g))
+        {
+          document.getElementById('s_db_name').src='images/unknown.gif';
+        }
+        else
+        {
+          document.getElementById('s_db_name').src='images/bad.gif';
+          ret = false;
+        }
+        if(frm.db_user.value != '')
+        {
+          document.getElementById('s_db_auth').src='images/unknown.gif';
+        }
+        else
+        {
+          document.getElementById('s_db_auth').src='images/bad.gif';
+          ret = false;
+        }
+        if(frm.table_prefix.value.match(/^([a-z0-9_]*)$/g))
+        {
+          document.getElementById('s_table_prefix').src='images/good.gif';
+        }
+        else
+        {
+          document.getElementById('s_table_prefix').src='images/bad.gif';
+          ret = false;
+        }
+        if(frm.db_root_user.value == '')
+        {
+          document.getElementById('s_db_root').src='images/good.gif';
+        }
+        else if(frm.db_root_user.value != '' && frm.db_root_pass.value == '')
+        {
+          document.getElementById('s_db_root').src='images/bad.gif';
+          ret = false;
+        }
+        else
+        {
+          document.getElementById('s_db_root').src='images/unknown.gif';
+        }
+        if(ret) frm._cont.disabled = false;
+        else    frm._cont.disabled = true;
+        return ret;
+      }
+      window.onload = verify;
+    </script>
+    <p>Now we need some information that will allow Enano to contact your database server. Enano uses PostgreSQL as a data storage backend,
+       and we need to have access to a PostgreSQL server in order to continue.</p>
+    <p>If you do not have access to a PostgreSQL server, and you are using your own server, you can download PostgreSQL for free from
+       <a href="http://www.postgresql.org/">PostgreSQL.org</a>.</p>
+    <form name="dbinfo" action="install.php?mode=website" method="post">
+      <input type="hidden" name="db_driver" value="postgresql" />
+      <table border="0">
+        <tr><td colspan="3" style="text-align: center"><h3>Database information</h3></td></tr>
+        <tr><td><b>Database hostname</b><br />This is the hostname (or sometimes the IP address) of your Postgres server. In many cases, this is "localhost".<br /><span style="color: #993300" id="e_db_host"></span></td><td><input onkeyup="verify();" name="db_host" size="30" type="text" /></td><td><img id="s_db_host" alt="Good/bad icon" src="images/bad.gif" /></td></tr>
+        <tr><td><b>Database name</b><br />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 superuser.<br /><span style="color: #993300" id="e_db_name"></span></td><td><input onkeyup="verify();" name="db_name" size="30" type="text" /></td><td><img id="s_db_name" alt="Good/bad icon" src="images/bad.gif" /></td></tr>
+        <tr><td rowspan="2"><b>Database login</b><br />These fields should be the username and password for a role that has permission to create and alter tables, select data, insert data, update data, and delete data. You may or may not choose to allow dropping tables.<br /><span style="color: #993300" id="e_db_auth"></span></td><td><input onkeyup="verify();" name="db_user" size="30" type="text" /></td><td rowspan="2"><img id="s_db_auth" alt="Good/bad icon" src="images/bad.gif" /></td></tr>
+        <tr><td><input name="db_pass" size="30" type="password" /></td></tr>
+        <tr><td colspan="3" style="text-align: center"><h3>Optional information</h3></td></tr>
+        <tr><td><b>Table prefix</b><br />The value that you enter here will be added to the beginning of the name of each Enano table. You may use lowercase letters (a-z), numbers (0-9), and underscores (_).</td><td><input onkeyup="verify();" name="table_prefix" size="30" type="text" /></td><td><img id="s_table_prefix" alt="Good/bad icon" src="images/good.gif" /></td></tr>
+        <tr><td rowspan="2"><b>Database administrative login</b><br />If the Postgres database or role that you entered above does not exist yet, you can create them here, assuming that you have the login information for a PostgreSQL superuser. Leave these fields blank unless you need to use them.<br /><span style="color: #993300" id="e_db_root"></span></td><td><input onkeyup="verify();" name="db_root_user" size="30" type="text" /></td><td rowspan="2"><img id="s_db_root" alt="Good/bad icon" src="images/good.gif" /></td></tr>
+        <tr><td><input onkeyup="verify();" name="db_root_pass" size="30" type="password" /></td></tr>
+        <tr><td><b>PostgreSQL version</b></td><td id="e_mysql_version">PostgreSQL version information will<br />be checked when you click "Test<br />Connection". You need to have at<br />least PostgreSQL 8.2.0 to install Enano.</td><td><img id="s_mysql_version" alt="Good/bad icon" src="images/unknown.gif" /></td></tr>
+        <tr><td><b>Delete existing tables?</b><br />If this option is checked, all the tables that will be used by Enano will be dropped (deleted) before the schema is executed. Do NOT use this option unless specifically instructed to.</td><td><input type="checkbox" name="drop_tables" id="dtcheck" />  <label for="dtcheck">Drop existing tables</label></td></tr>
+        <tr><td colspan="3" style="text-align: center"><input type="button" value="Test connection" onclick="ajaxTestConnection();" /></td></tr>
+      </table>
+      <div class="pagenav">
+       <table border="0">
+       <tr>
+       <td><input type="submit" value="Continue" onclick="return verify();" name="_cont" /></td><td><p><span style="font-weight: bold;">Before clicking continue:</span><br />&bull; Check your PostgreSQL connection using the "Test Connection" button.<br />&bull; Be aware that your database information will be transmitted unencrypted several times.</p></td>
+       </tr>
+       </table>
+     </div>
+    </form>
+    <?php
+    break;
   case "website":
     if(!isset($_POST['_cont'])) {
       echo 'No POST data signature found. Please <a href="install.php?mode=sysreqs">restart the installation</a>.';
@@ -1577,6 +2071,7 @@
        !isset($_POST['db_name']) ||
        !isset($_POST['db_user']) ||
        !isset($_POST['db_pass']) ||
+       !isset($_POST['db_driver']) ||
        !isset($_POST['sitename']) ||
        !isset($_POST['sitedesc']) ||
        !isset($_POST['copyright']) ||
@@ -1590,6 +2085,12 @@
       $template->footer();
       exit;
     }
+    if ( !in_array($_POST['db_driver'], array('mysql', 'postgresql')) )
+    {
+      echo 'Invalid database driver.';
+      $template->footer();
+      exit;
+    }
     switch($_POST['urlscheme'])
     {
       case "ugly":
--- a/plugins/SpecialAdmin.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/plugins/SpecialAdmin.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2165,6 +2165,10 @@
     return;
   }
   
+  if ( ENANO_DBLAYER != 'MYSQL' )
+    die('<h3>Not supported</h3>
+          <p>This function is only supported under the MySQL database driver.</p>');
+  
   if(isset($_GET['submitting']) && $_GET['submitting'] == 'yes' && defined('ENANO_DEMO_MODE') )
   {
     redirect(makeUrlComplete('Special', 'Administration'), 'Access denied', 'You\'ve got to be kidding me. Forget it, kid.', 4 );
@@ -2176,13 +2180,11 @@
     
     if(defined('SQL_BACKUP_CRYPT'))
       // Try to increase our time limit
-      @set_time_limit(300); // five minutes
+      @set_time_limit(0);
     // Do the actual export
     $aesext = ( defined('SQL_BACKUP_CRYPT') ) ? '.tea' : '';
     $filename = 'enano_backup_' . date('ymd') . '.sql' . $aesext;
     ob_start();
-    header('Content-disposition: attachment, filename="'.$filename.'";');
-    header('Content-type: application/transact-sql');
     // Spew some headers
     $headdate = date('F d, Y \a\t h:i a');
     echo <<<HEADER
@@ -2212,12 +2214,17 @@
       // THE FOLLOWING COMMENT DOES NOT APPLY AS OF 1.0.
       // Sorry folks - this script CAN'T backup enano_files and enano_search_index due to the sheer size of the tables.
       // If encryption is enabled the log data will be excluded too.
-      echo export_table(
+      $result = export_table(
         $t,
         isset($_POST['do_struct']),
         ( isset($_POST['do_data']) ),
         false
         ) . "\n";
+      if ( !$result )
+      {
+        $db->_die();
+      }
+      echo $result;
     }
     $data = ob_get_contents();
     ob_end_clean();
@@ -2229,6 +2236,8 @@
       $tea = new TEACrypt();
       $data = $tea->encrypt($data, $session->private_key);
     }
+    header('Content-disposition: attachment, filename="'.$filename.'";');
+    header('Content-type: application/transact-sql');
     header('Content-length: '.strlen($data));
     echo $data;
     exit;
@@ -2243,7 +2252,14 @@
     <p>Additional tables to export:</p>
     <p><select name="additional_tables[]" multiple="multiple">
        <?php
-         $q = $db->sql_query('SHOW TABLES;') or $db->_die('Somehow we were denied the request to get the list of tables.');
+         if ( ENANO_DBLAYER == 'MYSQL' )
+         {
+           $q = $db->sql_query('SHOW TABLES;') or $db->_die('Somehow we were denied the request to get the list of tables.');
+         }
+         else if ( ENANO_DBLAYER == 'PGSQL' )
+         {
+           $q = $db->sql_query('SELECT relname FROM pg_stat_user_tables ORDER BY relname;') or $db->_die('Somehow we were denied the request to get the list of tables.');
+         }
          while($row = $db->fetchrow_num())
          {
            if(!in_array($row[0], $system_table_list)) echo '<option value="'.$row[0].'">'.$row[0].'</option>';
--- a/plugins/SpecialGroups.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/plugins/SpecialGroups.php	Tue Dec 18 23:44:55 2007 -0500
@@ -59,14 +59,14 @@
     $db->free_result();
     $members = array();
     $pending = array();
-    $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,m.pending,COUNT(c.comment_id)
+    $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,m.pending,COUNT(c.comment_id) AS num_comments
                            FROM '.table_prefix.'users AS u
                            LEFT JOIN '.table_prefix.'group_members AS m
                              ON ( m.user_id = u.user_id )
                            LEFT JOIN '.table_prefix.'comments AS c
                              ON ( c.name = u.username )
                            WHERE m.group_id=' . $gid . '
-                           GROUP BY u.user_id
+                           GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,m.pending
                            ORDER BY m.is_mod DESC,u.username ASC;');
     if ( !$q )
     {
@@ -200,7 +200,7 @@
             $db->_die('SpecialGroups.php, line ' . __LINE__);
           echo '<div class="info-box">The user "' . $username . '" has been added to this usergroup.</div>';
           
-          $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id)
+          $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id) AS num_comments
                                  FROM '.table_prefix.'users AS u
                                  LEFT JOIN '.table_prefix.'group_members AS m
                                    ON ( m.user_id = u.user_id )
@@ -209,7 +209,7 @@
                                  WHERE m.group_id=' . $gid . '
                                    AND m.pending!=1
                                    AND u.user_id=' . $uid . '
-                                 GROUP BY u.user_id
+                                 GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod
                                  ORDER BY m.is_mod DESC,u.username ASC
                                  LIMIT 1;');
           if ( !$q )
@@ -267,7 +267,7 @@
         $db->_die('SpecialGroups.php, line ' . __LINE__);
       echo '<div class="info-box">You have been added to this group.</div>';
       
-      $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id)
+      $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id) AS num_comments
                              FROM '.table_prefix.'users AS u
                              LEFT JOIN '.table_prefix.'group_members AS m
                                ON ( m.user_id = u.user_id )
@@ -276,7 +276,7 @@
                              WHERE m.group_id=' . $gid . '
                                AND m.pending!=1
                                AND u.user_id=' . $session->user_id . '
-                             GROUP BY u.user_id
+                             GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod
                              ORDER BY m.is_mod DESC,u.username ASC
                              LIMIT 1;');
       if ( !$q )
@@ -370,7 +370,7 @@
                 <td class='{$cls}'>{$member['username']}</td>
                 <td class='{$cls}'>{$addy}</td>
                 <td class='{$cls}'>{$date}</td>
-                <td class='{$cls}'>{$member['COUNT(c.comment_id)']}</td>
+                <td class='{$cls}'>{$member['num_comments']}</td>
                 <td class='{$cls}' style='text-align: center;'><input type='checkbox' name='with_user[{$member['member_id']}]' /></td>
               </tr>";
       }
@@ -418,7 +418,7 @@
               <td class='{$cls}'>{$member['username']}</td>
               <td class='{$cls}'>{$addy}</td>
               <td class='{$cls}'>{$date}</td>
-              <td class='{$cls}'>{$member['COUNT(c.comment_id)']}</td>
+              <td class='{$cls}'>{$member['num_comments']}</td>
               " . ( ( $can_do_admin_stuff ) ? "
               <td class='{$cls}' style='text-align: center;'><input type='checkbox' name='del_user[{$member['member_id']}]' /></td>
               " : '' ) . "
@@ -442,7 +442,7 @@
               <td class='{$cls}'>{$member['username']}</td>
               <td class='{$cls}'>{$addy}</td>
               <td class='{$cls}'>{$date}</td>
-              <td class='{$cls}'>{$member['COUNT(c.comment_id)']}</td>
+              <td class='{$cls}'>{$member['num_comments']}</td>
               " . ( ( $can_do_admin_stuff ) ? "
               <td class='{$cls}' style='text-align: center;'><input type='checkbox' name='del_user[{$member['member_id']}]' /></td>
               " : '' ) . "
--- a/plugins/SpecialPageFuncs.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/plugins/SpecialPageFuncs.php	Tue Dec 18 23:44:55 2007 -0500
@@ -416,9 +416,25 @@
                 </a>
               </td>
               <td style="text-align: center;">
-                <a href="http://www.mysql.com/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
-                  <img alt="Database engine powered by MySQL" src="<?php echo scriptPath; ?>/images/about-powered-mysql.png" style="border-width: 0px;" width="88" height="31" />
-                </a>
+                <?php
+                switch(ENANO_DBLAYER)
+                {
+                  case 'MYSQL':
+                    ?>
+                    <a href="http://www.mysql.com/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
+                      <img alt="Database engine powered by MySQL" src="<?php echo scriptPath; ?>/images/about-powered-mysql.png" style="border-width: 0px;" width="88" height="31" />
+                    </a>
+                    <?php
+                    break;
+                  case 'PGSQL':
+                    ?>
+                    <a href="http://www.postgresql.org/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
+                      <img alt="Database engine powered by PostgreSQL" src="<?php echo scriptPath; ?>/images/about-powered-pgsql.png" style="border-width: 0px;" width="90" height="30" />
+                    </a>
+                    <?php
+                    break;
+                }
+                ?>
               </td>
             </tr>
           </table>
@@ -428,7 +444,23 @@
       <tr><td style="width: 100px;" class="row2">Web server:</td><td class="row2"><?php if(isset($_SERVER['SERVER_SOFTWARE'])) echo $_SERVER['SERVER_SOFTWARE']; else echo 'Unable to determine web server software.'; ?></td></tr>
       <tr><td style="width: 100px;" class="row1">Server platform:</td><td class="row1"><?php echo $platform; ?></td></tr>
       <tr><td style="width: 100px;" class="row2"><a href="http://www.php.net/">PHP</a> version:</td><td class="row2"><?php echo PHP_VERSION; ?></td></tr>
-      <tr><td style="width: 100px;" class="row1"><a href="http://www.mysql.com/">MySQL</a> version:</td><td class="row1"><?php echo mysql_get_server_info($db->_conn); ?></td></tr>
+      <?php
+      switch(ENANO_DBLAYER)
+      {
+        case 'MYSQL':
+          ?>
+          <tr><td style="width: 100px;" class="row1"><a href="http://www.mysql.com/">MySQL</a> version:</td><td class="row1"><?php echo mysql_get_server_info($db->_conn); ?></td></tr>
+          <?php
+          break;
+        case 'PGSQL':
+          $pg_serverdata = pg_version($db->_conn);
+          $pg_version = $pg_serverdata['server'];
+          ?>
+          <tr><td style="width: 100px;" class="row1"><a href="http://www.postgresql.org/">PostgreSQL</a> version:</td><td class="row1"><?php echo $pg_version; ?></td></tr>
+          <?php
+          break;
+      }
+      ?>
     </table>
   </div>
   <?php
--- a/plugins/SpecialUserFuncs.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/plugins/SpecialUserFuncs.php	Tue Dec 18 23:44:55 2007 -0500
@@ -1342,12 +1342,15 @@
                             array('%', '_'),
                             $finduser);
     $finduser = $db->escape($finduser);
-    $username_where = 'u.username LIKE "' . $finduser . '"';
+    $username_where = ENANO_SQLFUNC_LOWERCASE . '(u.username) LIKE \'%' . strtolower($finduser) . '%\'';
     $finduser_url = 'finduser=' . rawurlencode($_GET['finduser']) . '&';
   }
   else
   {
-    $username_where = 'u.username REGEXP "^' . $startletter_sql . '"';
+    if ( ENANO_DBLAYER == 'MYSQL' )
+      $username_where = 'lcase(u.username) REGEXP lcase("^' . $startletter_sql . '")';
+    else if ( ENANO_DBLAYER == 'PGSQL' )
+      $username_where = 'lower(u.username) ~ lower(\'^' . $startletter_sql . '\')';
     $finduser_url = '';
   }
   
@@ -1371,7 +1374,7 @@
                </tr>';
                
   // determine number of rows
-  $q = $db->sql_query('SELECT u.user_id FROM '.table_prefix.'users AS u WHERE ' . $username_where . ' AND u.username != "Anonymous";');
+  $q = $db->sql_query('SELECT u.user_id FROM '.table_prefix.'users AS u WHERE ' . $username_where . ' AND u.username != \'Anonymous\';');
   if ( !$q )
     $db->_die();
   
@@ -1388,7 +1391,7 @@
   $q = $db->sql_unbuffered_query('SELECT u.user_id, u.username, u.reg_time, u.email, u.user_level, u.reg_time, x.email_public FROM '.table_prefix.'users AS u
                                     LEFT JOIN '.table_prefix.'users_extra AS x
                                       ON ( u.user_id = x.user_id )
-                                    WHERE ' . $username_where . ' AND u.username != "Anonymous"
+                                    WHERE ' . $username_where . ' AND u.username != \'Anonymous\'
                                     ORDER BY ' . $sort_sqllet . ' ' . $target_order . ';');
   if ( !$q )
     $db->_die();
@@ -1418,7 +1421,7 @@
               ' .
               '<div style="float: left;">
                 <form action="' . makeUrlNS('Special', 'Memberlist') . '" method="get" onsubmit="if ( !submitAuthorized ) return false;">'
-               . ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->nslist[$paths->namespace] . $paths->cpage['urlname_nons'] ) . '" />' : '' )
+               . ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->page ) . '" />' : '' )
                . ( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : '')
                . '<p>Find a member: ' . $template->username_field('finduser') . ' <input type="submit" value="Go" /><br /><small>You may use the following wildcards: * to match multiple characters, ? to match a single character.</small></p>'
                . '</form>
--- a/plugins/SpecialUserPrefs.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/plugins/SpecialUserPrefs.php	Tue Dec 18 23:44:55 2007 -0500
@@ -47,7 +47,7 @@
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
   
-  if ( $paths->namespace != 'Special' || $paths->cpage['urlname_nons'] != 'Preferences' )
+  if ( $paths->namespace != 'Special' || $paths->page_id != 'Preferences' )
     return false;
   
   $tb .= "<ul>$template->toolbar_menu</ul>";
--- a/plugins/admin/SecurityLog.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/plugins/admin/SecurityLog.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * Copyright (C) 2006-2007 Dan Fuhry
  *
  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
--- a/themes/oxygen/css/bleu.css	Sat Dec 15 18:10:14 2007 -0500
+++ b/themes/oxygen/css/bleu.css	Tue Dec 18 23:44:55 2007 -0500
@@ -246,6 +246,11 @@
   color: #202020;
 }
 
+input[type ^="image"][disabled ^="disabled"] {
+  opacity: 0.5;
+  filter: alpha(opacity=50);
+}
+
 /* JWS window theming */
 div.jswindow                      { border: 2px solid #7090B0; border-top: 5px solid #7090B0; padding: 0px; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; display: none; position: absolute; background-color: #FFFFFF; }
 div.titlebar                      { background-color: #7090B0; color: #FFFFFF; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; padding-bottom: 4px; cursor: default; }
--- a/upgrade.php	Sat Dec 15 18:10:14 2007 -0500
+++ b/upgrade.php	Tue Dec 18 23:44:55 2007 -0500
@@ -2,7 +2,7 @@
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
+ * Version 1.0.3 (Dyrad)
  * upgrade.php - upgrade script
  * Copyright (C) 2006-2007 Dan Fuhry
  *