Restructure repository
authorMarcus Campbell <marcus.campbell@gmail.com>
Tue, 5 Dec 2006 07:58:42 +0000 (07:58 +0000)
committerMarcus Campbell <marcus.campbell@gmail.com>
Tue, 5 Dec 2006 07:58:42 +0000 (07:58 +0000)
150 files changed:
.cvsignore [new file with mode: 0644]
.htaccess [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
about.php [new file with mode: 0644]
ajaxDelete.php [new file with mode: 0644]
ajaxGetTitle.php [new file with mode: 0644]
ajaxIsAvailable.php [new file with mode: 0644]
alltags.php [new file with mode: 0644]
api/.htaccess [new file with mode: 0644]
api/httpauth.inc.php [new file with mode: 0644]
api/posts_add.php [new file with mode: 0644]
api/posts_all.php [new file with mode: 0644]
api/posts_dates.php [new file with mode: 0644]
api/posts_delete.php [new file with mode: 0644]
api/posts_get.php [new file with mode: 0644]
api/posts_recent.php [new file with mode: 0644]
api/posts_update.php [new file with mode: 0644]
api/tags_get.php [new file with mode: 0644]
api/tags_rename.php [new file with mode: 0644]
bg_bar.png [new file with mode: 0644]
bg_header.png [new file with mode: 0644]
bg_sidebar.png [new file with mode: 0644]
bookmarks.php [new file with mode: 0644]
cache/.cvsignore [new file with mode: 0644]
cache/.htaccess [new file with mode: 0644]
config.inc.php.example [new file with mode: 0644]
debug.inc.php [new file with mode: 0644]
edit.php [new file with mode: 0644]
functions.inc.php [new file with mode: 0644]
header.inc.php [new file with mode: 0644]
history.php [new file with mode: 0644]
icon.png [new file with mode: 0644]
import.php [new file with mode: 0644]
importNetscape.php [new file with mode: 0644]
includes/db/db2.php [new file with mode: 0644]
includes/db/firebird.php [new file with mode: 0644]
includes/db/index.htm [new file with mode: 0644]
includes/db/mssql-odbc.php [new file with mode: 0644]
includes/db/mssql.php [new file with mode: 0644]
includes/db/mysql.php [new file with mode: 0644]
includes/db/mysql4.php [new file with mode: 0644]
includes/db/mysqli.php [new file with mode: 0644]
includes/db/oracle.php [new file with mode: 0644]
includes/db/postgres.php [new file with mode: 0644]
includes/db/sqlite.php [new file with mode: 0644]
includes/php-gettext/AUTHORS [new file with mode: 0644]
includes/php-gettext/COPYING [new file with mode: 0644]
includes/php-gettext/ChangeLog [new file with mode: 0644]
includes/php-gettext/Makefile [new file with mode: 0644]
includes/php-gettext/README [new file with mode: 0644]
includes/php-gettext/bin/gettexts.bat [new file with mode: 0644]
includes/php-gettext/examples/index.php [new file with mode: 0644]
includes/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.mo [new file with mode: 0644]
includes/php-gettext/examples/locale/de_CH/LC_MESSAGES/messages.po [new file with mode: 0644]
includes/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo [new file with mode: 0644]
includes/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po [new file with mode: 0644]
includes/php-gettext/examples/pigs_dropin.php [new file with mode: 0644]
includes/php-gettext/examples/pigs_fallback.php [new file with mode: 0644]
includes/php-gettext/examples/update [new file with mode: 0644]
includes/php-gettext/gettext.inc [new file with mode: 0644]
includes/php-gettext/gettext.php [new file with mode: 0644]
includes/php-gettext/streams.php [new file with mode: 0644]
includes/player/error.swf [new file with mode: 0644]
includes/player/license.txt [new file with mode: 0644]
includes/player/load.swf [new file with mode: 0644]
includes/player/musicplayer_f6.swf [new file with mode: 0644]
includes/player/play.swf [new file with mode: 0644]
includes/player/stop.swf [new file with mode: 0644]
includes/utf8.php [new file with mode: 0644]
index.php [new file with mode: 0644]
jsScuttle.php [new file with mode: 0644]
licence.txt [new file with mode: 0644]
loading.gif [new file with mode: 0644]
locales/de_DE/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/de_DE/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/dk_DK/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/dk_DK/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/en_GB/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/en_GB/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/es_ES/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/es_ES/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/fr_FR/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/fr_FR/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/hi_IN/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/hi_IN/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/it_IT/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/it_IT/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/ja_JP/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/ja_JP/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/lt_LT/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/lt_LT/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/messages.po [new file with mode: 0644]
locales/nl_NL/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/nl_NL/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/pt_BR/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/pt_BR/LC_MESSAGES/messages.po [new file with mode: 0644]
locales/zh_CN/LC_MESSAGES/messages.mo [new file with mode: 0644]
locales/zh_CN/LC_MESSAGES/messages.po [new file with mode: 0644]
login.php [new file with mode: 0644]
logo.png [new file with mode: 0644]
logo_24.png [new file with mode: 0644]
password.php [new file with mode: 0644]
populartags.php [new file with mode: 0644]
profile.php [new file with mode: 0644]
readme.txt [new file with mode: 0644]
register.php [new file with mode: 0644]
rss.gif [new file with mode: 0644]
rss.php [new file with mode: 0644]
scuttle.css [new file with mode: 0644]
search.inc.php [new file with mode: 0644]
search.php [new file with mode: 0644]
services/bookmarkservice.php [new file with mode: 0644]
services/cacheservice.php [new file with mode: 0644]
services/servicefactory.php [new file with mode: 0644]
services/tagservice.php [new file with mode: 0644]
services/templateservice.php [new file with mode: 0644]
services/userservice.php [new file with mode: 0644]
tables.sql [new file with mode: 0644]
tagdelete.php [new file with mode: 0644]
tags.php [new file with mode: 0644]
templates/about.tpl.php [new file with mode: 0644]
templates/bookmarks.tpl.php [new file with mode: 0644]
templates/bottom.inc.php [new file with mode: 0644]
templates/dynamictags.inc.php [new file with mode: 0644]
templates/editbookmark.tpl.php [new file with mode: 0644]
templates/editprofile.tpl.php [new file with mode: 0644]
templates/error.404.tpl.php [new file with mode: 0644]
templates/error.500.tpl.php [new file with mode: 0644]
templates/importDelicious.tpl.php [new file with mode: 0644]
templates/importNetscape.tpl.php [new file with mode: 0644]
templates/login.tpl.php [new file with mode: 0644]
templates/password.tpl.php [new file with mode: 0644]
templates/profile.tpl.php [new file with mode: 0644]
templates/register.tpl.php [new file with mode: 0644]
templates/rss.tpl.php [new file with mode: 0644]
templates/sidebar.block.common.php [new file with mode: 0644]
templates/sidebar.block.popular.php [new file with mode: 0644]
templates/sidebar.block.profile.php [new file with mode: 0644]
templates/sidebar.block.recent.php [new file with mode: 0644]
templates/sidebar.block.related.php [new file with mode: 0644]
templates/sidebar.block.tagactions.php [new file with mode: 0644]
templates/sidebar.block.watchlist.php [new file with mode: 0644]
templates/sidebar.block.watchstatus.php [new file with mode: 0644]
templates/sidebar.tpl.php [new file with mode: 0644]
templates/tagdelete.tpl.php [new file with mode: 0644]
templates/tags.tpl.php [new file with mode: 0644]
templates/toolbar.inc.php [new file with mode: 0644]
templates/top.inc.php [new file with mode: 0644]
watch.php [new file with mode: 0644]
watchlist.php [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..a60a7b9
--- /dev/null
@@ -0,0 +1,4 @@
+*.~*~
+#*#
+*.project
+config.inc.php
diff --git a/.htaccess b/.htaccess
new file mode 100644 (file)
index 0000000..c129f69
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,7 @@
+Options +FollowSymlinks
+AcceptPathInfo On
+RewriteEngine On
+RewriteBase /
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule ^([^/]+)/?(.*)      $1.php/$2       [L]
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..a45e1e7
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,18 @@
+Scuttle contains code from the following applications:
+
+------------
+GPL Licenced
+------------
+
+phpBB2 (database abstraction layer)
+http://www.phpbb.com/
+
+php-gettext
+Danilo Segan <danilo@kvota.net>
+http://savannah.nongnu.org/projects/php-gettext/
+
+UTF8 Helper Functions
+Andreas Gohr <andi@splitbrain.org>
+
+XSPF Web Music Player (Flash)
+http://musicplayer.sourceforge.net/
\ No newline at end of file
diff --git a/about.php b/about.php
new file mode 100644 (file)
index 0000000..020a351
--- /dev/null
+++ b/about.php
@@ -0,0 +1,27 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004, 2005 Scuttle project
+http://sourceforge.net/projects/scuttle/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+require_once('header.inc.php');
+$templateservice =& ServiceFactory::getServiceInstance('TemplateService');
+
+$tplVars = array();
+$tplVars['subtitle'] = T_('About');
+$templateservice->loadTemplate('about.tpl', $tplVars);
+?>
\ No newline at end of file
diff --git a/ajaxDelete.php b/ajaxDelete.php
new file mode 100644 (file)
index 0000000..ad3efc8
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/***************************************************************************
+Copyright (C) 2005 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+header('Content-Type: text/xml; charset=UTF-8');
+header('Last-Modified: '. gmdate("D, d M Y H:i:s") .' GMT');
+header('Cache-Control: no-cache, must-revalidate');
+require_once('header.inc.php');
+
+$bookmarkservice = & ServiceFactory :: getServiceInstance('BookmarkService');
+$bookmark = intval($_GET['id']);
+if (!$bookmarkservice->editAllowed($bookmark)) {
+    $result = T_('You are not allowed to delete this bookmark');
+} elseif ($bookmarkservice->deleteBookmark($bookmark)) {
+    $result = 'true';
+} else {
+    $result = T_('Failed to delete bookmark');
+}
+?>
+<response>
+  <method>deleteConfirmed</method>
+  <result><?php echo $result; ?></result>
+</response>
\ No newline at end of file
diff --git a/ajaxGetTitle.php b/ajaxGetTitle.php
new file mode 100644 (file)
index 0000000..0bc142d
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/***************************************************************************
+Copyright (C) 2005 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+header('Content-Type: text/xml; charset=UTF-8');
+header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
+header("Cache-Control: no-cache, must-revalidate");
+
+require_once('header.inc.php');
+
+function getTitle($url) {
+    $fd = @fopen($url, 'r');
+    if ($fd) {
+        $html = fread($fd, 1750);
+        fclose($fd);
+
+        // Get title from title tag
+        preg_match_all('/<title>(.*)<\/title>/si', $html, $matches);
+        $title = $matches[1][0];
+
+        // Get encoding from charset attribute
+        preg_match_all('/<meta.*charset=([^;"]*)">/i', $html, $matches);
+        $encoding = strtoupper($matches[1][0]);
+
+        // Convert to UTF-8 from the original encoding
+        if (function_exists('mb_convert_encoding') {
+            $title = @mb_convert_encoding($title, 'UTF-8', $encoding);
+        }
+
+        if (utf8_strlen($title) > 0) {
+            return $title;
+        } else {
+            // No title, so return filename
+            $uriparts = explode('/', $url);
+            $filename = end($uriparts);
+            unset($uriparts);
+
+            return $filename;
+        }
+    } else {
+        return false;
+    }
+}
+echo '<?xml version="1.0" encoding="utf-8"?>';
+?>
+<response>
+  <method>getTitle</method>
+  <result><?php echo getTitle($_GET['url']); ?></result>
+</response>
\ No newline at end of file
diff --git a/ajaxIsAvailable.php b/ajaxIsAvailable.php
new file mode 100644 (file)
index 0000000..80883c6
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+header('Content-Type: text/xml; charset=UTF-8');
+header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
+header("Cache-Control: no-cache, must-revalidate");
+
+require_once('header.inc.php');
+$userservice = & ServiceFactory :: getServiceInstance('UserService');
+if ($userservice->isReserved($_GET['username'])) {
+    $result = 'false';
+} else {
+    $result = $userservice->getUserByUsername($_GET['username']) ? 'false' : 'true';
+}
+?>
+<response>
+  <method>isAvailable</method>
+  <result><?php echo $result; ?></result>
+</response>
\ No newline at end of file
diff --git a/alltags.php b/alltags.php
new file mode 100644 (file)
index 0000000..b784414
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+require_once('header.inc.php');
+$templateservice =& ServiceFactory::getServiceInstance('TemplateService');
+$tagservice =& ServiceFactory::getServiceInstance('TagService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+$cacheservice =& ServiceFactory::getServiceInstance('CacheService');
+
+list($url, $user) = explode('/', $_SERVER['PATH_INFO']);
+if (!$user) {
+    header('Location: '. createURL('populartags'));
+    exit;
+}
+
+if ($usecache) {
+    // Generate hash for caching on
+    $hashtext = $_SERVER['REQUEST_URI'];
+    if ($userservice->isLoggedOn()) {
+        $hashtext .= $userservice->getCurrentUserID();
+    }
+    $hash = md5($hashtext);
+
+    // Cache for an hour
+    $cacheservice->Start($hash, 3600);
+}
+
+// Header variables
+$tplvars = array();
+$pagetitle = T_('All Tags');
+
+if (isset($user) && $user != '') {
+    if (is_int($user)) {
+      $userid = intval($user);
+    } else {
+        if ($userinfo = $userservice->getUserByUsername($user)) {
+            $userid =& $userinfo[$userservice->getFieldName('primary')];
+        } else {
+            $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+            $templateservice->loadTemplate('error.404.tpl', $tplVars);
+            //throw a 404 error
+            exit();
+        }
+    }
+    $pagetitle .= ': '. ucfirst($user);
+} else {
+    $userid = NULL;
+}
+
+$tags =& $tagservice->getTags($userid);
+$tplVars['tags'] =& $tagservice->tagCloud($tags, 5, 90, 225, getSortOrder()); 
+$tplVars['user'] = $user;
+
+if (isset($userid)) {
+    $tplVars['cat_url'] = createURL('bookmarks', '%s/%s');
+} else {
+    $tplVars['cat_url'] = createURL('tags', '%2$s');
+}
+
+$tplVars['subtitle'] = $pagetitle;
+$templateservice->loadTemplate('tags.tpl', $tplVars);
+
+if ($usecache) {
+    // Cache output if existing copy has expired
+    $cacheservice->End($hash);
+}
+?>
diff --git a/api/.htaccess b/api/.htaccess
new file mode 100644 (file)
index 0000000..8c48221
--- /dev/null
@@ -0,0 +1,10 @@
+RewriteEngine On
+RewriteRule ^tags/get tags_get.php
+RewriteRule ^posts/dates posts_dates.php
+RewriteRule ^posts/get posts_get.php
+RewriteRule ^posts/recent posts_recent.php
+RewriteRule ^posts/all posts_all.php
+RewriteRule ^posts/update posts_update.php
+RewriteRule ^posts/add posts_add.php
+RewriteRule ^posts/delete posts_delete.php
+RewriteRule ^tags/rename tags_rename.php
\ No newline at end of file
diff --git a/api/httpauth.inc.php b/api/httpauth.inc.php
new file mode 100644 (file)
index 0000000..bc26582
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+//  Provides HTTP Basic authentication of a user, and sets two variables, sId and username,
+//  with the user's info.
+
+function authenticate() {
+    header('WWW-Authenticate: Basic realm="del.icio.us API"');
+    header('HTTP/1.0 401 Unauthorized');
+    die("Use of the API calls requires authentication.");
+}
+
+if (!isset($_SERVER['PHP_AUTH_USER'])) {
+    authenticate();
+} else {
+    require_once('../header.inc.php');
+    $userservice =& ServiceFactory::getServiceInstance('UserService');
+
+    $login = $userservice->login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); 
+    if (!$login) {
+        authenticate();
+    }
+}
+?>
\ No newline at end of file
diff --git a/api/posts_add.php b/api/posts_add.php
new file mode 100644 (file)
index 0000000..77c3288
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+// Implements the del.icio.us API request to add a new post.
+
+// del.icio.us behavior:
+// - tags can't have spaces
+// - address and description are mandatory
+
+// Scuttle behavior:
+// - Additional 'status' variable for privacy
+// - No support for 'replace' variable
+
+// Force HTTP authentication
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Get all the bookmark's passed-in information
+if (isset($_REQUEST['url']) && (trim($_REQUEST['url']) != ''))
+    $url = trim(urldecode($_REQUEST['url']));
+else
+    $url = NULL;
+
+if (isset($_REQUEST['description']) && (trim($_REQUEST['description']) != ''))
+    $description = trim($_REQUEST['description']);
+else
+    $description = NULL;
+
+if (isset($_REQUEST['extended']) && (trim($_REQUEST['extended']) != ""))
+    $extended = trim($_REQUEST['extended']);
+else
+    $extended = NULL;
+
+if (isset($_REQUEST['tags']) && (trim($_REQUEST['tags']) != '') && (trim($_REQUEST['tags']) != ','))
+    $tags = trim($_REQUEST['tags']);
+else
+    $tags = NULL;
+
+if (isset($_REQUEST['dt']) && (trim($_REQUEST['dt']) != ''))
+    $dt = trim($_REQUEST['dt']);
+else
+    $dt = NULL;
+
+$status = 0;
+if (isset($_REQUEST['status'])) {
+    $status_str = trim($_REQUEST['status']);
+    if (is_numeric($status_str)) {
+        $status = intval($status_str);
+        if($status < 0 || $status > 2) {
+            $status = 0;
+        }
+    } else {
+        switch ($status_str) {
+            case 'private':
+                $status = 2;
+                break;
+            case 'shared':
+                $status = 1;
+                break;
+            default:
+                $status = 0;
+                break;
+        }
+    }
+}
+
+// Error out if there's no address or description
+if (is_null($url) || is_null($description)) {
+    $added = false;
+} else {
+// We're good with info; now insert it!
+    if ($bookmarkservice->bookmarkExists($url, $userservice->getCurrentUserId()))
+        $added = false;
+    else
+        $added = $bookmarkservice->addBookmark($url, $description, $extended, $status, $tags, $dt, true);
+}
+
+// Set up the XML file and output the result.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<result code="'. ($added ? 'done' : 'something went wrong') .'" />';
+?>
\ No newline at end of file
diff --git a/api/posts_all.php b/api/posts_all.php
new file mode 100644 (file)
index 0000000..03026c4
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+// Implements the del.icio.us API request for all a user's posts, optionally filtered by tag.
+
+// del.icio.us behavior:
+// - doesn't include the filtered tag as an attribute on the root element (we do)
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != ''))
+    $tag = trim($_REQUEST['tag']);
+else
+    $tag = NULL;
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks =& $bookmarkservice->getBookmarks(0, NULL, $userservice->getCurrentUserId(), $tag);
+
+$currentuser = $userservice->getCurrentUser();
+$currentusername = $currentuser[$userservice->getFieldName('username')];
+
+// Set up the XML file and output all the posts.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<posts update="'. gmdate('Y-m-d\TH:i:s\Z') .'" user="'. htmlspecialchars($currentusername) .'"'. (is_null($tag) ? '' : ' tag="'. htmlspecialchars($tag) .'"') .">\r\n";
+
+foreach($bookmarks['bookmarks'] as $row) {
+    if (is_null($row['bDescription']) || (trim($row['bDescription']) == ''))
+        $description = '';
+    else
+        $description = 'extended="'. filter($row['bDescription'], 'xml') .'" ';
+
+    $taglist = '';
+    if (count($row['tags']) > 0) {
+        foreach($row['tags'] as $tag)
+            $taglist .= convertTag($tag) .' ';
+        $taglist = substr($taglist, 0, -1);
+    } else {
+        $taglist = 'system:unfiled';
+    }
+
+    echo "\t<post href=\"". filter($row['bAddress'], 'xml') .'" description="'. filter($row['bTitle'], 'xml') .'" '. $description .'hash="'. md5($row['bAddress']) .'" tag="'. filter($taglist, 'xml') .'" time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) ."\" />\r\n";
+}
+
+echo '</posts>';
+?>
\ No newline at end of file
diff --git a/api/posts_dates.php b/api/posts_dates.php
new file mode 100644 (file)
index 0000000..7098756
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+// Implements the del.icio.us API request for a user's post counts by date (and optionally
+// by tag).
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != ''))
+    $tag = trim($_REQUEST['tag']);
+else
+    $tag = NULL;
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks =& $bookmarkservice->getBookmarks(0, NULL, $userservice->getCurrentUserId(), $tag);
+
+$currentuser = $userservice->getCurrentUser();
+$currentusername = $currentuser[$userservice->getFieldName('username')];
+
+//     Set up the XML file and output all the tags.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<dates tag="'. (is_null($tag) ? '' : filter($tag, 'xml')) .'" user="'. filter($currentusername, 'xml') ."\">\r\n";
+
+$lastdate = NULL;
+foreach($bookmarks['bookmarks'] as $row) {
+    $thisdate = gmdate('Y-m-d', strtotime($row['bDatetime']));
+    if ($thisdate != $lastdate && $lastdate != NULL) {
+        echo "\t<date count=\"". $count .'" date="'. $lastdate ."\" />\r\n";
+        $count = 1;
+    } else {
+        $count = $count + 1;
+    }
+    $lastdate = $thisdate;
+}
+
+echo "</dates>";
+?>
\ No newline at end of file
diff --git a/api/posts_delete.php b/api/posts_delete.php
new file mode 100644 (file)
index 0000000..737a4fb
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+// Implements the del.icio.us API request to delete a post.
+
+// del.icio.us behavior:
+// - returns "done" even if the bookmark doesn't exist;
+// - does NOT allow the hash for the url parameter;
+// - doesn't set the Content-Type to text/xml (we do).
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Note that del.icio.us only errors out if no URL was passed in; there's no error on attempting
+// to delete a bookmark you don't have.
+
+// Error out if there's no address
+if (is_null($_REQUEST['url'])) {
+    $deleted = false;
+} else {
+    $bookmark = $bookmarkservice->getBookmarkByAddress($_REQUEST['url']);
+    $bid = $bookmark['bId'];
+    $delete = $bookmarkservice->deleteBookmark($bid);
+    $deleted = true;
+}
+
+// Set up the XML file and output the result.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<result code="'. ($deleted ? 'done' : 'something went wrong') .'" />';
+?>
\ No newline at end of file
diff --git a/api/posts_get.php b/api/posts_get.php
new file mode 100644 (file)
index 0000000..8be067b
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+// Implements the del.icio.us API request for a user's posts, optionally filtered by tag and/or
+// date.  Note that when using a date to select the posts returned, del.icio.us uses GMT dates --
+// so we do too.
+
+// del.icio.us behavior:
+// - includes an empty tag attribute on the root element when it hasn't been specified
+
+// Scuttle behavior:
+// - Uses today, instead of the last bookmarked date, if no date is specified
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != ''))
+    $tag = trim($_REQUEST['tag']);
+else
+    $tag = NULL;
+
+// Check to see if a date was specified; the format should be YYYY-MM-DD
+if (isset($_REQUEST['dt']) && (trim($_REQUEST['dt']) != ""))
+    $dtstart = trim($_REQUEST['dt']);
+else
+    $dtstart = date('Y-m-d H:i:s');
+$dtend = date('Y-m-d H:i:s', strtotime($dtstart .'+1 day'));
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks =& $bookmarkservice->getBookmarks(0, NULL, $userservice->getCurrentUserId(), $tag, NULL, NULL, NULL, $dtstart, $dtend);
+
+$currentuser = $userservice->getCurrentUser();
+$currentusername = $currentuser[$userservice->getFieldName('username')];
+
+// Set up the XML file and output all the tags.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<posts'. (is_null($dtstart) ? '' : ' dt="'. $dtstart .'"') .' tag="'. (is_null($tag) ? '' : filter($tag, 'xml')) .'" user="'. filter($currentusername, 'xml') ."\">\r\n";
+
+foreach($bookmarks['bookmarks'] as $row) {
+    if (is_null($row['bDescription']) || (trim($row['bDescription']) == ''))
+        $description = '';
+    else
+        $description = 'extended="'. filter($row['bDescription'], 'xml') .'" ';
+
+    $taglist = '';
+    if (count($row['tags']) > 0) {
+        foreach($row['tags'] as $tag)
+            $taglist .= convertTag($tag) .' ';
+        $taglist = substr($taglist, 0, -1);
+    } else {
+        $taglist = 'system:unfiled';
+    }
+
+    echo "\t<post href=\"". filter($row['bAddress'], 'xml') .'" description="'. filter($row['bTitle'], 'xml') .'" '. $description .'hash="'. $row['bHash'] .'" others="'. $bookmarkservice->countOthers($row['bAddress']) .'" tag="'. filter($taglist, 'xml') .'" time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) ."\" />\r\n";
+}
+
+echo '</posts>';
+?>
\ No newline at end of file
diff --git a/api/posts_recent.php b/api/posts_recent.php
new file mode 100644 (file)
index 0000000..22fc2bd
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+// Implements the del.icio.us API request for a user's recent posts, optionally filtered by
+// tag and/or number of posts (default 15, max 100, just like del.icio.us).
+
+// Set default and max number of posts
+$countDefault = 15;
+$countMax = 100;
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Check to see if a tag was specified.
+if (isset($_REQUEST['tag']) && (trim($_REQUEST['tag']) != ''))
+    $tag = trim($_REQUEST['tag']);
+else
+    $tag = NULL;
+
+// Check to see if the number of items was specified.
+if (isset($_REQUEST['count']) && (intval($_REQUEST['count']) != 0))  {
+    $count = intval($_REQUEST['count']);
+    if ($count > $countMax)
+        $count = $countMax;
+    elseif ($count < 0)
+        $count = 0;
+} else {
+    $count = $countDefault;
+}
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks =& $bookmarkservice->getBookmarks(0, $count, $userservice->getCurrentUserId(), $tag);
+
+$currentuser = $userservice->getCurrentUser();
+$currentusername = $currentuser[$userservice->getFieldName('username')];
+
+// Set up the XML file and output all the tags.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<posts tag="'. (is_null($tag) ? '' : filter($tag, 'xml')) .'" user="'. filter($currentusername, 'xml') ."\">\r\n";
+
+foreach($bookmarks['bookmarks'] as $row) {
+    if (is_null($row['bDescription']) || (trim($row['bDescription']) == ''))
+        $description = '';
+    else
+        $description = 'extended="'. filter($row['bDescription'], 'xml') .'" ';
+
+    $taglist = '';
+    if (count($row['tags']) > 0) {
+        foreach($row['tags'] as $tag)
+            $taglist .= convertTag($tag) .' ';
+        $taglist = substr($taglist, 0, -1);
+    } else {
+        $taglist = 'system:unfiled';
+    }
+
+    echo "\t<post href=\"". filter($row['bAddress'], 'xml') .'" description="'. filter($row['bTitle'], 'xml') .'" '. $description .'hash="'. $row['bHash'] .'" tag="'. filter($taglist, 'xml') .'" time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) ."\" />\r\n";
+}
+
+echo '</posts>';
+?>
\ No newline at end of file
diff --git a/api/posts_update.php b/api/posts_update.php
new file mode 100644 (file)
index 0000000..de379d2
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+// Implements the del.icio.us API request for a user's last update time and date.
+
+// del.icio.us behavior:
+// - doesn't set the Content-Type to text/xml (we do).
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Get the posts relevant to the passed-in variables.
+$bookmarks =& $bookmarkservice->getBookmarks(0, 1, $userservice->getCurrentUserId());
+
+$currentuser = $userservice->getCurrentUser();
+$currentusername = $currentuser[$userservice->getFieldName('username')];
+
+// Set up the XML file and output all the tags.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+foreach($bookmarks['bookmarks'] as $row) {
+    echo '<update time="'. gmdate('Y-m-d\TH:i:s\Z', strtotime($row['bDatetime'])) .'" />';
+}
+?>
\ No newline at end of file
diff --git a/api/tags_get.php b/api/tags_get.php
new file mode 100644 (file)
index 0000000..2584566
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+// Implements the del.icio.us API request for all a user's tags.
+
+// del.icio.us behavior:
+// - tags can't have spaces
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$tagservice =& ServiceFactory::getServiceInstance('TagService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Get the tags relevant to the passed-in variables.
+$tags =& $tagservice->getTags($userservice->getCurrentUserId());
+
+// Set up the XML file and output all the tags.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo "<tags>\r\n";
+foreach($tags as $row) {
+    echo "\t<tag count=\"". $row['bCount'] .'" tag="'. filter(convertTag($row['tag'], 'out'), 'xml') ."\" />\r\n";
+}
+echo "</tags>";
+?>
\ No newline at end of file
diff --git a/api/tags_rename.php b/api/tags_rename.php
new file mode 100644 (file)
index 0000000..20831e7
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+// Implements the del.icio.us API request to rename a user's tag.
+
+// del.icio.us behavior:
+// - oddly, returns an entirely different result (<result></result>) than the other API calls.
+
+// Force HTTP authentication first!
+require_once('httpauth.inc.php');
+require_once('../header.inc.php');
+
+$tagservice =& ServiceFactory::getServiceInstance('TagService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+
+// Get the tag info.
+if (isset($_REQUEST['old']) && (trim($_REQUEST['old']) != ''))
+    $old = trim($_REQUEST['old']);
+else
+    $old = NULL;
+
+if (isset($_REQUEST['new']) && (trim($_REQUEST['new']) != ''))
+    $new = trim($_REQUEST['new']);
+else
+    $new = NULL;
+
+if (is_null($old) || is_null($new)) {
+    $renamed = false;
+} else {
+    // Rename the tag.
+    $result = $tagservice->renameTag($userservice->getCurrentUserId(), $old, $new, true);
+    $renamed = $result;
+}
+
+// Set up the XML file and output the result.
+header('Content-Type: text/xml');
+echo '<?xml version="1.0" standalone="yes" ?'.">\r\n";
+echo '<result>'. ($renamed ? 'done' : 'something went wrong') .'</result>';
+?>
diff --git a/bg_bar.png b/bg_bar.png
new file mode 100644 (file)
index 0000000..7152ba1
Binary files /dev/null and b/bg_bar.png differ
diff --git a/bg_header.png b/bg_header.png
new file mode 100644 (file)
index 0000000..6fa4161
Binary files /dev/null and b/bg_header.png differ
diff --git a/bg_sidebar.png b/bg_sidebar.png
new file mode 100644 (file)
index 0000000..0edb153
Binary files /dev/null and b/bg_sidebar.png differ
diff --git a/bookmarks.php b/bookmarks.php
new file mode 100644 (file)
index 0000000..cc5eba4
--- /dev/null
@@ -0,0 +1,228 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+require_once('header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$templateservice =& ServiceFactory::getServiceInstance('TemplateService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+$cacheservice =& ServiceFactory::getServiceInstance('CacheService');
+
+$tplVars = array();
+
+if (isset($_GET['action']) && ($_GET['action'] == "add") && !$userservice->isLoggedOn()) {
+    $loginqry = str_replace("'", '%27', stripslashes($_SERVER['QUERY_STRING']));
+    header('Location: '. createURL('login', '?'. $loginqry));
+    exit();
+}
+
+@list($url, $user, $cat) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+$loggedon = false;
+if ($userservice->isLoggedOn()) {
+    $loggedon = true;
+    $currentUser = $userservice->getCurrentUser();
+    $currentUserID = $userservice->getCurrentUserId();
+    $currentUsername = $currentUser[$userservice->getFieldName('username')];
+}
+
+$endcache = false;
+if ($usecache) {
+    // Generate hash for caching on
+    $hash = md5($_SERVER['REQUEST_URI'] . $user);
+
+    // Don't cache if its users' own bookmarks
+    if ($loggedon) {
+        if ($currentUsername != $user) {
+            // Cache for 5 minutes
+            $cacheservice->Start($hash);
+            $endcache = true;
+        }
+    } else {
+        // Cache for 30 minutes
+        $cacheservice->Start($hash, 1800);
+        $endcache = true;
+    }
+}
+
+$pagetitle = $rssCat = $catTitle = '';
+if ($user) {
+    if (is_int($user)) {
+        $userid = intval($user);
+    } else {
+        if (!($userinfo = $userservice->getUserByUsername($user))) {
+            $tplVars['error'] = sprintf(T_('User with username %s was not found'), $user);
+            $templateservice->loadTemplate('error.404.tpl', $tplVars);
+            exit();
+        } else {
+            $userid =& $userinfo['uId'];
+        }
+    }
+    $pagetitle .= ': '. $user;
+}
+if ($cat) {
+    $catTitle = ': '. str_replace('+', ' + ', $cat);
+    $pagetitle .= $catTitle;
+}
+$pagetitle = substr($pagetitle, 2);
+
+// Header variables
+$tplVars['loadjs'] = true;
+
+// ADD A BOOKMARK
+$saved = false;
+$templatename = 'bookmarks.tpl';
+if ($loggedon && isset($_POST['submitted'])) {
+    if (!$_POST['title'] || !$_POST['address']) {
+        $tplVars['error'] = T_('Your bookmark must have a title and an address');
+        $templatename = 'editbookmark.tpl';
+    } else {
+        $address = trim($_POST['address']);
+        // If the bookmark exists already, edit the original
+        if ($bookmarkservice->bookmarkExists($address, $currentUserID)) {
+            $bookmark =& $bookmarkservice->getBookmarkByAddress($address);
+            header('Location: '. createURL('edit', $bookmark['bId']));
+            exit();
+        // If it's new, save it
+        } else {
+            $title = trim($_POST['title']);
+            $description = trim($_POST['description']);
+            $status = intval($_POST['status']);
+            $categories = trim($_POST['tags']);
+            $saved = true;
+            if ($bookmarkservice->addBookmark($address, $title, $description, $status, $categories)) {
+                if (isset($_POST['popup'])) {
+                    $tplVars['msg'] = '<script type="text/javascript">window.close();</script>';
+                } else {
+                    $tplVars['msg'] = T_('Bookmark saved');
+                    // Redirection option
+                    if ($GLOBALS['useredir']) {
+                        $address = $GLOBALS['url_redir'] . $address;
+                    }
+                    header('Location: '. $address);
+                }
+            } else {
+                $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.');
+                $templatename = 'editbookmark.tpl';
+                $saved = false;
+            }
+        }
+    }
+}
+
+if (isset($_GET['action']) && ($_GET['action'] == "add")) {
+    // If the bookmark exists already, edit the original
+    if ($bookmarkservice->bookmarkExists(stripslashes($_GET['address']), $currentUserID)) {
+        $bookmark =& $bookmarkservice->getBookmarkByAddress(stripslashes($_GET['address']));
+        $popup = (isset($_GET['popup'])) ? '?popup=1' : '';
+        header('Location: '. createURL('edit', $bookmark['bId'] . $popup));
+        exit();
+    }
+    $templatename = 'editbookmark.tpl';
+}
+if ($templatename == 'editbookmark.tpl') {
+    if ($loggedon) {
+        $tplVars['formaction']  = createURL('bookmarks', $currentUsername);
+        if (isset($_POST['submitted'])) {
+            $tplVars['row'] = array(
+                'bTitle' => stripslashes($_POST['title']),
+                'bAddress' => stripslashes($_POST['address']),
+                'bDescription' => stripslashes($_POST['description']),
+                'tags' => ($_POST['tags'] ? explode(',', stripslashes($_POST['tags'])) : array())
+            );
+            $tplVars['tags'] = $_POST['tags'];
+        } else {
+            $tplVars['row'] = array(
+                'bTitle' => stripslashes($_GET['title']),
+                'bAddress' => stripslashes($_GET['address']),
+                'bDescription' => stripslashes($_GET['description']),
+                'tags' => ($_GET['tags'] ? explode(',', stripslashes($_GET['tags'])) : array())
+            );
+        }
+        $title = T_('Add a Bookmark');
+        $tplVars['pagetitle'] = $title;
+        $tplVars['subtitle'] = $title;
+        $tplVars['btnsubmit'] = T_('Add Bookmark');
+        $tplVars['popup'] = (isset($_GET['popup'])) ? $_GET['popup'] : null;
+    } else {
+        $tplVars['error'] = T_('You must be logged in before you can add bookmarks.');
+    }
+} else if ($user && !isset($_GET['popup'])) {
+        
+    $tplVars['sidebar_blocks'] = array('profile', 'watchstatus');
+
+    if (!$cat) {
+        $cat = NULL;
+        $tplVars['currenttag'] = NULL; 
+    } else {
+        $rssCat = '/'. filter($cat, 'url');
+        $tplVars['currenttag'] = $cat;
+        $tplVars['sidebar_blocks'][] = 'related';
+        $tplVars['sidebar_blocks'][] = 'tagactions';
+    }
+    $tplVars['popCount'] = 30;
+    $tplVars['sidebar_blocks'][] = 'popular';
+    
+    $tplVars['userid'] = $userid;
+    $tplVars['userinfo'] =& $userinfo;
+    $tplVars['user'] = $user;
+    $tplVars['range'] = 'user';
+    
+    // Pagination
+    $perpage = getPerPageCount();
+    if (isset($_GET['page']) && intval($_GET['page']) > 1) {
+        $page = $_GET['page'];
+        $start = ($page - 1) * $perpage;
+    } else {
+        $page = 0;
+        $start = 0;
+    }
+    
+    // Set template vars
+    $tplVars['rsschannels'] = array(
+        array(filter($sitename .': '. $pagetitle), createURL('rss', filter($user, 'url') . $rssCat))
+    );
+
+    $tplVars['page'] = $page;
+    $tplVars['start'] = $start;
+    $tplVars['bookmarkCount'] = $start + 1;
+    
+    $bookmarks =& $bookmarkservice->getBookmarks($start, $perpage, $userid, $cat, $terms, getSortOrder());
+    $tplVars['total'] = $bookmarks['total'];
+    $tplVars['bookmarks'] =& $bookmarks['bookmarks'];
+    $tplVars['cat_url'] = createURL('bookmarks', '%s/%s');
+    $tplVars['nav_url'] = createURL('bookmarks', '%s/%s%s');
+    if ($user == $currentUsername) {
+        $title = T_('My Bookmarks') . filter($catTitle);
+    } else {
+        $title = filter($pagetitle);
+    }
+    $tplVars['pagetitle'] = $title;
+    $tplVars['subtitle'] = $title;
+}
+$templateservice->loadTemplate($templatename, $tplVars);
+
+if ($usecache && $endcache) {
+    // Cache output if existing copy has expired
+    $cacheservice->End($hash);
+}
+?>
diff --git a/cache/.cvsignore b/cache/.cvsignore
new file mode 100644 (file)
index 0000000..f05fcdc
--- /dev/null
@@ -0,0 +1 @@
+*.cache
\ No newline at end of file
diff --git a/cache/.htaccess b/cache/.htaccess
new file mode 100644 (file)
index 0000000..baa56e5
--- /dev/null
@@ -0,0 +1,2 @@
+order allow,deny
+deny from all
\ No newline at end of file
diff --git a/config.inc.php.example b/config.inc.php.example
new file mode 100644 (file)
index 0000000..c78f361
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+######################################################################
+# SCUTTLE: Online social bookmarks manager
+######################################################################
+# Copyright (C) 2005 - 2006 Scuttle project
+# http://sourceforge.net/projects/scuttle/
+# http://scuttle.org/
+#
+# This module is to configure the main options for your site
+#
+# This program is free software. You can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License.
+######################################################################
+
+######################################################################
+# Database Configuration
+#
+# dbtype:   Database driver - mysql, mysqli, mysql4, oracle, postgres,
+#                             sqlite, db2, firebird, mssql, mssq-odbc
+# dbhost:   Database hostname
+# dbport:   Database port
+# dbuser:   Database username
+# dbpass:   Database password
+# dbname:   Database name
+######################################################################
+
+$dbtype = 'mysql4';
+$dbhost = '127.0.0.1';
+$dbport = '3306';
+$dbuser = 'username';
+$dbpass = 'password';
+$dbname = 'scuttle';
+
+######################################################################
+# You have finished configuring the database!
+# ONLY EDIT THE INFORMATION BELOW IF YOU KNOW WHAT YOU ARE DOING.
+######################################################################
+# System Configuration
+#
+# sitename:         The name of this site.
+# locale:           The locale used.
+# top_include:      The header file.
+# bottom_include:   The footer file.
+# shortdate:        The format of short dates.
+# longdate:         The format of long dates.
+# nofollow:             true    - Include rel="nofollow" attribute on
+#                                 bookmark links.
+#                       false   - Don't include rel="nofollow".
+# defaultPerPage:   The default number of bookmarks per page.
+#                   -1 means no limit!
+# defaultRecentDays:    The number of days that bookmarks or tags should
+#                       be considered recent.
+# defaultOrderBy:   The default order in which bookmarks will appear.
+#                   Possible values are:
+#                   date_desc   - By date of entry descending.
+#                                 Latest entry first. (Default)
+#                   date_asc    - By date of entry ascending.
+#                                 Earliest entry first.
+#                   title_desc  - By title, descending alphabetically.
+#                   title_asc   - By title, ascending alphabetically.
+#                   url_desc    - By URL, descending alphabetically.
+#                   url_asc     - By URL, ascending alphabetically.
+# TEMPLATES_DIR: The directory where the template files should be
+#                loaded from (the *.tpl.php files)
+# root         : Set to NULL to autodetect the root url of the website
+# cookieprefix : The prefix to use for the cookies on the site
+# tableprefix  : The table prefix used for this installation
+# adminemail   : Contact address for the site administrator. Used
+#                as the FROM address in password retrieval e-mails.
+# cleanurls    : true   - Use mod_rewrite to hide PHP extensions
+#              : false  - Don't hide extensions [Default]
+#
+# usecache     : true   - Cache pages when possible
+#                false  - Don't cache at all [Default]
+# dir_cache    : The directory where cache files will be stored
+#
+# useredir     : true   - Improve privacy by redirecting all bookmarks
+#                         through the address specified in url_redir
+#                false  - Don't redirect bookmarks
+# url_redir    : URL prefix for bookmarks to redirect through
+#
+# filetypes    : An array of bookmark extensions that Scuttle should
+#                add system tags for.
+# reservedusers : An array of usernames that cannot be registered
+######################################################################
+
+$sitename           = 'Scuttle';
+$locale             = 'en_GB';
+$top_include        = 'top.inc.php';
+$bottom_include     = 'bottom.inc.php';
+$shortdate          = 'd-m-Y';
+$longdate           = 'j F Y';
+$nofollow           = true;
+$defaultPerPage     = 10;
+$defaultRecentDays  = 14;
+$defaultOrderBy     = 'date_desc';
+$TEMPLATES_DIR      = dirname(__FILE__) .'/templates/';
+$root               = NULL;
+$cookieprefix       = 'SCUTTLE';
+$tableprefix        = 'sc_';
+$adminemail         = 'admin@example.org';
+$cleanurls          = false;
+
+$usecache           = false;
+$dir_cache          = dirname(__FILE__) .'/cache/';
+
+$useredir           = false;
+$url_redir          = 'http://www.google.com/url?sa=D&q=';
+
+$filetypes          = array(
+                        'audio' => array('mp3', 'ogg', 'wav'),
+                        'document' => array('doc', 'odt', 'pdf'),
+                        'image' => array('gif', 'jpeg', 'jpg', 'png'),
+                        'video' => array('avi', 'mov', 'mp4', 'mpeg', 'mpg', 'wmv')
+                        );
+$reservedusers      = array('all', 'watchlist');
+
+include_once('debug.inc.php');
+?>
diff --git a/debug.inc.php b/debug.inc.php
new file mode 100644 (file)
index 0000000..8bd7f33
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+// Turn debugging on
+define('SCUTTLE_DEBUG',true);
+
+// generic debugging function
+// Sample:
+//     pc_debug(__FILE__, __LINE__, "This is a debug message.");
+
+function pc_debug($file, $line, $message) {
+    if (defined('SCUTTLE_DEBUG') && SCUTTLE_DEBUG) {
+        error_log("---DEBUG-". $sitename .": [$file][$line]: $message");
+    } else {
+        error_log("SCUTTLE_DEBUG disabled");
+    }
+}
+?>
\ No newline at end of file
diff --git a/edit.php b/edit.php
new file mode 100644 (file)
index 0000000..028e8ba
--- /dev/null
+++ b/edit.php
@@ -0,0 +1,93 @@
+<?php
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+require_once('header.inc.php');
+
+$bookmarkservice = & ServiceFactory :: getServiceInstance('BookmarkService');
+$templateservice = & ServiceFactory :: getServiceInstance('TemplateService');
+$userservice = & ServiceFactory :: getServiceInstance('UserService');
+
+// Header variables
+$tplVars['subtitle'] = T_('Edit Bookmark');
+$tplVars['loadjs'] = true;
+
+list ($url, $bookmark) = explode('/', $_SERVER['PATH_INFO']);
+if (!($row = $bookmarkservice->getBookmark(intval($bookmark), true))) {
+    $tplVars['error'] = sprintf(T_('Bookmark with id %s not was not found'), $bookmark);
+    $templateservice->loadTemplate('error.404.tpl', $tplVars);
+    exit();
+} else {
+    if (!$bookmarkservice->editAllowed($row)) {
+        $tplVars['error'] = T_('You are not allowed to edit this bookmark');
+        $templateservice->loadTemplate('error.500.tpl', $tplVars);
+        exit();
+    } else if ($_POST['submitted']) {
+        if (!$_POST['title'] || !$_POST['address']) {
+            $tplVars['error'] = T_('Your bookmark must have a title and an address');
+        } else {
+            // Update bookmark
+            $bId = intval($bookmark);
+            $address = trim($_POST['address']);
+            $title = trim($_POST['title']);
+            $description = trim($_POST['description']);
+            $status = intval($_POST['status']);
+            $tags = trim($_POST['tags']);
+            $logged_on_user = $userservice->getCurrentUser();
+            if (!$bookmarkservice->updateBookmark($bId, $address, $title, $description, $status, $tags)) {
+                $tplvars['error'] = T_('Error while saving your bookmark');
+            } else {
+                if (isset($_POST['popup'])) {
+                    $tplVars['msg'] = (isset($_POST['popup'])) ? '<script type="text/javascript">window.close();</script>' : T_('Bookmark saved');
+                } elseif (isset($_POST['referrer'])) {
+                    header('Location: '. $_POST['referrer']);
+                } else {
+                    header('Location: '. createURL('bookmarks', $logged_on_user[$userservice->getFieldName('username')]));
+                }
+            }
+        }
+    } else {
+        if ($_POST['delete']) {
+            // Delete bookmark
+            if ($bookmarkservice->deleteBookmark($bookmark)) {
+                $logged_on_user = $userservice->getCurrentUser();
+                if (isset($_POST['referrer'])) {
+                    header('Location: '. $_POST['referrer']);
+                } else {
+                    header('Location: '. createURL('bookmarks', $logged_on_user[$userservice->getFieldName('username')]));
+                }
+                exit();
+            } else {
+                $tplVars['error'] = T_('Failed to delete the bookmark');
+                $templateservice->loadTemplate('error.500.tpl', $tplVars);
+                exit();
+            }
+        }
+    }
+
+    $tplVars['popup'] = (isset($_GET['popup'])) ? $_GET['popup'] : null;
+    $tplVars['row'] =& $row;
+    $tplVars['formaction']  = createURL('edit', $bookmark);
+    $tplVars['btnsubmit'] = T_('Save Changes');
+    $tplVars['showdelete'] = true;
+    $tplVars['referrer'] = $_SERVER['HTTP_REFERER'];
+    $templateservice->loadTemplate('editbookmark.tpl', $tplVars);
+}
+?>
diff --git a/functions.inc.php b/functions.inc.php
new file mode 100644 (file)
index 0000000..63b789a
--- /dev/null
@@ -0,0 +1,160 @@
+<?php
+// UTF-8 functions
+require_once(dirname(__FILE__) .'/includes/utf8.php');
+
+// Translation
+require_once(dirname(__FILE__) .'/includes/php-gettext/gettext.inc');
+$domain = 'messages';
+T_setlocale(LC_MESSAGES, $locale);
+T_bindtextdomain($domain, dirname(__FILE__) .'/locales');
+T_bind_textdomain_codeset($domain, 'UTF-8');
+T_textdomain($domain);
+
+// Converts tags:
+// - direction = out: convert spaces to underscores;
+// - direction = in: convert underscores to spaces.
+function convertTag($tag, $direction = 'out') {
+    if ($direction == 'out') {
+        $tag = str_replace(' ', '_', $tag);
+    } else {
+        $tag = str_replace('_', ' ', $tag);
+    }
+    return $tag;
+}
+
+function filter($data, $type = NULL) {
+    if (is_string($data)) {
+        $data = trim($data);
+        $data = stripslashes($data);
+        switch ($type) {
+            case 'url':
+                $data = rawurlencode($data);
+                break;
+            default:
+                $data = htmlspecialchars($data);
+                break;
+        }
+    } else if (is_array($data)) {
+        foreach(array_keys($data) as $key) {
+            $row =& $data[$key];
+            $row = filter($row, $type);
+        }
+    }
+    return $data;
+}
+
+function getPerPageCount() {
+    global $defaultPerPage;
+    return $defaultPerPage;
+}
+
+function getSortOrder($override = NULL) {
+    global $defaultOrderBy;
+
+    if (isset($_GET['sort'])) {
+        return $_GET['sort'];
+    } else if (isset($override)) {
+        return $override;
+    } else {
+        return $defaultOrderBy;
+    }
+}
+
+function multi_array_search($needle, $haystack) {
+    if (is_array($haystack)) {
+        foreach(array_keys($haystack) as $key) {
+            $value =& $haystack[$key];
+            $result = multi_array_search($needle, $value);
+            if (is_array($result)) {
+                $return = $result;
+                array_unshift($return, $key);
+                return $return;
+            } elseif ($result == true) {
+                $return[] = $key;
+                return $return;
+            }
+        }
+        return false;
+    } else {
+        if ($needle === $haystack) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
+
+function createURL($page = '', $ending = '') {
+    global $cleanurls, $root;
+    if (!$cleanurls && $page != '') {
+        $page .= '.php';
+    }
+    return $root . $page .'/'. $ending;
+}
+
+function message_die($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '', $db = NULL) {
+    if(defined('HAS_DIED'))
+        die(T_('message_die() was called multiple times.'));
+    define('HAS_DIED', 1);
+       
+       $sql_store = $sql;
+       
+       // Get SQL error if we are debugging. Do this as soon as possible to prevent 
+       // subsequent queries from overwriting the status of sql_error()
+       if (DEBUG && ($msg_code == GENERAL_ERROR || $msg_code == CRITICAL_ERROR)) {
+               $sql_error = is_null($db) ? '' : $db->sql_error();
+               $debug_text = '';
+               
+               if ($sql_error['message'] != '')
+                       $debug_text .= '<br /><br />'. T_('SQL Error') .' : '. $sql_error['code'] .' '. $sql_error['message'];
+
+               if ($sql_store != '')
+                       $debug_text .= '<br /><br />'. $sql_store;
+
+               if ($err_line != '' && $err_file != '')
+                       $debug_text .= '</br /><br />'. T_('Line') .' : '. $err_line .'<br />'. T_('File') .' :'. $err_file;
+       }
+
+       switch($msg_code) {
+               case GENERAL_MESSAGE:
+                       if ($msg_title == '')
+                               $msg_title = T_('Information');
+                       break;
+
+               case CRITICAL_MESSAGE:
+                       if ($msg_title == '')
+                               $msg_title = T_('Critical Information');
+                       break;
+
+               case GENERAL_ERROR:
+                       if ($msg_text == '')
+                               $msg_text = T_('An error occured');
+
+                       if ($msg_title == '')
+                               $msg_title = T_('General Error');
+                       break;
+
+               case CRITICAL_ERROR:
+                       // Critical errors mean we cannot rely on _ANY_ DB information being
+                       // available so we're going to dump out a simple echo'd statement
+
+                       if ($msg_text == '')
+                               $msg_text = T_('An critical error occured');
+
+                       if ($msg_title == '')
+                               $msg_title = T_('Critical Error');
+                       break;
+       }
+
+       // Add on DEBUG info if we've enabled debug mode and this is an error. This
+       // prevents debug info being output for general messages should DEBUG be
+       // set TRUE by accident (preventing confusion for the end user!)
+       if (DEBUG && ($msg_code == GENERAL_ERROR || $msg_code == CRITICAL_ERROR)) {
+               if ($debug_text != '')
+                       $msg_text = $msg_text . '<br /><br /><strong>'. T_('DEBUG MODE') .'</strong>'. $debug_text;
+       }
+
+       echo "<html>\n<body>\n". $msg_title ."\n<br /><br />\n". $msg_text ."</body>\n</html>";
+       exit;
+}
+?>
diff --git a/header.inc.php b/header.inc.php
new file mode 100644 (file)
index 0000000..751e4e8
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+ini_set('display_errors', '1');
+ini_set('mysql.trace_mode', '0');
+
+error_reporting(E_ALL ^ E_NOTICE);
+
+define('DEBUG', true);
+session_start();
+
+require_once(dirname(__FILE__) .'/services/servicefactory.php');
+require_once(dirname(__FILE__) .'/config.inc.php');
+require_once(dirname(__FILE__) .'/functions.inc.php');
+
+// Determine the base URL
+if (!isset($root)) {
+    $pieces = explode('/', $_SERVER['SCRIPT_NAME']);
+    $root = '/';
+    foreach($pieces as $piece) {
+        if ($piece != '' && !strstr($piece, '.php')) {
+            $root .= $piece .'/';
+        }
+    }
+    if (($root != '/') && (substr($root, -1, 1) != '/')) {
+        $root .= '/';
+    }
+    $root = 'http://'. $_SERVER['HTTP_HOST'] . $root;
+}
+
+// Error codes
+define('GENERAL_MESSAGE', 200);
+define('GENERAL_ERROR', 202);
+define('CRITICAL_MESSAGE', 203);
+define('CRITICAL_ERROR', 204);
+?>
\ No newline at end of file
diff --git a/history.php b/history.php
new file mode 100644 (file)
index 0000000..568a8b6
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/***************************************************************************
+Copyright (C) 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+require_once('header.inc.php');
+
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$templateservice =& ServiceFactory::getServiceInstance('TemplateService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+$cacheservice =& ServiceFactory::getServiceInstance('CacheService');
+
+$tplVars = array();
+
+@list($url, $hash) = isset($_SERVER['PATH_INFO']) ? explode('/', $_SERVER['PATH_INFO']) : NULL;
+
+$loggedon = false;
+if ($userservice->isLoggedOn()) {
+    $loggedon = true;
+    $currentUser = $userservice->getCurrentUser();
+    $currentUsername = $currentUser[$userservice->getFieldName('username')];
+}
+
+if ($usecache) {
+    // Generate hash for caching on
+    $hashtext = $_SERVER['REQUEST_URI'];
+    if ($userservice->isLoggedOn()) {
+        $hashtext .= $currentUsername;
+    }
+    $cachehash = md5($hashtext);
+
+    // Cache for 30 minutes
+    $cacheservice->Start($cachehash, 1800);
+}
+
+// Pagination
+$perpage = getPerPageCount();
+if (isset($_GET['page']) && intval($_GET['page']) > 1) {
+    $page = $_GET['page'];
+    $start = ($page - 1) * $perpage;
+} else {
+    $page = 0;
+    $start = 0;
+}
+
+if ($bookmark =& $bookmarkservice->getBookmarkByHash($hash)) {
+    // Template variables
+    $bookmarks =& $bookmarkservice->getBookmarks($start, $perpage, NULL, NULL, NULL, getSortOrder(), NULL, NULL, NULL, $hash);
+    $tplVars['pagetitle'] = T_('History') .': '. $bookmark['bAddress'];
+    $tplVars['subtitle'] = sprintf(T_('History for %s'), $bookmark['bAddress']);
+    $tplVars['loadjs'] = true;
+    $tplVars['page'] = $page;
+    $tplVars['start'] = $start;
+    $tplVars['bookmarkCount'] = $start + 1;
+    $tplVars['total'] = $bookmarks['total'];
+    $tplVars['bookmarks'] =& $bookmarks['bookmarks'];
+    $tplVars['hash'] = $hash;
+    $tplVars['popCount'] = 50;
+    $tplVars['sidebar_blocks'] = array('common');
+    $tplVars['cat_url'] = createURL('tags', '%2$s');
+    $tplVars['nav_url'] = createURL('history', $hash .'/%3$s');
+    $templateservice->loadTemplate('bookmarks.tpl', $tplVars);
+} else {
+    // Throw a 404 error
+    $tplVars['error'] = T_('Address was not found');
+    $templateservice->loadTemplate('error.404.tpl', $tplVars);
+    exit();
+}
+
+if ($usecache) {
+    // Cache output if existing copy has expired
+    $cacheservice->End($cachehash);
+}
+?>
\ No newline at end of file
diff --git a/icon.png b/icon.png
new file mode 100644 (file)
index 0000000..be864a7
Binary files /dev/null and b/icon.png differ
diff --git a/import.php b/import.php
new file mode 100644 (file)
index 0000000..f25b439
--- /dev/null
@@ -0,0 +1,109 @@
+<?
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+require_once('header.inc.php');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+$templateservice =& ServiceFactory::getServiceInstance('TemplateService');
+$tplVars = array();
+
+if ($userservice->isLoggedOn() && sizeof($_FILES) > 0 && $_FILES['userfile']['size'] > 0) {
+    $userinfo = $userservice->getCurrentUser();
+
+    if (isset($_POST['status']) && is_numeric($_POST['status'])) {
+        $status = intval($_POST['status']);
+    } else {
+        $status = 2;
+    }
+
+    $depth = array();
+    $xml_parser = xml_parser_create();
+    xml_set_element_handler($xml_parser, "startElement", "endElement");
+
+    if (!($fp = fopen($_FILES['userfile']['tmp_name'], "r")))
+        die(T_("Could not open XML input"));
+
+    while ($data = fread($fp, 4096)) {
+        if (!xml_parse($xml_parser, $data, feof($fp))) {
+            die(sprintf(T_("XML error: %s at line %d"),
+                xml_error_string(xml_get_error_code($xml_parser)),
+                xml_get_current_line_number($xml_parser)));
+        }
+    }
+    xml_parser_free($xml_parser);
+    header('Location: '. createURL('bookmarks', $userinfo[$userservice->getFieldName('username')]));
+} else {
+    $templatename = 'importDelicious.tpl';
+    $tplVars['subtitle'] = T_('Import Bookmarks from del.icio.us');
+    $tplVars['formaction']  = createURL('import');
+    $templateservice->loadTemplate($templatename, $tplVars);
+}
+
+function startElement($parser, $name, $attrs) {
+    global $depth, $status, $tplVars, $userservice;
+
+    $bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+    $userservice =& ServiceFactory::getServiceInstance('UserService');
+
+    if ($name == 'POST') {
+        while(list($attrTitle, $attrVal) = each($attrs)) {
+            switch ($attrTitle) {
+                case 'HREF':
+                    $bAddress = $attrVal;
+                    break;
+                case 'DESCRIPTION':
+                    $bTitle = $attrVal;
+                    break;
+                case 'EXTENDED':
+                    $bDescription = $attrVal;
+                    break;
+                case 'TIME':
+                    $bDatetime = $attrVal;
+                    break;
+                case 'TAG':
+                    $tags = strtolower($attrVal);
+                    break;
+            }
+        }
+        if ($bookmarkservice->bookmarkExists($bAddress, $userservice->getCurrentUserId())) {
+            $tplVars['error'] = T_('You have already submitted this bookmark.');
+        } else {
+            // Strangely, PHP can't work out full ISO 8601 dates, so we have to chop off the Z.
+            $bDatetime = substr($bDatetime, 0, -1);
+
+            // If bookmark claims to be from the future, set it to be now instead
+            if (strtotime($bDatetime) > time()) {
+                $bDatetime = gmdate('Y-m-d H:i:s');
+            }
+
+            if ($bookmarkservice->addBookmark($bAddress, $bTitle, $bDescription, $status, $tags, $bDatetime, true, true))
+                $tplVars['msg'] = T_('Bookmark imported.');
+            else
+                $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.');
+        }
+    }
+    $depth[$parser]++;
+}
+
+function endElement($parser, $name) {
+    global $depth;
+    $depth[$parser]--;
+}
+?>
diff --git a/importNetscape.php b/importNetscape.php
new file mode 100644 (file)
index 0000000..6682ec0
--- /dev/null
@@ -0,0 +1,85 @@
+<?
+/***************************************************************************
+Copyright (C) 2004 - 2006 Scuttle project
+http://sourceforge.net/projects/scuttle/
+http://scuttle.org/
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+***************************************************************************/
+
+require_once('header.inc.php');
+$bookmarkservice =& ServiceFactory::getServiceInstance('BookmarkService');
+$userservice =& ServiceFactory::getServiceInstance('UserService');
+$templateservice =& ServiceFactory::getServiceInstance('TemplateService');
+$tplVars = array();
+
+if ($userservice->isLoggedOn() && sizeof($_FILES) > 0 && $_FILES['userfile']['size'] > 0) {
+    $userinfo = $userservice->getCurrentUser();
+
+    if (isset($_POST['status']) && is_numeric($_POST['status'])) {
+        $status = intval($_POST['status']);
+    } else {
+        $status = 2;
+    }
+
+    // File handle
+    $html = file_get_contents($_FILES['userfile']['tmp_name']);
+    
+    // Create link array
+    preg_match_all('/<a\s+(.*?)\s*\/*>([^<]*)/si', $html, $matches);
+    $links = $matches[1];
+    $titles = $matches[2];
+    
+    $size = count($links);
+    for ($i = 0; $i < $size; $i++) {
+        $attributes = preg_split('/\s+/s', $links[$i]);
+        foreach ($attributes as $attribute) {
+            $att = preg_split('/\s*=\s*/s', $attribute, 2);
+            $attrTitle = $att[0];
+            $attrVal = eregi_replace('"', '&quot;', preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1]));
+            switch ($attrTitle) {
+                case "HREF":
+                    $bAddress = $attrVal;
+                    break;
+                case "ADD_DATE":
+                    $bDatetime = gmdate('Y-m-d H:i:s', $attrVal);
+                    break;
+            }
+        }
+        $bTitle = eregi_replace('"', '&quot;', trim($titles[$i]));
+
+        if ($bookmarkservice->bookmarkExists($bAddress, $userservice->getCurrentUserId())) {
+            $tplVars['error'] = T_('You have already submitted this bookmark.');
+        } else {
+            // If bookmark claims to be from the future, set it to be now instead
+            if (strtotime($bDatetime) > time()) {
+                $bDatetime = gmdate('Y-m-d H:i:s');
+            }
+
+            if ($bookmarkservice->addBookmark($bAddress, $bTitle, NULL, $status, NULL, $bDatetime, false, true)) {
+                $tplVars['msg'] = T_('Bookmark imported.');
+            } else {
+                $tplVars['error'] = T_('There was an error saving your bookmark. Please try again or contact the administrator.');
+            }
+        }
+    }
+    header('Location: '. createURL('bookmarks', $userinfo[$userservice->getFieldName('username')]));
+} else {
+    $templatename = 'importNetscape.tpl';
+    $tplVars['subtitle'] = T_('Import Bookmarks from Browser File');
+    $tplVars['formaction'] = createURL('importNetscape');
+    $templateservice->loadTemplate($templatename, $tplVars);
+}
+?>
diff --git a/includes/db/db2.php b/includes/db/db2.php
new file mode 100644 (file)
index 0000000..b1abf1a
--- /dev/null
@@ -0,0 +1,417 @@
+<?php
+/** 
+*
+* @package dbal_db2
+* @version $Id: db2.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if(!defined("SQL_LAYER"))
+{
+
+define("SQL_LAYER","db2");
+
+/**
+* @package dbal_db2
+* DB2 Database Abstraction Layer
+*/
+class sql_db
+{
+
+       var $db_connect_id;
+       var $query_result;
+       var $query_resultset;
+       var $query_numrows;
+       var $next_id;
+       var $row = array();
+       var $rowset = array();
+       var $row_index;
+       var $num_queries = 0;
+
+       //
+       // Constructor
+       //
+       function sql_db($sqlserver, $sqluser, $sqlpassword, $database, $persistency = true)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->password = $sqlpassword;
+               $this->dbname = $database;
+
+               $this->server = $sqlserver;
+
+               if($this->persistency)
+               {
+                       $this->db_connect_id = odbc_pconnect($this->server, "", "");
+               }
+               else
+               {
+                       $this->db_connect_id = odbc_connect($this->server, "", "");
+               }
+
+               if($this->db_connect_id)
+               {
+                       @odbc_autocommit($this->db_connect_id, off);
+
+                       return $this->db_connect_id;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       //
+       // Other base methods
+       //
+       function sql_close()
+       {
+               if($this->db_connect_id)
+               {
+                       if($this->query_result)
+                       {
+                               @odbc_free_result($this->query_result);
+                       }
+                       $result = @odbc_close($this->db_connect_id);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+
+       //
+       // Query method
+       //
+       function sql_query($query = "", $transaction = FALSE)
+       {
+               //
+               // Remove any pre-existing queries
+               //
+               unset($this->query_result);
+               unset($this->row);
+               if($query != "")
+               {
+                       $this->num_queries++;
+
+                       if(!eregi("^INSERT ",$query))
+                       {
+                               if(eregi("LIMIT", $query))
+                               {
+                                       preg_match("/^(.*)LIMIT ([0-9]+)[, ]*([0-9]+)*/s", $query, $limits);
+
+                                       $query = $limits[1];
+                                       if($limits[3])
+                                       {
+                                               $row_offset = $limits[2];
+                                               $num_rows = $limits[3];
+                                       }
+                                       else
+                                       {
+                                               $row_offset = 0;
+                                               $num_rows = $limits[2];
+                                       }
+
+                                       $query .= " FETCH FIRST ".($row_offset+$num_rows)." ROWS ONLY OPTIMIZE FOR ".($row_offset+$num_rows)." ROWS";
+
+                                       $this->query_result = odbc_exec($this->db_connect_id, $query);
+
+                                       $query_limit_offset = $row_offset;
+                                       $this->result_numrows[$this->query_result] = $num_rows;
+                               }
+                               else
+                               {
+                                       $this->query_result = odbc_exec($this->db_connect_id, $query);
+
+                                       $row_offset = 0;
+                                       $this->result_numrows[$this->query_result] = 5E6;
+                               }
+
+                               $result_id = $this->query_result;
+                               if($this->query_result && eregi("^SELECT", $query))
+                               {
+
+                                       for($i = 1; $i < odbc_num_fields($result_id)+1; $i++)
+                                       {
+                                               $this->result_field_names[$result_id][] = odbc_field_name($result_id, $i);
+                                       }
+
+                                       $i =  $row_offset + 1;
+                                       $k = 0;
+                                       while(odbc_fetch_row($result_id, $i) && $k < $this->result_numrows[$result_id])
+                                       {
+
+                                               for($j = 1; $j < count($this->result_field_names[$result_id])+1; $j++)
+                                               {
+                                                       $this->result_rowset[$result_id][$k][$this->result_field_names[$result_id][$j-1]] = odbc_result($result_id, $j);
+                                               }
+                                               $i++;
+                                               $k++;
+                                       }
+
+                                       $this->result_numrows[$result_id] = $k;
+                                       $this->row_index[$result_id] = 0;
+                               }
+                               else
+                               {
+                                       $this->result_numrows[$result_id] = @odbc_num_rows($result_id);
+                                       $this->row_index[$result_id] = 0;
+                               }
+                       }
+                       else
+                       {
+                               if(eregi("^(INSERT|UPDATE) ", $query))
+                               {
+                                       $query = preg_replace("/\\\'/s", "''", $query);
+                               }
+
+                               $this->query_result = odbc_exec($this->db_connect_id, $query);
+
+                               if($this->query_result)
+                               {
+                                       $sql_id = "VALUES(IDENTITY_VAL_LOCAL())";
+
+                                       $id_result = odbc_exec($this->db_connect_id, $sql_id);
+                                       if($id_result)
+                                       {
+                                               $row_result = odbc_fetch_row($id_result);
+                                               if($row_result)
+                                               {
+                                                       $this->next_id[$this->query_result] = odbc_result($id_result, 1);
+                                               }
+                                       }
+                               }
+
+                               odbc_commit($this->db_connect_id);
+
+                               $this->query_limit_offset[$this->query_result] = 0;
+                               $this->result_numrows[$this->query_result] = 0;
+                       }
+
+                       return $this->query_result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+       //
+       // Other query methods
+       //
+       function sql_numrows($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       return $this->result_numrows[$query_id];
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_affectedrows($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       return $this->result_numrows[$query_id];
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_numfields($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = count($this->result_field_names[$query_id]);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fieldname($offset, $query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = $this->result_field_names[$query_id][$offset];
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fieldtype($offset, $query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = @odbc_field_type($query_id, $offset);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fetchrow($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       if($this->row_index[$query_id] < $this->result_numrows[$query_id])
+                       {
+                               $result = $this->result_rowset[$query_id][$this->row_index[$query_id]];
+                               $this->row_index[$query_id]++;
+                               return $result;
+                       }
+                       else
+                       {
+                               return false;
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fetchrowset($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $this->row_index[$query_id] = $this->result_numrows[$query_id];
+                       return $this->result_rowset[$query_id];
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fetchfield($field, $row = -1, $query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       if($row < $this->result_numrows[$query_id])
+                       {
+                               if($row == -1)
+                               {
+                                       $getrow = $this->row_index[$query_id]-1;
+                               }
+                               else
+                               {
+                                       $getrow = $row;
+                               }
+
+                               return $this->result_rowset[$query_id][$getrow][$this->result_field_names[$query_id][$field]];
+
+                       }
+                       else
+                       {
+                               return false;
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_rowseek($offset, $query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $this->row_index[$query_id] = 0;
+                       return true;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_nextid($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       return $this->next_id[$query_id];
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_freeresult($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = @odbc_free_result($query_id);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_error($query_id = 0)
+       {
+//             $result['code'] = @odbc_error($this->db_connect_id);
+//             $result['message'] = @odbc_errormsg($this->db_connect_id);
+
+               return "";
+       }
+
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/firebird.php b/includes/db/firebird.php
new file mode 100644 (file)
index 0000000..58a7d07
--- /dev/null
@@ -0,0 +1,527 @@
+<?php
+/** 
+*
+* @package dbal_firebird
+* @version $Id: firebird.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'firebird');
+
+/**
+* @package dbal_firebird
+* Firebird/Interbase Database Abstraction Layer
+* Minimum Requirement is Firebird 1.5+/Interbase 7.1+
+*/
+class sql_db
+{
+       var $db_connect_id;
+       var $query_result;
+       var $return_on_error = false;
+       var $transaction = false;
+       var $sql_time = 0;
+       var $num_queries = 0;
+       var $open_queries = array();
+
+       var $last_query_text = '';
+
+       function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->server = $sqlserver . (($port) ? ':' . $port : '');
+               $this->dbname = $database;
+
+               $this->db_connect_id = ($this->persistency) ? @ibase_pconnect($this->server . ':' . $this->dbname, $this->user, $sqlpassword, false, false, 3) : @ibase_connect($this->server . ':' . $this->dbname, $this->user, $sqlpassword, false, false, 3);
+
+               return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+       }
+
+       //
+       // Other base methods
+       //
+       function sql_close()
+       {
+               if (!$this->db_connect_id)
+               {
+                       return false;
+               }
+
+               if ($this->transaction)
+               {
+                       @ibase_commit($this->db_connect_id);
+               }
+
+               if (sizeof($this->open_queries))
+               {
+                       foreach ($this->open_queries as $i_query_id => $query_id)
+                       {
+                               @ibase_free_query($query_id);
+                       }
+               }
+
+               return @ibase_close($this->db_connect_id);
+       }
+
+       function sql_return_on_error($fail = false)
+       {
+               $this->return_on_error = $fail;
+       }
+
+       function sql_num_queries()
+       {
+               return $this->num_queries;
+       }
+
+       function sql_transaction($status = 'begin')
+       {
+               switch ($status)
+               {
+                       case 'begin':
+                               $this->transaction = true;
+                               break;
+
+                       case 'commit':
+                               $result = @ibase_commit();
+                               $this->transaction = false;
+
+                               if (!$result)
+                               {
+                                       @ibase_rollback();
+                               }
+                               break;
+
+                       case 'rollback':
+                               $result = @ibase_rollback();
+                               $this->transaction = false;
+                               break;
+
+                       default:
+                               $result = true;
+               }
+
+               return $result;
+       }
+
+       // Base query method
+       function sql_query($query = '', $cache_ttl = 0)
+       {
+               if ($query != '')
+               {
+                       global $cache;
+
+                       $this->last_query_text = $query;
+                       $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+                       if (!$this->query_result)
+                       {
+                               $this->num_queries++;
+
+                               if (($this->query_result = @ibase_query($this->db_connect_id, $query)) === false)
+                               {
+                                       $this->sql_error($query);
+                               }
+
+                               // TODO: have to debug the commit states in firebird
+                               if (!$this->transaction)
+                               {
+                                       @ibase_commit_ret();
+                               }
+
+                               if ($cache_ttl && method_exists($cache, 'sql_save'))
+                               {
+                                       $cache->sql_save($query, $this->query_result, $cache_ttl);
+                               }
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+
+               return ($this->query_result) ? $this->query_result : false;
+       }
+
+       function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) 
+       { 
+               if ($query != '') 
+               {
+                       $this->query_result = false; 
+
+                       $query = 'SELECT FIRST ' . $total . ((!empty($offset)) ? ' SKIP ' . $offset : '') . substr($query, 6);
+
+                       return $this->sql_query($query, $cache_ttl); 
+               } 
+               else 
+               { 
+                       return false; 
+               } 
+       }
+
+       // Idea for this from Ikonboard
+       function sql_build_array($query, $assoc_ary = false)
+       {
+               if (!is_array($assoc_ary))
+               {
+                       return false;
+               }
+
+               $fields = array();
+               $values = array();
+               if ($query == 'INSERT')
+               {
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               $fields[] = $key;
+
+                               if (is_null($var))
+                               {
+                                       $values[] = 'NULL';
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "'" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? intval($var) : $var;
+                               }
+                       }
+
+                       $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+               }
+               else if ($query == 'UPDATE' || $query == 'SELECT')
+               {
+                       $values = array();
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               if (is_null($var))
+                               {
+                                       $values[] = "$key = NULL";
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "$key = '" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+                               }
+                       }
+                       $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+               }
+
+               return $query;
+       }
+
+       // Other query methods
+       //
+       // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+       //         don't want this here by a middle Milestone
+       function sql_numrows($query_id = false)
+       {
+               return FALSE;
+       }
+
+       function sql_affectedrows()
+       {
+               // TODO: hmm, maybe doing something similar as in mssql-odbc.php?
+               return ($this->query_result) ? true : false;
+       }
+
+       function sql_fetchrow($query_id = false)
+       {
+               global $cache;
+
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($cache->sql_rowset[$query_id]))
+               {
+                       return $cache->sql_fetchrow($query_id);
+               }
+
+               $row = array();
+               $cur_row = @ibase_fetch_object($query_id, IBASE_TEXT);
+
+               if (!$cur_row)
+               {
+                       return false;
+               }
+
+               foreach (get_object_vars($cur_row) as $key => $value)
+               {
+                       $row[strtolower($key)] = trim(str_replace("\\0", "\0", str_replace("\\n", "\n", $value)));
+               }
+               return ($query_id) ? $row : false;
+       }
+
+       function sql_fetchrowset($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       unset($this->rowset[$query_id]);
+                       unset($this->row[$query_id]);
+
+                       $result = array();
+                       while ($this->rowset[$query_id] = get_object_vars(@ibase_fetch_object($query_id, IBASE_TEXT)))
+                       {
+                               $result[] = $this->rowset[$query_id];
+                       }
+
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+       function sql_fetchfield($field, $rownum = -1, $query_id = 0)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       if ($rownum > -1)
+                       {
+                               // erm... ok, my bad, we always use zero. :/
+                               for ($i = 0; $i <= $rownum; $i++)
+                               {
+                                       $row = $this->sql_fetchrow($query_id);
+                               }
+
+                               return $row[$field];
+                       }
+                       else
+                       {
+                               if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+                               {
+                                       if ($this->sql_fetchrow($query_id))
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                               else
+                               {
+                                       if ($this->rowset[$query_id])
+                                       {
+                                               $result = $this->rowset[$query_id][$field];
+                                       }
+                                       else if ($this->row[$query_id])
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                       }
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+       function sql_rowseek($rownum, $query_id = 0)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               for($i = 1; $i < $rownum; $i++)
+               {
+                       if (!$this->sql_fetchrow($query_id))
+                       {
+                               return false;
+                       }
+               }
+
+               return true;
+       }
+
+       function sql_nextid()
+       {
+               if ($this->query_result && preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename))
+               {
+                       $query = "SELECT GEN_ID('" . $tablename[1] . "_gen', 0) AS new_id  
+                               FROM RDB\$DATABASE";
+                       if (!($temp_q_id =  @ibase_query($this->db_connect_id, $query)))
+                       {
+                               return false;
+                       }
+
+                       $temp_result = @ibase_fetch_object($temp_q_id);
+                       $this->sql_freeresult($temp_q_id);
+
+                       return ($temp_result) ? $temp_result->last_value : false;
+               }
+       }
+
+       function sql_freeresult($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (!$this->transaction && $query_id)
+               {
+                       @ibase_commit();
+               }
+
+               return ($query_id) ? @ibase_free_result($query_id) : false;
+       }
+
+       function sql_escape($msg)
+       {
+               return (@ini_get('magic_quotes_sybase') || strtolower(@ini_get('magic_quotes_sybase')) == 'on') ? str_replace('\\\'', '\'', addslashes($msg)) : str_replace('\'', '\'\'', stripslashes($msg));
+       }
+
+       function sql_error($sql = '')
+       {
+               if (!$this->return_on_error)
+               {
+                       $this_page =(!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+                       $this_page .= '&' .((!empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : $_ENV['QUERY_STRING']);
+
+                       $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @ibase_errmsg() . '<br /><br /><u>CALLING PAGE</u><br /><br />'  . $this_page .(($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+                       if ($this->transaction)
+                       {
+                               $this->sql_transaction('rollback');
+                       }
+
+                       trigger_error($message, E_USER_ERROR);
+               }
+
+               $result['message'] = @ibase_errmsg();
+               $result['code'] = '';
+
+               return $result;
+       }
+
+       function sql_report($mode, $query = '')
+       {
+               if (empty($_GET['explain']))
+               {
+                       return;
+               }
+
+               global $cache, $starttime, $phpbb_root_path;
+               static $curtime, $query_hold, $html_hold;
+               static $sql_report = '';
+               static $cache_num_queries = 0;
+
+               if (!$query && !empty($query_hold))
+               {
+                       $query = $query_hold;
+               }
+
+               switch ($mode)
+               {
+                       case 'display':
+                               if (!empty($cache))
+                               {
+                                       $cache->unload();
+                               }
+                               $this->sql_close();
+
+                               $mtime = explode(' ', microtime());
+                               $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+                               echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+                               echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+                               echo 'td.cat    { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+                               echo '</style><title>' . $msg_title . '</title></head><body>';
+                               echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+                               echo $sql_report;
+                               echo '</td></tr></table><br /></body></html>';
+                               exit;
+                               break;
+
+                       case 'start':
+                               $query_hold = $query;
+                               $html_hold = '';
+
+                               $curtime = explode(' ', microtime());
+                               $curtime = $curtime[0] + $curtime[1];
+                               break;
+
+                       case 'fromcache':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $result = @ibase_query($this->db_connect_id, $query);
+                               while ($void = @ibase_fetch_object($result, IBASE_TEXT))
+                               {
+                                       // Take the time spent on parsing rows into account
+                               }
+                               $splittime = explode(' ', microtime());
+                               $splittime = $splittime[0] + $splittime[1];
+
+                               $time_cache = $endtime - $curtime;
+                               $time_db = $splittime - $endtime;
+                               $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+                               $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+                               // Pad the start time to not interfere with page timing
+                               $starttime += $time_db;
+
+                               @ibase_freeresult($result);
+                               $cache_num_queries++;
+                               break;
+
+                       case 'stop':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+                               if ($this->query_result)
+                               {
+                                       if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+                                       {
+                                               $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+                                       }
+                                       $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+                               }
+                               else
+                               {
+                                       $error = $this->sql_error();
+                                       $sql_report .= '<b style="color: red">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+                               }
+
+                               $sql_report .= '</p>';
+
+                               $this->sql_time += $endtime - $curtime;
+                               break;
+               }
+       }
+
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/index.htm b/includes/db/index.htm
new file mode 100644 (file)
index 0000000..ee1f723
--- /dev/null
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000">
+
+</body>
+</html>
diff --git a/includes/db/mssql-odbc.php b/includes/db/mssql-odbc.php
new file mode 100644 (file)
index 0000000..a2d3d02
--- /dev/null
@@ -0,0 +1,576 @@
+<?php
+/** 
+*
+* @package dbal_odbc_mssql
+* @version $Id: mssql-odbc.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mssql-odbc');
+
+/**
+* @package dbal_odbc_mssql
+* MSSQL ODBC Database Abstraction Layer for MSSQL
+* Minimum Requirement is Version 2000+
+*/
+class sql_db
+{
+       var $db_connect_id;
+       var $query_result;
+       var $return_on_error = false;
+       var $transaction = false;
+       var $sql_time = 0;
+       var $num_queries = 0;
+       var $open_queries = array();
+
+       var $result_rowset = array();
+       var $field_names = array();
+       var $field_types = array();
+       var $num_rows = array();
+       var $current_row = array();
+
+       function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->server = $sqlserver . (($port) ? ':' . $port : '');
+               $this->dbname = $database;
+
+               $this->db_connect_id = ($this->persistency) ? @odbc_pconnect($this->server, $this->user, $sqlpassword) : @odbc_connect($this->server, $this->user, $sqlpassword);
+
+               return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+       }
+
+       //
+       // Other base methods
+       //
+       function sql_close()
+       {
+               if (!$this->db_connect_id)
+               {
+                       return false;
+               }
+
+               if ($this->transaction)
+               {
+                       @odbc_commit($this->db_connect_id);
+               }
+
+               if (sizeof($this->result_rowset))
+               {
+                       unset($this->result_rowset);
+                       unset($this->field_names);
+                       unset($this->field_types);
+                       unset($this->num_rows);
+                       unset($this->current_row);
+               }
+
+               if (sizeof($this->open_queries))
+               {
+                       foreach ($this->open_queries as $i_query_id => $query_id)
+                       {
+                               @odbc_free_result($query_id);
+                       }
+               }
+
+               return @odbc_close($this->db_connect_id);
+       }
+
+       function sql_return_on_error($fail = false)
+       {
+               $this->return_on_error = $fail;
+       }
+
+       function sql_num_queries()
+       {
+               return $this->num_queries;
+       }
+
+       function sql_transaction($status = 'begin')
+       {
+               switch ($status)
+               {
+                       case 'begin':
+                               $result = @odbc_autocommit($this->db_connect_id, false);
+                               $this->transaction = true;
+                               break;
+
+                       case 'commit':
+                               $result = @odbc_commit($this->db_connect_id);
+                               @odbc_autocommit($this->db_connect_id, true);
+                               $this->transaction = false;
+
+                               if (!$result)
+                               {
+                                       @odbc_rollback($this->db_connect_id);
+                                       @odbc_autocommit($this->db_connect_id, true);
+                               }
+                               break;
+
+                       case 'rollback':
+                               $result = @odbc_rollback($this->db_connect_id);
+                               @odbc_autocommit($this->db_connect_id, true);
+                               $this->transaction = false;
+                               break;
+
+                       default:
+                               $result = true;
+               }
+
+               return $result;
+       }
+
+       // Base query method
+       function sql_query($query = '', $cache_ttl = 0)
+       {
+               if ($query != '')
+               {
+                       global $cache;
+
+                       // EXPLAIN only in extra debug mode
+                       if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('start', $query);
+                       }
+
+                       $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+                       if (!$this->query_result)
+                       {
+                               $this->num_queries++;
+
+                               if (($this->query_result = $this->_odbc_execute_query($query)) === false)
+                               {
+                                       $this->sql_error($query);
+                               }
+
+                               if (defined('DEBUG_EXTRA'))
+                               {
+                                       $this->sql_report('stop', $query);
+                               }
+
+                               if ($cache_ttl && method_exists($cache, 'sql_save'))
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                                       $cache->sql_save($query, $this->query_result, $cache_ttl);
+                                       // odbc_free_result called within sql_save()
+                               }
+                               else if (strpos($query, 'SELECT') !== false && $this->query_result)
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                               }
+                       }
+                       else if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('fromcache', $query);
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+
+               return ($this->query_result) ? $this->query_result : false;
+       }
+
+       function _odbc_execute_query($query)
+       {
+               $result = false;
+               
+               if (eregi("^SELECT ", $query))
+               {
+                       $result = @odbc_exec($this->db_connect_id, $query); 
+
+                       if ($result)
+                       {
+                               if (empty($this->field_names[$result]))
+                               {
+                                       for ($i = 1, $j = @odbc_num_fields($result) + 1; $i < $j; $i++)
+                                       {
+                                               $this->field_names[$result][] = @odbc_field_name($result, $i);
+                                               $this->field_types[$result][] = @odbc_field_type($result, $i);
+                                       }
+                               }
+
+                               $this->current_row[$result] = 0;
+                               $this->result_rowset[$result] = array();
+
+                               $row_outer = (isset($row_offset)) ? $row_offset + 1 : 1;
+                               $row_outer_max = (isset($num_rows)) ? $row_offset + $num_rows + 1 : 1E9;
+                               $row_inner = 0;
+
+                               while (@odbc_fetch_row($result, $row_outer) && $row_outer < $row_outer_max)
+                               {
+                                       for ($i = 0, $j = sizeof($this->field_names[$result]); $i < $j; $i++)
+                                       {
+                                               $this->result_rowset[$result][$row_inner][$this->field_names[$result][$i]] = stripslashes(@odbc_result($result, $i + 1));
+                                       }
+
+                                       $row_outer++;
+                                       $row_inner++;
+                               }
+
+                               $this->num_rows[$result] = sizeof($this->result_rowset[$result]);       
+                       }
+               }
+               else if (eregi("^INSERT ", $query))
+               {
+                       $result = @odbc_exec($this->db_connect_id, $query);
+
+                       if ($result)
+                       {
+                               $result_id = @odbc_exec($this->db_connect_id, 'SELECT @@IDENTITY');
+                               if ($result_id)
+                               {
+                                       if (@odbc_fetch_row($result_id))
+                                       {
+                                               $this->next_id[$this->db_connect_id] = @odbc_result($result_id, 1);     
+                                               $this->affected_rows[$this->db_connect_id] = @odbc_num_rows($result);
+                                       }
+                               }
+                       }
+               }
+               else
+               {
+                       $result = @odbc_exec($this->db_connect_id, $query);
+
+                       if ($result)
+                       {
+                               $this->affected_rows[$this->db_connect_id] = @odbc_num_rows($result);
+                       }
+               }
+
+               return $result;
+       }
+
+       function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) 
+       { 
+               if ($query != '') 
+               {
+                       $this->query_result = false; 
+
+                       // if $total is set to 0 we do not want to limit the number of rows
+                       if ($total == 0)
+                       {
+                               $total = -1;
+                       }
+
+                       $row_offset = ($total) ? $offset : '';
+                       $num_rows = ($total) ? $total : $offset;
+
+                       $query = 'SELECT TOP ' . ($row_offset + $num_rows) . ' ' . substr($query, 6);
+
+                       return $this->sql_query($query, $cache_ttl); 
+               } 
+               else 
+               { 
+                       return false; 
+               } 
+       }
+
+       // Idea for this from Ikonboard
+       function sql_build_array($query, $assoc_ary = false)
+       {
+               if (!is_array($assoc_ary))
+               {
+                       return false;
+               }
+
+               $fields = array();
+               $values = array();
+               if ($query == 'INSERT')
+               {
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               $fields[] = $key;
+
+                               if (is_null($var))
+                               {
+                                       $values[] = 'NULL';
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "'" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? intval($var) : $var;
+                               }
+                       }
+
+                       $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+               }
+               else if ($query == 'UPDATE' || $query == 'SELECT')
+               {
+                       $values = array();
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               if (is_null($var))
+                               {
+                                       $values[] = "$key = NULL";
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "$key = '" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+                               }
+                       }
+                       $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+               }
+
+               return $query;
+       }
+
+       // Other query methods
+       //
+       // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+       //         don't want this here by a middle Milestone
+       function sql_numrows($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($query_id) ? @$this->num_rows($query_id) : false;
+       }
+
+       function sql_affectedrows()
+       {
+               return ($this->affected_rows[$this->db_connect_id]) ? $this->affected_rows[$this->db_connect_id] : false;
+       }
+
+       function sql_fetchrow($query_id = false)
+       {
+               global $cache;
+
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($cache->sql_rowset[$query_id]))
+               {
+                       return $cache->sql_fetchrow($query_id);
+               }
+
+               return ($this->num_rows[$query_id] && $this->current_row[$query_id] < $this->num_rows[$query_id]) ? $this->result_rowset[$query_id][$this->current_row[$query_id]++] : false;
+       }
+
+       function sql_fetchrowset($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($this->num_rows[$query_id]) ? $this->result_rowset[$query_id] : false;
+       }
+
+       function sql_fetchfield($field, $rownum = -1, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       if ($rownum < $this->num_rows[$query_id])
+                       {
+                               $getrow = ($rownum == -1) ? $this->current_row[$query_id] - 1 : $rownum;
+
+                               return $this->result_rowset[$query_id][$getrow][$this->field_names[$query_id][$field]];
+                       }
+               }
+
+               return false;
+       }
+
+       function sql_rowseek($rownum, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($this->current_row[$query_id]))
+               {
+                       $this->current_row[$query_id] = $rownum;
+                       return true;
+               }
+
+               return false;
+       }
+
+       function sql_nextid()
+       {
+               return ($this->next_id[$this->db_connect_id]) ? $this->next_id[$this->db_connect_id] : false;
+       }
+
+       function sql_freeresult($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($this->open_queries[(int) $query_id]))
+               {
+                       unset($this->open_queries[(int) $query_id]);
+                       unset($this->num_rows[$query_id]);
+                       unset($this->current_row[$query_id]);
+                       unset($this->result_rowset[$query_id]);
+                       unset($this->field_names[$query_id]);
+                       unset($this->field_types[$query_id]);
+
+                       return @odbc_free_result($query_id);
+               }
+
+               return false;
+       }
+
+       function sql_escape($msg)
+       {
+               return str_replace("'", "''", str_replace('\\', '\\\\', $msg));
+       }
+
+       function sql_error($sql = '')
+       {
+               if (!$this->return_on_error)
+               {
+                       $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+                       $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+                       $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @odbc_errormsg() . '<br /><br /><u>CALLING PAGE</u><br /><br />'  . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+                       if ($this->transaction)
+                       {
+                               $this->sql_transaction('rollback');
+                       }
+                       
+                       trigger_error($message, E_USER_ERROR);
+               }
+
+               $result = array(
+                       'message'       => @odbc_errormsg(),
+                       'code'          => @odbc_error()
+               );
+
+               return $result;
+       }
+
+       function sql_report($mode, $query = '')
+       {
+               if (empty($_GET['explain']))
+               {
+                       return;
+               }
+
+               global $cache, $starttime, $phpbb_root_path;
+               static $curtime, $query_hold, $html_hold;
+               static $sql_report = '';
+               static $cache_num_queries = 0;
+
+               if (!$query && !empty($query_hold))
+               {
+                       $query = $query_hold;
+               }
+
+               switch ($mode)
+               {
+                       case 'display':
+                               if (!empty($cache))
+                               {
+                                       $cache->unload();
+                               }
+                               $this->sql_close();
+
+                               $mtime = explode(' ', microtime());
+                               $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+                               echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+                               echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+                               echo 'td.cat    { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+                               echo '</style><title>' . $msg_title . '</title></head><body>';
+                               echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+                               echo $sql_report;
+                               echo '</td></tr></table><br /></body></html>';
+                               exit;
+                               break;
+
+                       case 'start':
+                               $query_hold = $query;
+                               $html_hold = '';
+
+                               $curtime = explode(' ', microtime());
+                               $curtime = $curtime[0] + $curtime[1];
+                               break;
+
+                       case 'fromcache':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $result = $this->_odbc_execute_query($query);
+
+                               $splittime = explode(' ', microtime());
+                               $splittime = $splittime[0] + $splittime[1];
+
+                               $time_cache = $endtime - $curtime;
+                               $time_db = $splittime - $endtime;
+                               $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+                               $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+                               // Pad the start time to not interfere with page timing
+                               $starttime += $time_db;
+
+                               @odbc_free_result($result);
+                               $cache_num_queries++;
+                               break;
+
+                       case 'stop':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+                               if ($this->query_result)
+                               {
+                                       if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+                                       {
+                                               $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+                                       }
+                                       $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+                               }
+                               else
+                               {
+                                       $error = $this->sql_error();
+                                       $sql_report .= '<b style="color: red">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+                               }
+
+                               $sql_report .= '</p>';
+
+                               $this->sql_time += $endtime - $curtime;
+                               break;
+               }
+       }
+
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/mssql.php b/includes/db/mssql.php
new file mode 100644 (file)
index 0000000..2b17b9e
--- /dev/null
@@ -0,0 +1,551 @@
+<?php
+/** 
+*
+* @package dbal_mssql
+* @version $Id: mssql.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mssql');
+
+/**
+* @package dbal_mssql
+* MSSQL Database Abstraction Layer
+* Minimum Requirement is MSSQL 2000+
+*/
+class sql_db
+{
+       var $db_connect_id;
+       var $query_result;
+       var $return_on_error = false;
+       var $transaction = false;
+       var $sql_time = 0;
+       var $num_queries = 0;
+       var $open_queries = array();
+
+       function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->server = $sqlserver . (($port) ? ':' . $port : '');
+               $this->dbname = $database;
+
+               $this->db_connect_id = ($this->persistency) ? @mssql_pconnect($this->server, $this->user, $sqlpassword) : @mssql_connect($this->server, $this->user, $sqlpassword);
+
+               if ($this->db_connect_id && $this->dbname != '')
+               {
+                       if (!@mssql_select_db($this->dbname, $this->db_connect_id))
+                       {
+                               @mssql_close($this->db_connect_id);
+                               return false;
+                       }
+               }
+
+               return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+       }
+
+       function sql_close()
+       {
+               if (!$this->db_connect_id)
+               {
+                       return false;
+               }
+
+               if ($this->transaction)
+               {
+                       @mssql_query('COMMIT', $this->db_connect_id);
+               }
+
+               if (sizeof($this->open_queries))
+               {
+                       foreach ($this->open_queries as $i_query_id => $query_id)
+                       {
+                               @mssql_free_result($query_id);
+                       }
+               }
+
+               return @mssql_close($this->db_connect_id);
+       }
+
+       function sql_return_on_error($fail = false)
+       {
+               $this->return_on_error = $fail;
+       }
+
+       function sql_num_queries()
+       {
+               return $this->num_queries;
+       }
+
+       function sql_transaction($status = 'begin')
+       {
+               switch ($status)
+               {
+                       case 'begin':
+                               $result = @mssql_query('BEGIN TRANSACTION', $this->db_connect_id);
+                               $this->transaction = true;
+                               break;
+
+                       case 'commit':
+                               $result = @mssql_query('commit', $this->db_connect_id);
+                               $this->transaction = false;
+
+                               if (!$result)
+                               {
+                                       @mssql_query('ROLLBACK', $this->db_connect_id);
+                               }
+                               break;
+
+                       case 'rollback':
+                               $result = @mssql_query('ROLLBACK', $this->db_connect_id);
+                               $this->transaction = false;
+                               break;
+
+                       default:
+                               $result = true;
+               }
+
+               return $result;
+       }
+
+       // Base query method
+       function sql_query($query = '', $cache_ttl = 0)
+       {
+               if ($query != '')
+               {
+                       global $cache;
+
+                       // EXPLAIN only in extra debug mode
+                       if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('start', $query);
+                       }
+
+                       $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+                       if (!$this->query_result)
+                       {
+                               $this->num_queries++;
+                               
+                               if (($this->query_result = @mssql_query($query, $this->db_connect_id)) === false)
+                               {
+                                       $this->sql_error($query);
+                               }
+
+                               if (defined('DEBUG_EXTRA'))
+                               {
+                                       $this->sql_report('stop', $query);
+                               }
+
+                               if ($cache_ttl && method_exists($cache, 'sql_save'))
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                                       $cache->sql_save($query, $this->query_result, $cache_ttl);
+                                       // sql_freeresult called within sql_save()
+                               }
+                               else if (strpos($query, 'SELECT') !== false && $this->query_result)
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                               }
+                       }
+                       else if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('fromcache', $query);
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+
+               return ($this->query_result) ? $this->query_result : false;
+       }
+
+       function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) 
+       { 
+               if ($query != '') 
+               {
+                       $this->query_result = false; 
+
+                       // if $total is set to 0 we do not want to limit the number of rows
+                       if ($total == 0)
+                       {
+                               $total = -1;
+                       }
+
+                       $row_offset = ($total) ? $offset : '';
+                       $num_rows = ($total) ? $total : $offset;
+
+                       $query = 'SELECT TOP ' . ($row_offset + $num_rows) . ' ' . substr($query, 6);
+
+                       return $this->sql_query($query, $cache_ttl); 
+               } 
+               else 
+               { 
+                       return false; 
+               } 
+       }
+
+       // Idea for this from Ikonboard
+       function sql_build_array($query, $assoc_ary = false)
+       {
+               if (!is_array($assoc_ary))
+               {
+                       return false;
+               }
+
+               $fields = array();
+               $values = array();
+               if ($query == 'INSERT')
+               {
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               $fields[] = $key;
+
+                               if (is_null($var))
+                               {
+                                       $values[] = 'NULL';
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "'" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? intval($var) : $var;
+                               }
+                       }
+
+                       $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+               }
+               else if ($query == 'UPDATE' || $query == 'SELECT')
+               {
+                       $values = array();
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               if (is_null($var))
+                               {
+                                       $values[] = "$key = NULL";
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "$key = '" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+                               }
+                       }
+                       $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+               }
+
+               return $query;
+       }
+
+       // Other query methods
+       //
+       // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+       //         don't want this here by a middle Milestone
+       function sql_numrows($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+//             return (isset($this->limit_offset[$query_id])) ? @mssql_num_rows($query_id) - $this->limit_offset[$query_id] : @mssql_num_rows($query_id);
+               return ($query_id) ? @mssql_num_rows($query_id) : false;
+       }
+
+       function sql_affectedrows()
+       {
+               return ($this->db_connect_id) ? @mssql_rows_affected($this->db_connect_id) : false;
+       }
+
+       function sql_fetchrow($query_id = false)
+       {
+               global $cache;
+
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($cache->sql_rowset[$query_id]))
+               {
+                       return $cache->sql_fetchrow($query_id);
+               }
+
+               $row = @mssql_fetch_array($query_id, MSSQL_ASSOC);
+               
+               if ($row)
+               {
+                       foreach ($row as $key => $value)
+                       {
+                               $row[$key] = ($value === ' ') ? trim($value) : $value;
+                       }
+               }
+
+               return $row;
+       }
+
+       function sql_fetchrowset($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       unset($this->rowset[$query_id]);
+                       unset($this->row[$query_id]);
+
+                       $result = array();
+                       while ($this->rowset[$query_id] = $this->sql_fetchrow($query_id))
+                       {
+                               $result[] = $this->rowset[$query_id];
+                       }
+                       return $result;
+               }
+
+               return false;
+       }
+
+       function sql_fetchfield($field, $rownum = -1, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       if ($rownum > -1)
+                       {
+//                             (!empty($this->limit_offset[$query_id])) ? @mssql_data_seek($query_id, ($this->limit_offset[$query_id] + $rownum)) : @mssql_data_seek($query_id, $rownum);
+                               @mssql_data_seek($query_id, $rownum);
+                               $row = @mssql_fetch_array($query_id, MSSQL_ASSOC);
+                               $result = isset($row[$field]) ? $row[$field] : false;
+                       }
+                       else
+                       {
+                               if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+                               {
+                                       if ($this->sql_fetchrow($query_id))
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                               else
+                               {
+                                       if ($this->rowset[$query_id])
+                                       {
+                                               $result = $this->rowset[$query_id][$field];
+                                       }
+                                       elseif ($this->row[$query_id])
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                       }
+
+                       return $result;
+               }
+
+               return false;
+       }
+
+       function sql_rowseek($rownum, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($this->current_row[$query_id]))
+               {
+//                     (!empty($this->limit_offset[$query_id])) ? @mssql_data_seek($query_id, ($this->limit_offset[$query_id] + $rownum)) : @mssql_data_seek($query_id, $rownum);
+                       @mssql_data_seek($query_id, $rownum);
+                       return true;
+               }
+
+               return false;
+       }
+
+       function sql_nextid()
+       {
+               $result_id = @mssql_query('SELECT @@IDENTITY', $this->db_connect_id);
+               if ($result_id)
+               {
+                       if (@mssql_fetch_array($result_id, MSSQL_ASSOC))
+                       {
+                               return @mssql_result($result_id, 1);    
+                       }
+               }
+
+               return false;
+       }
+
+       function sql_freeresult($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($this->open_queries[$query_id]))
+               {
+                       unset($this->open_queries[$query_id]);
+                       unset($this->result_rowset[$query_id]);
+
+                       return @mssql_free_result($query_id);
+               }
+
+               return false;
+       }
+
+       function sql_escape($msg)
+       {
+               return str_replace("'", "''", str_replace('\\', '\\\\', $msg));
+       }
+
+       function sql_error($sql = '')
+       {
+               if (!$this->return_on_error)
+               {
+                       $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+                       $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+                       $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mssql_get_last_message() . '<br /><br /><u>CALLING PAGE</u><br /><br />'  . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+                       if ($this->transaction)
+                       {
+                               $this->sql_transaction('rollback');
+                       }
+                       
+                       trigger_error($message, E_USER_ERROR);
+               }
+
+               $result = array(
+                       'message'       => @mssql_get_last_message($this->db_connect_id),
+                       'code'          => ''
+               );
+
+               return $result;
+       }
+
+       function sql_report($mode, $query = '')
+       {
+               if (empty($_GET['explain']))
+               {
+                       return;
+               }
+
+               global $cache, $starttime, $phpbb_root_path;
+               static $curtime, $query_hold, $html_hold;
+               static $sql_report = '';
+               static $cache_num_queries = 0;
+
+               if (!$query && !empty($query_hold))
+               {
+                       $query = $query_hold;
+               }
+
+               switch ($mode)
+               {
+                       case 'display':
+                               if (!empty($cache))
+                               {
+                                       $cache->unload();
+                               }
+                               $this->sql_close();
+
+                               $mtime = explode(' ', microtime());
+                               $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+                               echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+                               echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+                               echo 'td.cat    { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+                               echo '</style><title>' . $msg_title . '</title></head><body>';
+                               echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+                               echo $sql_report;
+                               echo '</td></tr></table><br /></body></html>';
+                               exit;
+                               break;
+
+                       case 'start':
+                               $query_hold = $query;
+                               $html_hold = '';
+
+                               $curtime = explode(' ', microtime());
+                               $curtime = $curtime[0] + $curtime[1];
+                               break;
+
+                       case 'fromcache':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $result = @mssql_query($query, $this->db_connect_id);
+                               while ($void = @mssql_fetch_array($result, MSSQL_ASSOC))
+                               {
+                                       // Take the time spent on parsing rows into account
+                               }
+                               $splittime = explode(' ', microtime());
+                               $splittime = $splittime[0] + $splittime[1];
+
+                               $time_cache = $endtime - $curtime;
+                               $time_db = $splittime - $endtime;
+                               $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+                               $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+                               // Pad the start time to not interfere with page timing
+                               $starttime += $time_db;
+
+                               @mssql_free_result($result);
+                               $cache_num_queries++;
+                               break;
+
+                       case 'stop':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+                               if ($this->query_result)
+                               {
+                                       if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+                                       {
+                                               $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+                                       }
+                                       $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+                               }
+                               else
+                               {
+                                       $error = $this->sql_error();
+                                       $sql_report .= '<b style="color: red">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+                               }
+
+                               $sql_report .= '</p>';
+
+                               $this->sql_time += $endtime - $curtime;
+                               break;
+               }
+       }
+
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/mysql.php b/includes/db/mysql.php
new file mode 100644 (file)
index 0000000..a646e0d
--- /dev/null
@@ -0,0 +1,552 @@
+<?php
+/** 
+*
+* @package dbal_mysql
+* @version $Id: mysql.php,v 1.5 2006/02/10 01:30:19 scronide Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mysql');
+
+/**
+* @package dbal_mysql
+* MySQL Database Abstraction Layer
+* Minimum Requirement is 3.23+/4.0+/4.1+
+*/
+class sql_db
+{
+       var $db_connect_id;
+       var $query_result;
+       var $return_on_error = false;
+       var $transaction = false;
+       var $sql_time = 0;
+       var $num_queries = 0;
+       var $open_queries = array();
+
+       function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->server = $sqlserver . (($port) ? ':' . $port : '');
+               $this->dbname = $database;
+
+               $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword) : @mysql_connect($this->server, $this->user, $sqlpassword);
+
+               if ($this->db_connect_id && $this->dbname != '')
+               {
+                       if (@mysql_select_db($this->dbname))
+                       {
+                               return $this->db_connect_id;
+                       }
+               }
+
+               return $this->sql_error('');
+       }
+
+       //
+       // Other base methods
+       //
+       function sql_close()
+       {
+               if (!$this->db_connect_id)
+               {
+                       return false;
+               }
+
+               if (sizeof($this->open_queries))
+               {
+                       foreach ($this->open_queries as $i_query_id => $query_id)
+                       {
+                               @mysql_free_result($query_id);
+                       }
+               }
+
+               return @mysql_close($this->db_connect_id);
+       }
+
+       function sql_return_on_error($fail = false)
+       {
+               $this->return_on_error = $fail;
+       }
+
+       function sql_num_queries()
+       {
+               return $this->num_queries;
+       }
+
+       function sql_transaction($status = 'begin')
+       {
+               switch ($status)
+               {
+                       case 'begin':
+                               $result = @mysql_query('BEGIN', $this->db_connect_id);
+                               $this->transaction = true;
+                               break;
+
+                       case 'commit':
+                               $result = @mysql_query('COMMIT', $this->db_connect_id);
+                               $this->transaction = false;
+                               
+                               if (!$result)
+                               {
+                                       @mysql_query('ROLLBACK', $this->db_connect_id);
+                               }
+                               break;
+
+                       case 'rollback':
+                               $result = @mysql_query('ROLLBACK', $this->db_connect_id);
+                               $this->transaction = false;
+                               break;
+
+                       default:
+                               $result = true;
+               }
+
+               return $result;
+       }
+
+       // Base query method
+       function sql_query($query = '', $cache_ttl = 0)
+       {
+               if ($query != '')
+               {
+                       global $cache;
+
+                       // EXPLAIN only in extra debug mode
+                       if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('start', $query);
+                       }
+
+                       $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+                       if (!$this->query_result)
+                       {
+                               $this->num_queries++;
+
+                               if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false)
+                               {
+                                       $this->sql_error($query);
+                               }
+
+                               if (defined('DEBUG_EXTRA'))
+                               {
+                                       $this->sql_report('stop', $query);
+                               }
+
+                               if ($cache_ttl && method_exists($cache, 'sql_save'))
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                                       $cache->sql_save($query, $this->query_result, $cache_ttl);
+                                       // mysql_free_result called within sql_save()
+                               }
+                               else if (strpos($query, 'SELECT') !== false && $this->query_result)
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                               }
+                       }
+                       else if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('fromcache', $query);
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+
+               return ($this->query_result) ? $this->query_result : false;
+       }
+
+       function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) { 
+               if ($query != '') {
+            $this->query_result = false; 
+
+                       // only limit the number of rows if $total is greater than 0
+                       if ($total > 0)
+                       $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+                       return $this->sql_query($query, $cache_ttl); 
+               } else { 
+            return false; 
+               } 
+       }
+
+       // Idea for this from Ikonboard
+       function sql_build_array($query, $assoc_ary = false)
+       {
+               if (!is_array($assoc_ary))
+               {
+                       return false;
+               }
+
+               $fields = array();
+               $values = array();
+               if ($query == 'INSERT')
+               {
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               $fields[] = $key;
+
+                               if (is_null($var))
+                               {
+                                       $values[] = 'NULL';
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "'" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? intval($var) : $var;
+                               }
+                       }
+
+                       $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+               }
+               else if ($query == 'UPDATE' || $query == 'SELECT')
+               {
+                       $values = array();
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               if (is_null($var))
+                               {
+                                       $values[] = "$key = NULL";
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "$key = '" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+                               }
+                       }
+                       $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+               }
+
+               return $query;
+       }
+
+       // Other query methods
+       //
+       // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+       //         don't want this here by a middle Milestone
+       function sql_numrows($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($query_id) ? @mysql_num_rows($query_id) : false;
+       }
+
+       function sql_affectedrows()
+       {
+               return ($this->db_connect_id) ? @mysql_affected_rows($this->db_connect_id) : false;
+       }
+
+       function sql_fetchrow($query_id = false)
+       {
+               global $cache;
+
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($cache->sql_rowset[$query_id]))
+               {
+                       return $cache->sql_fetchrow($query_id);
+               }
+
+               return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+       }
+
+       function sql_fetchrowset($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       unset($this->rowset[$query_id]);
+                       unset($this->row[$query_id]);
+
+                       $result = array();
+                       while ($this->rowset[$query_id] = $this->sql_fetchrow($query_id))
+                       {
+                               $result[] = $this->rowset[$query_id];
+                       }
+                       return $result;
+               }
+               
+               return false;
+       }
+
+       function sql_fetchfield($field, $rownum = -1, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       if ($rownum > -1)
+                       {
+                               $result = @mysql_result($query_id, $rownum, $field);
+                       }
+                       else
+                       {
+                               if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+                               {
+                                       if ($this->sql_fetchrow($query_id))
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                               else
+                               {
+                                       if ($this->rowset[$query_id])
+                                       {
+                                               $result = $this->rowset[$query_id][$field];
+                                       }
+                                       elseif ($this->row[$query_id])
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                       }
+                       return $result;
+               }
+               return false;
+       }
+
+       function sql_rowseek($rownum, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($query_id) ? @mysql_data_seek($query_id, $rownum) : false;
+       }
+
+       function sql_nextid()
+       {
+               return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false;
+       }
+
+       function sql_freeresult($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($this->open_queries[(int) $query_id]))
+               {
+                       unset($this->open_queries[(int) $query_id]);
+                       return @mysql_free_result($query_id);
+               }
+
+               return false;
+       }
+
+       function sql_escape($msg) {
+               if (function_exists('mysql_real_escape_string')) {
+                       return @mysql_real_escape_string($msg, $this->db_connect_id);
+               } else {
+                       return mysql_escape_string($msg);
+               }               
+       }
+       
+       function sql_error($sql = '')
+       {
+               if (!$this->return_on_error)
+               {
+                       $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+                       $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+                       $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mysql_error() . '<br /><br /><u>CALLING PAGE</u><br /><br />'  . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+                       if ($this->transaction)
+                       {
+                               $this->sql_transaction('rollback');
+                       }
+                       
+                       trigger_error($message, E_USER_ERROR);
+               }
+
+               $result = array(
+                       'message'       => @mysql_error(),
+                       'code'          => @mysql_errno()
+               );
+
+               return $result;
+       }
+
+       function sql_report($mode, $query = '')
+       {
+               if (empty($_GET['explain']))
+               {
+                       return;
+               }
+
+               global $db, $cache, $starttime, $phpbb_root_path;
+               static $curtime, $query_hold, $html_hold;
+               static $sql_report = '';
+               static $cache_num_queries = 0;
+
+               if (!$query && !empty($query_hold))
+               {
+                       $query = $query_hold;
+               }
+
+               switch ($mode)
+               {
+                       case 'display':
+                               if (!empty($cache))
+                               {
+                                       $cache->unload();
+                               }
+                               $db->sql_close();
+
+                               $mtime = explode(' ', microtime());
+                               $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+                               echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+                               echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+                               echo 'td.cat    { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+                               echo '</style><title>' . $msg_title . '</title></head><body>';
+                               echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+                               echo $sql_report;
+                               echo '</td></tr></table><br /></body></html>';
+                               exit;
+                               break;
+
+                       case 'start':
+                               $query_hold = $query;
+                               $html_hold = '';
+
+                               $explain_query = $query;
+                               if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+                               {
+                                       $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+                               }
+                               elseif (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+                               {
+                                       $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+                               }
+
+                               if (preg_match('/^SELECT/', $explain_query))
+                               {
+                                       $html_table = FALSE;
+
+                                       if ($result = mysql_query("EXPLAIN $explain_query", $this->db_connect_id))
+                                       {
+                                               while ($row = mysql_fetch_assoc($result))
+                                               {
+                                                       if (!$html_table && sizeof($row))
+                                                       {
+                                                               $html_table = TRUE;
+                                                               $html_hold .= '<table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0" align="center"><tr>';
+                                                               
+                                                               foreach (array_keys($row) as $val)
+                                                               {
+                                                                       $html_hold .= '<th nowrap="nowrap">' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
+                                                               }
+                                                               $html_hold .= '</tr>';
+                                                       }
+                                                       $html_hold .= '<tr>';
+
+                                                       $class = 'row1';
+                                                       foreach (array_values($row) as $val)
+                                                       {
+                                                               $class = ($class == 'row1') ? 'row2' : 'row1';
+                                                               $html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
+                                                       }
+                                                       $html_hold .= '</tr>';
+                                               }
+                                       }
+
+                                       if ($html_table)
+                                       {
+                                               $html_hold .= '</table>';
+                                       }
+                               }
+
+                               $curtime = explode(' ', microtime());
+                               $curtime = $curtime[0] + $curtime[1];
+                               break;
+
+                       case 'fromcache':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $result = mysql_query($query, $this->db_connect_id);
+                               while ($void = mysql_fetch_assoc($result))
+                               {
+                                       // Take the time spent on parsing rows into account
+                               }
+                               $splittime = explode(' ', microtime());
+                               $splittime = $splittime[0] + $splittime[1];
+
+                               $time_cache = $endtime - $curtime;
+                               $time_db = $splittime - $endtime;
+                               $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+                               $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+                               // Pad the start time to not interfere with page timing
+                               $starttime += $time_db;
+
+                               mysql_free_result($result);
+                               $cache_num_queries++;
+                               break;
+
+                       case 'stop':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+                               if ($this->query_result)
+                               {
+                                       if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+                                       {
+                                               $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+                                       }
+                                       $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+                               }
+                               else
+                               {
+                                       $error = $this->sql_error();
+                                       $sql_report .= '<b style="color: red">FAILED</b> - MySQL Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+                               }
+
+                               $sql_report .= '</p>';
+
+                               $this->sql_time += $endtime - $curtime;
+                               break;
+               }
+       }
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/mysql4.php b/includes/db/mysql4.php
new file mode 100644 (file)
index 0000000..0639518
--- /dev/null
@@ -0,0 +1,552 @@
+<?php
+/** 
+*
+* @package dbal_mysql4
+* @version $Id: mysql4.php,v 1.4 2006/02/10 01:30:19 scronide Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mysql4');
+
+/**
+* @package dbal_mysql4
+* MySQL4 Database Abstraction Layer
+* Minimum Requirement is 4.0+/4.1+
+*/
+class sql_db
+{
+       var $db_connect_id;
+       var $query_result;
+       var $return_on_error = false;
+       var $transaction = false;
+       var $sql_time = 0;
+       var $num_queries = 0;
+       var $open_queries = array();
+
+       function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->server = $sqlserver . (($port) ? ':' . $port : '');
+               $this->dbname = $database;
+
+               $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword) : @mysql_connect($this->server, $this->user, $sqlpassword);
+
+               if ($this->db_connect_id && $this->dbname != '')
+               {
+                       if (@mysql_select_db($this->dbname))
+                       {
+                               return $this->db_connect_id;
+                       }
+               }
+
+               return $this->sql_error('');
+       }
+
+       //
+       // Other base methods
+       //
+       function sql_close()
+       {
+               if (!$this->db_connect_id)
+               {
+                       return false;
+               }
+
+               if (sizeof($this->open_queries))
+               {
+                       foreach ($this->open_queries as $i_query_id => $query_id)
+                       {
+                               @mysql_free_result($query_id);
+                       }
+               }
+
+               return @mysql_close($this->db_connect_id);
+       }
+
+       function sql_return_on_error($fail = false)
+       {
+               $this->return_on_error = $fail;
+       }
+
+       function sql_num_queries()
+       {
+               return $this->num_queries;
+       }
+
+       function sql_transaction($status = 'begin')
+       {
+               switch ($status)
+               {
+                       case 'begin':
+                               $result = @mysql_query('BEGIN', $this->db_connect_id);
+                               $this->transaction = true;
+                               break;
+
+                       case 'commit':
+                               $result = @mysql_query('COMMIT', $this->db_connect_id);
+                               $this->transaction = false;
+                               
+                               if (!$result)
+                               {
+                                       @mysql_query('ROLLBACK', $this->db_connect_id);
+                               }
+                               break;
+
+                       case 'rollback':
+                               $result = @mysql_query('ROLLBACK', $this->db_connect_id);
+                               $this->transaction = false;
+                               break;
+
+                       default:
+                               $result = true;
+               }
+
+               return $result;
+       }
+
+       // Base query method
+       function sql_query($query = '', $cache_ttl = 0)
+       {
+               if ($query != '')
+               {
+                       global $cache;
+
+                       // EXPLAIN only in extra debug mode
+                       if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('start', $query);
+                       }
+
+                       $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+
+                       if (!$this->query_result)
+                       {
+                               $this->num_queries++;
+
+                               if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false)
+                               {
+                                       $this->sql_error($query);
+                               }
+
+                               if (defined('DEBUG_EXTRA'))
+                               {
+                                       $this->sql_report('stop', $query);
+                               }
+
+                               if ($cache_ttl && method_exists($cache, 'sql_save'))
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                                       $cache->sql_save($query, $this->query_result, $cache_ttl);
+                                       // mysql_free_result called within sql_save()
+                               }
+                               else if (strpos($query, 'SELECT') !== false && $this->query_result)
+                               {
+                                       $this->open_queries[(int) $this->query_result] = $this->query_result;
+                               }
+                       }
+                       else if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('fromcache', $query);
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+
+               return ($this->query_result) ? $this->query_result : false;
+       }
+
+       function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) { 
+               if ($query != '') {
+            $this->query_result = false; 
+
+                       // only limit the number of rows if $total is greater than 0
+                       if ($total > 0)
+                       $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+                       return $this->sql_query($query, $cache_ttl); 
+               } else { 
+            return false; 
+               } 
+       }
+
+       // Idea for this from Ikonboard
+       function sql_build_array($query, $assoc_ary = false)
+       {
+               if (!is_array($assoc_ary))
+               {
+                       return false;
+               }
+
+               $fields = array();
+               $values = array();
+               if ($query == 'INSERT')
+               {
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               $fields[] = $key;
+
+                               if (is_null($var))
+                               {
+                                       $values[] = 'NULL';
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "'" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? intval($var) : $var;
+                               }
+                       }
+
+                       $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+               }
+               else if ($query == 'UPDATE' || $query == 'SELECT')
+               {
+                       $values = array();
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               if (is_null($var))
+                               {
+                                       $values[] = "$key = NULL";
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "$key = '" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+                               }
+                       }
+                       $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+               }
+
+               return $query;
+       }
+
+       // Other query methods
+       //
+       // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+       //         don't want this here by a middle Milestone
+       function sql_numrows($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($query_id) ? @mysql_num_rows($query_id) : false;
+       }
+
+       function sql_affectedrows()
+       {
+               return ($this->db_connect_id) ? @mysql_affected_rows($this->db_connect_id) : false;
+       }
+
+       function sql_fetchrow($query_id = false)
+       {
+               global $cache;
+
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($cache->sql_rowset[$query_id]))
+               {
+                       return $cache->sql_fetchrow($query_id);
+               }
+
+               return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+       }
+
+       function sql_fetchrowset($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       unset($this->rowset[$query_id]);
+                       unset($this->row[$query_id]);
+
+                       $result = array();
+                       while ($this->rowset[$query_id] = $this->sql_fetchrow($query_id))
+                       {
+                               $result[] = $this->rowset[$query_id];
+                       }
+                       return $result;
+               }
+               
+               return false;
+       }
+
+       function sql_fetchfield($field, $rownum = -1, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       if ($rownum > -1)
+                       {
+                               $result = @mysql_result($query_id, $rownum, $field);
+                       }
+                       else
+                       {
+                               if (empty($this->row[$query_id]) && empty($this->rowset[$query_id]))
+                               {
+                                       if ($this->sql_fetchrow($query_id))
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                               else
+                               {
+                                       if ($this->rowset[$query_id])
+                                       {
+                                               $result = $this->rowset[$query_id][$field];
+                                       }
+                                       elseif ($this->row[$query_id])
+                                       {
+                                               $result = $this->row[$query_id][$field];
+                                       }
+                               }
+                       }
+                       return $result;
+               }
+               return false;
+       }
+
+       function sql_rowseek($rownum, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($query_id) ? @mysql_data_seek($query_id, $rownum) : false;
+       }
+
+       function sql_nextid()
+       {
+               return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false;
+       }
+
+       function sql_freeresult($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (isset($this->open_queries[(int) $query_id]))
+               {
+                       unset($this->open_queries[(int) $query_id]);
+                       return @mysql_free_result($query_id);
+               }
+
+               return false;
+       }
+
+       function sql_escape($msg) {
+               if (function_exists('mysql_real_escape_string')) {
+                       return @mysql_real_escape_string($msg, $this->db_connect_id);
+               } else {
+                       return mysql_escape_string($msg);
+               }               
+       }
+       
+       function sql_error($sql = '')
+       {
+               if (!$this->return_on_error)
+               {
+                       $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+                       $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+                       $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mysql_error() . '<br /><br /><u>CALLING PAGE</u><br /><br />'  . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+                       if ($this->transaction)
+                       {
+                               $this->sql_transaction('rollback');
+                       }
+                       
+                       trigger_error($message, E_USER_ERROR);
+               }
+
+               $result = array(
+                       'message'       => @mysql_error(),
+                       'code'          => @mysql_errno()
+               );
+
+               return $result;
+       }
+
+       function sql_report($mode, $query = '')
+       {
+               if (empty($_GET['explain']))
+               {
+                       return;
+               }
+
+               global $db, $cache, $starttime, $phpbb_root_path;
+               static $curtime, $query_hold, $html_hold;
+               static $sql_report = '';
+               static $cache_num_queries = 0;
+
+               if (!$query && !empty($query_hold))
+               {
+                       $query = $query_hold;
+               }
+
+               switch ($mode)
+               {
+                       case 'display':
+                               if (!empty($cache))
+                               {
+                                       $cache->unload();
+                               }
+                               $db->sql_close();
+
+                               $mtime = explode(' ', microtime());
+                               $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+                               echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+                               echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+                               echo 'td.cat    { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+                               echo '</style><title>' . $msg_title . '</title></head><body>';
+                               echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+                               echo $sql_report;
+                               echo '</td></tr></table><br /></body></html>';
+                               exit;
+                               break;
+
+                       case 'start':
+                               $query_hold = $query;
+                               $html_hold = '';
+
+                               $explain_query = $query;
+                               if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+                               {
+                                       $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+                               }
+                               elseif (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+                               {
+                                       $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+                               }
+
+                               if (preg_match('/^SELECT/', $explain_query))
+                               {
+                                       $html_table = FALSE;
+
+                                       if ($result = mysql_query("EXPLAIN $explain_query", $this->db_connect_id))
+                                       {
+                                               while ($row = mysql_fetch_assoc($result))
+                                               {
+                                                       if (!$html_table && sizeof($row))
+                                                       {
+                                                               $html_table = TRUE;
+                                                               $html_hold .= '<table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0" align="center"><tr>';
+                                                               
+                                                               foreach (array_keys($row) as $val)
+                                                               {
+                                                                       $html_hold .= '<th nowrap="nowrap">' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
+                                                               }
+                                                               $html_hold .= '</tr>';
+                                                       }
+                                                       $html_hold .= '<tr>';
+
+                                                       $class = 'row1';
+                                                       foreach (array_values($row) as $val)
+                                                       {
+                                                               $class = ($class == 'row1') ? 'row2' : 'row1';
+                                                               $html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
+                                                       }
+                                                       $html_hold .= '</tr>';
+                                               }
+                                       }
+
+                                       if ($html_table)
+                                       {
+                                               $html_hold .= '</table>';
+                                       }
+                               }
+
+                               $curtime = explode(' ', microtime());
+                               $curtime = $curtime[0] + $curtime[1];
+                               break;
+
+                       case 'fromcache':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $result = mysql_query($query, $this->db_connect_id);
+                               while ($void = mysql_fetch_assoc($result))
+                               {
+                                       // Take the time spent on parsing rows into account
+                               }
+                               $splittime = explode(' ', microtime());
+                               $splittime = $splittime[0] + $splittime[1];
+
+                               $time_cache = $endtime - $curtime;
+                               $time_db = $splittime - $endtime;
+                               $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+                               $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+                               // Pad the start time to not interfere with page timing
+                               $starttime += $time_db;
+
+                               mysql_free_result($result);
+                               $cache_num_queries++;
+                               break;
+
+                       case 'stop':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+                               if ($this->query_result)
+                               {
+                                       if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+                                       {
+                                               $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+                                       }
+                                       $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+                               }
+                               else
+                               {
+                                       $error = $this->sql_error();
+                                       $sql_report .= '<b style="color: red">FAILED</b> - MySQL Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+                               }
+
+                               $sql_report .= '</p>';
+
+                               $this->sql_time += $endtime - $curtime;
+                               break;
+               }
+       }
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/mysqli.php b/includes/db/mysqli.php
new file mode 100644 (file)
index 0000000..fa643c2
--- /dev/null
@@ -0,0 +1,566 @@
+<?php
+/** 
+*
+* @package dbal_mysqli
+* @version $Id: mysqli.php,v 1.4 2006/02/10 01:30:19 scronide Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'mysqli');
+
+/**
+* @package dbal_mysqli
+* MySQLi Database Abstraction Layer
+* Minimum Requirement is MySQL 4.1+ and the mysqli-extension
+*/
+class sql_db
+{
+       var $db_connect_id;
+       var $query_result;
+       var $return_on_error = false;
+       var $transaction = false;
+       var $sql_time = 0;
+       var $num_queries = 0;
+       var $open_queries = array();
+
+       var $indexed = 0;
+
+       function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->server = $sqlserver . (($port) ? ':' . $port : '');
+               $this->dbname = $database;
+
+               $this->db_connect_id = ($this->persistency) ? @mysqli_pconnect($this->server, $this->user, $sqlpassword) : @mysqli_connect($this->server, $this->user, $sqlpassword);
+
+               if ($this->db_connect_id && $this->dbname != '')
+               {
+                       if (@mysqli_select_db($this->db_connect_id, $this->dbname))
+                       {
+                               return $this->db_connect_id;
+                       }
+               }
+
+               return $this->sql_error('');
+       }
+
+       //
+       // Other base methods
+       //
+       function sql_close()
+       {
+               if (!$this->db_connect_id)
+               {
+                       return false;
+               }
+
+               if ($this->transaction)
+               {
+                       @mysqli_commit($this->db_connect_id);
+               }
+
+               return @mysqli_close($this->db_connect_id);
+       }
+
+       function sql_return_on_error($fail = false)
+       {
+               $this->return_on_error = $fail;
+       }
+
+       function sql_num_queries()
+       {
+               return $this->num_queries;
+       }
+
+       function sql_transaction($status = 'begin')
+       {
+               switch ($status)
+               {
+                       case 'begin':
+                               $result = @mysqli_autocommit($this->db_connect_id, false);
+                               $this->transaction = true;
+                               break;
+
+                       case 'commit':
+                               $result = @mysqli_commit($this->db_connect_id);
+                               @mysqli_autocommit($this->db_connect_id, true);
+                               $this->transaction = false;
+
+                               if (!$result)
+                               {
+                                       @mysqli_rollback($this->db_connect_id);
+                                       @mysqli_autocommit($this->db_connect_id, true);
+                               }
+                               break;
+
+                       case 'rollback':
+                               $result = @mysqli_rollback($this->db_connect_id);
+                               @mysqli_autocommit($this->db_connect_id, true);
+                               $this->transaction = false;
+                               break;
+
+                       default:
+                               $result = true;
+               }
+
+               return $result;
+       }
+
+       // Base query method
+       function sql_query($query = '', $cache_ttl = 0)
+       {
+               if ($query != '')
+               {
+                       global $cache;
+
+                       // EXPLAIN only in extra debug mode
+                       if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('start', $query);
+                       }
+
+                       $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
+                       
+                       if (!$this->query_result)
+                       {
+                               $this->num_queries++;
+
+                               if (($this->query_result = @mysqli_query($this->db_connect_id, $query)) === false)
+                               {
+                                       $this->sql_error($query);
+                               }
+
+                               if (is_object($this->query_result))
+                               {
+                                       $this->query_result->cur_index = $this->indexed++;
+                               }
+
+                               if (defined('DEBUG_EXTRA'))
+                               {
+                                       $this->sql_report('stop', $query);
+                               }
+
+                               if ($cache_ttl && method_exists($cache, 'sql_save'))
+                               {
+                                       $cache->sql_save($query, $this->query_result, $cache_ttl);
+                               }
+                       }
+                       else if (defined('DEBUG_EXTRA'))
+                       {
+                               $this->sql_report('fromcache', $query);
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+
+               return ($this->query_result) ? $this->query_result : false;
+       }
+
+       function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) { 
+               if ($query != '') {
+            $this->query_result = false; 
+
+                       // only limit the number of rows if $total is greater than 0
+                       if ($total > 0)
+                       $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+                       return $this->sql_query($query, $cache_ttl); 
+               } else { 
+            return false; 
+               } 
+       }
+
+       // Idea for this from Ikonboard
+       function sql_build_array($query, $assoc_ary = false)
+       {
+               if (!is_array($assoc_ary))
+               {
+                       return false;
+               }
+
+               $fields = array();
+               $values = array();
+               if ($query == 'INSERT')
+               {
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               $fields[] = $key;
+
+                               if (is_null($var))
+                               {
+                                       $values[] = 'NULL';
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "'" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? intval($var) : $var;
+                               }
+                       }
+
+                       $query = ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
+               }
+               else if ($query == 'UPDATE' || $query == 'SELECT')
+               {
+                       $values = array();
+                       foreach ($assoc_ary as $key => $var)
+                       {
+                               if (is_null($var))
+                               {
+                                       $values[] = "$key = NULL";
+                               }
+                               elseif (is_string($var))
+                               {
+                                       $values[] = "$key = '" . $this->sql_escape($var) . "'";
+                               }
+                               else
+                               {
+                                       $values[] = (is_bool($var)) ? "$key = " . intval($var) : "$key = $var";
+                               }
+                       }
+                       $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+               }
+
+               return $query;
+       }
+
+       // Other query methods
+       //
+       // NOTE :: Want to remove _ALL_ reliance on sql_numrows from core code ...
+       //         don't want this here by a middle Milestone
+       function sql_numrows($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($query_id) ? @mysqli_num_rows($query_id) : false;
+       }
+
+       function sql_affectedrows()
+       {
+               return ($this->db_connect_id) ? @mysqli_affected_rows($this->db_connect_id) : false;
+       }
+
+       function sql_fetchrow($query_id = false)
+       {
+               global $cache;
+
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
+               {
+                       return $cache->sql_fetchrow($query_id);
+               }
+
+               return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+       }
+
+       function sql_fetchrowset($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       $cur_index = (is_object($query_id)) ? $query_id->cur_index : $query_id;
+
+                       unset($this->rowset[$cur_index]);
+                       unset($this->row[$cur_index]);
+                       
+                       $result = array();
+                       while ($this->rowset[$cur_index] = $this->sql_fetchrow($query_id))
+                       {
+                               $result[] = $this->rowset[$cur_index];
+                       }
+                       return $result;
+               }
+
+               return false;
+       }
+
+       function sql_fetchfield($field, $rownum = -1, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               if ($query_id)
+               {
+                       if ($rownum > -1)
+                       {
+                               @mysqli_data_seek($query_id, $rownum);
+                               $row = @mysqli_fetch_assoc($query_id);
+                               $result = isset($row[$field]) ? $row[$field] : false;
+                       }
+                       else
+                       {
+                               $cur_index = (is_object($query_id)) ? $query_id->cur_index : $query_id;
+       
+                               if (empty($this->row[$cur_index]) && empty($this->rowset[$cur_index]))
+                               {
+                                       if ($this->row[$cur_index] = $this->sql_fetchrow($query_id))
+                                       {
+                                               $result = $this->row[$cur_index][$field];
+                                       }
+                               }
+                               else
+                               {
+                                       if ($this->rowset[$cur_index])
+                                       {
+                                               $result = $this->rowset[$cur_index][$field];
+                                       }
+                                       elseif ($this->row[$cur_index])
+                                       {
+                                               $result = $this->row[$cur_index][$field];
+                                       }
+                               }
+                       }
+                       return $result;
+               }
+               return false;
+       }
+
+       function sql_rowseek($rownum, $query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               return ($query_id) ? @mysqli_data_seek($query_id, $rownum) : false;
+       }
+
+       function sql_nextid()
+       {
+               return ($this->db_connect_id) ? @mysqli_insert_id($this->db_connect_id) : false;
+       }
+
+       function sql_freeresult($query_id = false)
+       {
+               if (!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+
+               $cur_index = (is_object($query_id)) ? $query_id->cur_index : $query_id;
+
+               unset($this->rowset[$cur_index]);
+               unset($this->row[$cur_index]);
+
+               if (is_object($query_id))
+               {
+                       $this->indexed--;
+                       return @mysqli_free_result($query_id);
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+       function sql_escape($msg) {
+               if (function_exists('mysql_real_escape_string')) {
+                       return @mysql_real_escape_string($msg, $this->db_connect_id);
+               } else {
+                       return mysql_escape_string($msg);
+               }               
+       }
+       
+       function sql_error($sql = '')
+       {
+               if (!$this->return_on_error)
+               {
+                       $this_page = (isset($_SERVER['PHP_SELF']) && !empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
+                       $this_page .= '&' . ((isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : (isset($_ENV['QUERY_STRING']) ? $_ENV['QUERY_STRING'] : ''));
+
+                       $message = '<u>SQL ERROR</u> [ ' . SQL_LAYER . ' ]<br /><br />' . @mysqli_error($this->db_connect_id) . '<br /><br /><u>CALLING PAGE</u><br /><br />'  . htmlspecialchars($this_page) . (($sql != '') ? '<br /><br /><u>SQL</u><br /><br />' . $sql : '') . '<br />';
+
+                       if ($this->transaction)
+                       {
+                               $this->sql_transaction('rollback');
+                       }
+                       
+                       trigger_error($message, E_USER_ERROR);
+               }
+
+               $result = array(
+                       'message'       => @mysqli_error($this->db_connect_id),
+                       'code'          => @mysqli_errno($this->db_connect_id)
+               );
+
+               return $result;
+       }
+
+       function sql_report($mode, $query = '')
+       {
+               if (empty($_GET['explain']))
+               {
+                       return;
+               }
+
+               global $db, $cache, $starttime, $phpbb_root_path;
+               static $curtime, $query_hold, $html_hold;
+               static $sql_report = '';
+               static $cache_num_queries = 0;
+
+               if (!$query && !empty($query_hold))
+               {
+                       $query = $query_hold;
+               }
+
+               switch ($mode)
+               {
+                       case 'display':
+                               if (!empty($cache))
+                               {
+                                       $cache->unload();
+                               }
+                               $db->sql_close();
+
+                               $mtime = explode(' ', microtime());
+                               $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+                               echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8869-1"><meta http-equiv="Content-Style-Type" content="text/css"><link rel="stylesheet" href="' . $phpbb_root_path . 'adm/subSilver.css" type="text/css"><style type="text/css">' . "\n";
+                               echo 'th { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic3.gif\') }' . "\n";
+                               echo 'td.cat    { background-image: url(\'' . $phpbb_root_path . 'adm/images/cellpic1.gif\') }' . "\n";
+                               echo '</style><title>' . $msg_title . '</title></head><body>';
+                               echo '<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td><a href="' . htmlspecialchars(preg_replace('/&explain=([^&]*)/', '', $_SERVER['REQUEST_URI'])) . '"><img src="' . $phpbb_root_path . 'adm/images/header_left.jpg" width="200" height="60" alt="phpBB Logo" title="phpBB Logo" border="0"/></a></td><td width="100%" background="' . $phpbb_root_path . 'adm/images/header_bg.jpg" height="60" align="right" nowrap="nowrap"><span class="maintitle">SQL Report</span> &nbsp; &nbsp; &nbsp;</td></tr></table><br clear="all"/><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td height="40" align="center" valign="middle"><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries} queries" . (($cache_num_queries) ? " + $cache_num_queries " . (($cache_num_queries == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></td></tr><tr><td align="center" nowrap="nowrap">Time spent on MySQL queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></td></tr></table><table width="95%" cellspacing="1" cellpadding="4" border="0" align="center"><tr><td>';
+                               echo $sql_report;
+                               echo '</td></tr></table><br /></body></html>';
+                               exit;
+                               break;
+
+                       case 'start':
+                               $query_hold = $query;
+                               $html_hold = '';
+
+                               $explain_query = $query;
+                               if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+                               {
+                                       $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+                               }
+                               elseif (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+                               {
+                                       $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+                               }
+
+                               if (preg_match('/^SELECT/', $explain_query))
+                               {
+                                       $html_table = FALSE;
+
+                                       if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query"))
+                                       {
+                                               while ($row = @mysqli_fetch_assoc($result))
+                                               {
+                                                       if (!$html_table && sizeof($row))
+                                                       {
+                                                               $html_table = TRUE;
+                                                               $html_hold .= '<table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0" align="center"><tr>';
+                                                               
+                                                               foreach (array_keys($row) as $val)
+                                                               {
+                                                                       $html_hold .= '<th nowrap="nowrap">' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
+                                                               }
+                                                               $html_hold .= '</tr>';
+                                                       }
+                                                       $html_hold .= '<tr>';
+
+                                                       $class = 'row1';
+                                                       foreach (array_values($row) as $val)
+                                                       {
+                                                               $class = ($class == 'row1') ? 'row2' : 'row1';
+                                                               $html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
+                                                       }
+                                                       $html_hold .= '</tr>';
+                                               }
+                                       }
+
+                                       if ($html_table)
+                                       {
+                                               $html_hold .= '</table>';
+                                       }
+                               }
+
+                               $curtime = explode(' ', microtime());
+                               $curtime = $curtime[0] + $curtime[1];
+                               break;
+
+                       case 'fromcache':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $result = @mysqli_query($this->db_connect_id, $query);
+                               while ($void = @mysqli_fetch_assoc($result))
+                               {
+                                       // Take the time spent on parsing rows into account
+                               }
+                               $splittime = explode(' ', microtime());
+                               $splittime = $splittime[0] + $splittime[1];
+
+                               $time_cache = $endtime - $curtime;
+                               $time_db = $splittime - $endtime;
+                               $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query results obtained from the cache</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table><p align="center">';
+
+                               $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p>';
+
+                               // Pad the start time to not interfere with page timing
+                               $starttime += $time_db;
+
+                               @mysqli_free_result($result);
+                               $cache_num_queries++;
+                               break;
+
+                       case 'stop':
+                               $endtime = explode(' ', microtime());
+                               $endtime = $endtime[0] + $endtime[1];
+
+                               $sql_report .= '<hr width="100%"/><br /><table class="bg" width="100%" cellspacing="1" cellpadding="4" border="0"><tr><th>Query #' . $this->num_queries . '</th></tr><tr><td class="row1"><textarea style="font-family:\'Courier New\',monospace;width:100%" rows="5">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></table> ' . $html_hold . '<p align="center">';
+
+                               if ($this->query_result)
+                               {
+                                       if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+                                       {
+                                               $sql_report .= "Affected rows: <b>" . $this->sql_affectedrows($this->query_result) . '</b> | ';
+                                       }
+                                       $sql_report .= 'Before: ' . sprintf('%.5f', $curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $curtime) . 's</b>';
+                               }
+                               else
+                               {
+                                       $error = $this->sql_error();
+                                       $sql_report .= '<b style="color: red">FAILED</b> - MySQL Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+                               }
+
+                               $sql_report .= '</p>';
+
+                               $this->sql_time += $endtime - $curtime;
+                               break;
+               }
+       }
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/oracle.php b/includes/db/oracle.php
new file mode 100644 (file)
index 0000000..7ef10e5
--- /dev/null
@@ -0,0 +1,468 @@
+<?php
+/** 
+*
+* @package dbal_oracle
+* @version $Id: oracle.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if(!defined("SQL_LAYER"))
+{
+
+define("SQL_LAYER","oracle");
+
+/**
+* @package dbal_oracle
+* Oracle Database Abstraction Layer
+*/
+class sql_db
+{
+
+       var $db_connect_id;
+       var $query_result;
+       var $in_transaction = 0;
+       var $row = array();
+       var $rowset = array();
+       var $num_queries = 0;
+       var $last_query_text = "";
+
+       //
+       // Constructor
+       //
+       function sql_db($sqlserver, $sqluser, $sqlpassword, $database="", $persistency = true)
+       {
+               $this->persistency = $persistency;
+               $this->user = $sqluser;
+               $this->password = $sqlpassword;
+               $this->server = $sqlserver;
+               $this->dbname = $database;
+
+               if($this->persistency)
+               {
+                       $this->db_connect_id = @OCIPLogon($this->user, $this->password, $this->server);
+               }
+               else
+               {
+                       $this->db_connect_id = @OCINLogon($this->user, $this->password, $this->server);
+               }
+               if($this->db_connect_id)
+               {
+                       return $this->db_connect_id;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+       //
+       // Other base methods
+       //
+       function sql_close()
+       {
+               if($this->db_connect_id)
+               {
+                       // Commit outstanding transactions
+                       if($this->in_transaction)
+                       {
+                               OCICommit($this->db_connect_id);
+                       }
+
+                       if($this->query_result)
+                       {
+                               @OCIFreeStatement($this->query_result);
+                       }
+                       $result = @OCILogoff($this->db_connect_id);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+       //
+       // Base query method
+       //
+       function sql_query($query = "", $transaction = FALSE)
+       {
+               // Remove any pre-existing queries
+               unset($this->query_result);
+
+               // Put us in transaction mode because with Oracle as soon as you make a query you're in a transaction
+               $this->in_transaction = TRUE;
+
+               if($query != "")
+               {
+                       $this->last_query = $query;
+                       $this->num_queries++;
+
+                       if(eregi("LIMIT", $query))
+                       {
+                               preg_match("/^(.*)LIMIT ([0-9]+)[, ]*([0-9]+)*/s", $query, $limits);
+
+                               $query = $limits[1];
+                               if($limits[3])
+                               {
+                                       $row_offset = $limits[2];
+                                       $num_rows = $limits[3];
+                               }
+                               else
+                               {
+                                       $row_offset = 0;
+                                       $num_rows = $limits[2];
+                               }
+                       }
+
+                       if(eregi("^(INSERT|UPDATE) ", $query))
+                       {
+                               $query = preg_replace("/\\\'/s", "''", $query);
+                       }
+
+                       $this->query_result = @OCIParse($this->db_connect_id, $query);
+                       $success = @OCIExecute($this->query_result, OCI_DEFAULT);
+               }
+               if($success)
+               {
+                       if($transaction == END_TRANSACTION)
+                       {
+                               OCICommit($this->db_connect_id);
+                               $this->in_transaction = FALSE;
+                       }
+
+                       unset($this->row[$this->query_result]);
+                       unset($this->rowset[$this->query_result]);
+                       $this->last_query_text[$this->query_result] = $query;
+
+                       return $this->query_result;
+               }
+               else
+               {
+                       if($this->in_transaction)
+                       {
+                               OCIRollback($this->db_connect_id);
+                       }
+                       return false;
+               }
+       }
+
+       //
+       // Other query methods
+       //
+       function sql_numrows($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = @OCIFetchStatement($query_id, $this->rowset);
+                       // OCIFetchStatment kills our query result so we have to execute the statment again
+                       // if we ever want to use the query_id again.
+                       @OCIExecute($query_id, OCI_DEFAULT);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_affectedrows($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = @OCIRowCount($query_id);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_numfields($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = @OCINumCols($query_id);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fieldname($offset, $query_id = 0)
+       {
+               // OCIColumnName uses a 1 based array so we have to up the offset by 1 in here to maintain
+               // full abstraction compatibitly
+               $offset += 1;
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = strtolower(@OCIColumnName($query_id, $offset));
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fieldtype($offset, $query_id = 0)
+       {
+               // This situation is the same as fieldname
+               $offset += 1;
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result = @OCIColumnType($query_id, $offset);
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fetchrow($query_id = 0, $debug = FALSE)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $result_row = "";
+                       $result = @OCIFetchInto($query_id, $result_row, OCI_ASSOC+OCI_RETURN_NULLS);
+                       if($debug)
+                       {
+                               echo "Query was: ".$this->last_query . "<br>";
+                               echo "Result: $result<br>";
+                               echo "Query ID: $query_id<br>";
+                               echo "<pre>";
+                               var_dump($result_row);
+                               echo "</pre>";
+                       }
+                       if($result_row == "")
+                       {
+                               return false;
+                       }
+
+                       for($i = 0; $i < count($result_row); $i++)
+                       {
+                               list($key, $val) = each($result_row);
+                               $return_arr[strtolower($key)] = $val;
+                       }
+                       $this->row[$query_id] = $return_arr;
+
+                       return $this->row[$query_id];
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       // This function probably isn't as efficant is it could be but any other way I do it
+       // I end up losing 1 row...
+       function sql_fetchrowset($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       $rows = @OCIFetchStatement($query_id, $results);
+                       @OCIExecute($query_id, OCI_DEFAULT);
+                       for($i = 0; $i <= $rows; $i++)
+                       {
+                               @OCIFetchInto($query_id, $tmp_result, OCI_ASSOC+OCI_RETURN_NULLS);
+
+                               for($j = 0; $j < count($tmp_result); $j++)
+                               {
+                                       list($key, $val) = each($tmp_result);
+                                       $return_arr[strtolower($key)] = $val;
+                               }
+                               $result[] = $return_arr;
+                       }
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_fetchfield($field, $rownum = -1, $query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                       if($rownum > -1)
+                       {
+                               // Reset the internal rownum pointer.
+                               @OCIExecute($query_id, OCI_DEFAULT);
+                               for($i = 0; $i < $rownum; $i++)
+                                 {
+                                               // Move the interal pointer to the row we want
+                                               @OCIFetch($query_id);
+                                 }
+                               // Get the field data.
+                               $result = @OCIResult($query_id, strtoupper($field));
+                       }
+                       else
+                       {
+                               // The internal pointer should be where we want it
+                               // so we just grab the field out of the current row.
+                               $result = @OCIResult($query_id, strtoupper($field));
+                       }
+                       return $result;
+               }
+               else
+               {
+                       return false;
+               }
+       }
+       function sql_rowseek($rownum, $query_id = 0)
+       {
+               if(!$query_id)
+               {
+                               $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                               @OCIExecute($query_id, OCI_DEFAULT);
+                       for($i = 0; $i < $rownum; $i++)
+                               {
+                                       @OCIFetch($query_id);
+                               }
+                       $result = @OCIFetch($query_id);
+                       return $result;
+               }
+               else
+               {
+                               return false;
+               }
+       }
+       function sql_nextid($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id && $this->last_query_text[$query_id] != "")
+               {
+                       if( eregi("^(INSERT{1}|^INSERT INTO{1})[[:space:]][\"]?([a-zA-Z0-9\_\-]+)[\"]?", $this->last_query_text[$query_id], $tablename))
+                       {
+                               $query = "SELECT ".$tablename[2]."_id_seq.currval FROM DUAL";
+                               $stmt = @OCIParse($this->db_connect_id, $query);
+                               @OCIExecute($stmt,OCI_DEFAULT );
+                               $temp_result = @OCIFetchInto($stmt, $temp_result, OCI_ASSOC+OCI_RETURN_NULLS);
+                               if($temp_result)
+                               {
+                                       return $temp_result['CURRVAL'];
+                               }
+                               else
+                               {
+                                       return false;
+                               }
+                       }
+                       else
+                       {
+                               return false;
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+       function sql_nextid($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               if($query_id && $this->last_query_text[$query_id] != "")
+               {
+                       if( eregi("^(INSERT{1}|^INSERT INTO{1})[[:space:]][\"]?([a-zA-Z0-9\_\-]+)[\"]?", $this->last_query_text[$query_id], $tablename))
+                       {
+                               $query = "SELECT ".$tablename[2]."_id_seq.CURRVAL FROM DUAL";
+                               $temp_q_id =  @OCIParse($this->db_connect_id, $query);
+                               @OCIExecute($temp_q_id, OCI_DEFAULT);
+                               @OCIFetchInto($temp_q_id, $temp_result, OCI_ASSOC+OCI_RETURN_NULLS);
+
+                               if($temp_result)
+                               {
+                                       return $temp_result['CURRVAL'];
+                               }
+                               else
+                               {
+                                       return false;
+                               }
+                       }
+                       else
+                       {
+                               return false;
+                       }
+               }
+               else
+               {
+                       return false;
+               }
+       }
+
+
+
+       function sql_freeresult($query_id = 0)
+       {
+               if(!$query_id)
+               {
+                               $query_id = $this->query_result;
+               }
+               if($query_id)
+               {
+                               $result = @OCIFreeStatement($query_id);
+                               return $result;
+               }
+               else
+               {
+                               return false;
+               }
+       }
+       function sql_error($query_id  = 0)
+       {
+               if(!$query_id)
+               {
+                       $query_id = $this->query_result;
+               }
+               $result  = @OCIError($query_id);
+               return $result;
+       }
+
+} // class sql_db
+
+} // if ... define
+
+?>
\ No newline at end of file
diff --git a/includes/db/postgres.php b/includes/db/postgres.php
new file mode 100644 (file)
index 0000000..b5bad20
--- /dev/null
@@ -0,0 +1,597 @@
+<?php
+/** 
+*
+* @package dbal_postgres
+* @version $Id: postgres.php,v 1.2 2005/06/10 08:52:03 devalley Exp $
+* @copyright (c) 2005 phpBB Group 
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('SQL_LAYER'))
+{
+
+define('SQL_LAYER', 'postgresql');
+
+/**
+* @package dbal_postgres
+* PostgreSQL Database Abstraction Layer
+* Minimum Requirement is Version 7.3+
+*/
+class sql_db
+{
+       var $db_connect_id;
+       var $query_result;
+       var $return_on_error = false;
+       var $transaction = false;
+       var $sql_time = 0;
+       var $num_queries = 0;
+       var $open_queries = array();
+
+       function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
+       {
+               $this->connect_string = '';
+
+               if ($sqluser)
+               {
+                       $this->connect_string .= "user=$sqluser ";
+               }
+
+               if ($sqlpassword)
+               {
+                       $this->connect_string .= "password=$sqlpassword ";
+               }
+
+               if ($sqlserver)
+               {
+                       if (ereg(":", $sqlserver))
+                       {
+                               list($sqlserver, $sqlport) = split(":", $sqlserver);
+                               $this->connect_string .= "host=$sqlserver port=$sqlport ";
+                       }
+                       else
+                       {
+                               if ($sqlserver != "localhost")
+                               {
+                                       $this->connect_string .= "host=$sqlserver ";
+                               }
+                       
+                               if ($port)
+                               {
+