summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2011-03-17 00:01:25 +0000
committerdakkar <dakkar@thenautilus.net>2011-03-17 00:01:25 +0000
commitabd38899f6f1fcf7c8f16eb83356c8ce1070cf71 (patch)
tree1374fd9507ef1d0a5bb77b82c6d3846b9170b79e
downloadweave-minimal-abd38899f6f1fcf7c8f16eb83356c8ce1070cf71.tar.gz
weave-minimal-abd38899f6f1fcf7c8f16eb83356c8ce1070cf71.tar.bz2
weave-minimal-abd38899f6f1fcf7c8f16eb83356c8ce1070cf71.zip
import from http://tobyelliott.wordpress.com/2009/09/11/weave-minimal-server/
-rw-r--r--.gitignore2
-rw-r--r--README19
-rw-r--r--create_user78
-rw-r--r--index.php503
-rw-r--r--weave_basic_object.php241
-rw-r--r--weave_storage.php787
6 files changed, 1630 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..727dad6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/weave_db
+
diff --git a/README b/README
new file mode 100644
index 0000000..4c3a9f4
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+SERVER SETUP
+
+Add the following line to your apache config:
+
+Alias /weave /<path to this folder>/index.php
+
+Restart your apache server. Point your browser at https://localhost/weave/1.0/blah/info/collection.
+Enter "blah" for the username and garbage for the pwd. Auth will fail, but it will
+create the db (you can cancel the subsequent request for auth). You should now see a file
+called weave_db in the directory.
+
+You can create and delete users by running the create_user script.
+
+CLIENT SETUP
+
+in about.config, set extensions.weave.serverURL to https://<your servername>/weave/
+
+You can run it under http, but this is insecure and not recommended.
+
diff --git a/create_user b/create_user
new file mode 100644
index 0000000..f912c6c
--- /dev/null
+++ b/create_user
@@ -0,0 +1,78 @@
+<?php
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Weave Minimal Server
+#
+# The Initial Developer of the Original Code is
+# Mozilla Labs.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Toby Elliott (telliott@mozilla.com)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+ require_once 'weave_storage.php';
+ $db = new WeaveStorage(null);
+
+ fwrite(STDOUT, "(c)reate, (d)elete or change (p)assword: ");
+ switch (substr(fgets(STDIN), 0, 1))
+ {
+ case 'c':
+ fwrite(STDOUT, "Please enter username: ");
+ $name = trim(fgets(STDIN));
+ fwrite(STDOUT, "Please enter password: ");
+ $pwd = trim(fgets(STDIN));
+ if ($db->create_user($name, $pwd))
+ echo "$name created\n";
+ else
+ echo "An error occured\n";
+ break;
+ case 'd':
+ fwrite(STDOUT, "Please enter username: ");
+ $name = trim(fgets(STDIN));
+ if ($db->delete_user($name))
+ echo "$name deleted\n";
+ else
+ echo "An error occured\n";
+ break;
+ case 'p':
+ fwrite(STDOUT, "Please enter username: ");
+ $name = trim(fgets(STDIN));
+ fwrite(STDOUT, "Please enter new password: ");
+ $password = trim(fgets(STDIN));
+ if ($db->change_password($name,$password))
+ echo "password changed for $name\n";
+ else
+ echo "An error occured\n";
+ break;
+ default:
+ echo "unknown command";
+ }
+
+?> \ No newline at end of file
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..d8da439
--- /dev/null
+++ b/index.php
@@ -0,0 +1,503 @@
+<?php
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Weave Minimal Server
+#
+# The Initial Developer of the Original Code is
+# Mozilla Labs.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Toby Elliott (telliott@mozilla.com)
+# Luca Tettamanti
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+ require_once 'weave_storage.php';
+ require_once 'weave_basic_object.php';
+
+ function report_problem($message, $code = 503)
+ {
+ $headers = array('400' => '400 Bad Request',
+ '401' => '401 Unauthorized',
+ '404' => '404 Not Found',
+ '412' => '412 Precondition Failed',
+ '503' => '503 Service Unavailable');
+ header('HTTP/1.1 ' . $headers{$code},true,$code);
+
+ if ($code == 401)
+ {
+ header('WWW-Authenticate: Basic realm="Weave"');
+ }
+
+ exit(json_encode($message));
+ }
+
+
+ header("Content-type: application/json");
+
+
+ #get the http auth user data
+
+ $auth_user = array_key_exists('PHP_AUTH_USER', $_SERVER) ? $_SERVER['PHP_AUTH_USER'] : null;
+ $auth_pw = array_key_exists('PHP_AUTH_PW', $_SERVER) ? $_SERVER['PHP_AUTH_PW'] : null;
+
+ if (is_null($auth_user) || is_null($auth_pw))
+ {
+ /* CGI/FCGI auth workarounds */
+ $auth_str = null;
+ if (array_key_exists('Authorization', $_SERVER))
+ /* Standard fastcgi configuration */
+ $auth_str = $_SERVER['Authorization'];
+ else if (array_key_exists('AUTHORIZATION', $_SERVER))
+ /* Alternate fastcgi configuration */
+ $auth_str = $_SERVER['AUTHORIZATION'];
+ else if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER))
+ /* IIS/ISAPI and newer (yet to be released) fastcgi */
+ $auth_str = $_SERVER['HTTP_AUTHORIZATION'];
+ else if (array_key_exists('REDIRECT_HTTP_AUTHORIZATION', $_SERVER))
+ /* mod_rewrite - per-directory internal redirect */
+ $auth_str = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
+ if (!is_null($auth_str))
+ {
+ /* Basic base64 auth string */
+ if (preg_match('/Basic\s+(.*)$/', $auth_str))
+ {
+ $auth_str = substr($auth_str, 6);
+ $auth_str = base64_decode($auth_str, true);
+ if ($auth_str != FALSE) {
+ $tmp = explode(':', $auth_str);
+ if (count($tmp) == 2)
+ {
+ $auth_user = $tmp[0];
+ $auth_pw = $tmp[1];
+ }
+ }
+ }
+ }
+ }
+
+ $server_time = round(microtime(1), 2);
+ header("X-Weave-Timestamp: " . $server_time);
+
+ #Basic path extraction and validation. No point in going on if these are missing
+ $path = '/';
+ if (!empty($_SERVER['PATH_INFO'])) {
+ $path = $_SERVER['PATH_INFO'];
+ }
+ else if (!empty($_SERVER['ORIG_PATH_INFO'])) {
+ $path = $_SERVER['ORIG_PATH_INFO'];
+ }
+ $path = substr($path, 1); #chop the lead slash
+ list($version, $username, $function, $collection, $id) = explode('/', $path.'//');
+
+ if ($version != '0.5' && $version != '1.0')
+ report_problem('Function not found', 404);
+
+ # Lowercase username before checking path
+ $username = strtolower($username);
+ $auth_user = strtolower($auth_user);
+
+ if (!$username)
+ report_problem(3, 400);
+
+ if ($auth_user != $username)
+ report_problem(5, 401);
+
+ #quick check to make sure that any non-storage function calls are just using GET
+ if ($function != 'storage' && $_SERVER['REQUEST_METHOD'] != 'GET')
+ report_problem(1, 400);
+
+ #only a get has meaning without a collection (GET returns a collection list)
+ if (!$collection && $_SERVER['REQUEST_METHOD'] != 'GET')
+ report_problem(1, 400);
+
+ #storage requires a collection to have been passed in. Info requires a directive
+ if (!$collection)
+ report_problem(1, 400);
+
+ #hook up with the db
+ try
+ {
+ $db = new WeaveStorage($username);
+ }
+ catch(Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+
+ #Auth the user
+ try
+ {
+ if (!$db->authenticate_user($auth_pw))
+ report_problem('Authentication failed', '401');
+ }
+ catch(Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+
+ #user passes, onto actually getting the data
+ if ($_SERVER['REQUEST_METHOD'] == 'GET')
+ {
+ if ($function == 'info')
+ {
+ switch ($collection)
+ {
+ case 'quota':
+ exit(json_encode(array($db->get_storage_total())));
+ case 'collections':
+ exit(json_encode($db->get_collection_list_with_timestamps()));
+ case 'collection_counts':
+ exit(json_encode($db->get_collection_list_with_counts()));
+ default:
+ report_problem(1, 400);
+ }
+ }
+ elseif ($function == 'storage')
+ {
+ if ($id) #retrieve a single record
+ {
+ try
+ {
+ $wbo = $db->retrieve_objects($collection, $id, 1); #get the full contents of one record
+ }
+ catch(Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+
+ if (count($wbo) > 0)
+ echo $wbo[0]->json();
+ else
+ report_problem("record not found", 404);
+ }
+ else #retrieve a batch of records. Sadly, due to potential record sizes, have the storage object stream the output...
+ {
+ $full = array_key_exists('full', $_GET) ? $_GET['full'] : null;
+ $outputter = new WBOJsonOutput($full);
+ if (array_key_exists('HTTP_ACCEPT', $_SERVER)
+ && !preg_match('/\*\/\*/', $_SERVER['HTTP_ACCEPT'])
+ && !preg_match('/application\/json/', $_SERVER['HTTP_ACCEPT']))
+ {
+ if (preg_match('/application\/whoisi/', $_SERVER['HTTP_ACCEPT']))
+ {
+ header("Content-type: application/whoisi");
+ $outputter->set_format('whoisi');
+ }
+ elseif (preg_match('/application\/newlines/', $_SERVER['HTTP_ACCEPT']))
+ {
+ header("Content-type: application/newlines");
+ $outputter->set_format('newlines');
+ }
+
+ }
+
+ try
+ {
+ $ids = $db->retrieve_objects($collection, null, $full, $outputter,
+ array_key_exists('parentid', $_GET) ? $_GET['parentid'] : null,
+ array_key_exists('predecessorid', $_GET) ? $_GET['predecessorid'] : null,
+ array_key_exists('newer', $_GET) ? $_GET['newer'] : null,
+ array_key_exists('older', $_GET) ? $_GET['older'] : null,
+ array_key_exists('sort', $_GET) ? $_GET['sort'] : null,
+ array_key_exists('limit', $_GET) ? $_GET['limit'] : null,
+ array_key_exists('offset', $_GET) ? $_GET['offset'] : null,
+ array_key_exists('ids', $_GET) ? explode(',', $_GET['ids']) : null,
+ array_key_exists('index_above', $_GET) ? $_GET['index_above'] : null,
+ array_key_exists('index_below', $_GET) ? $_GET['index_below'] : null,
+ array_key_exists('depth', $_GET) ? $_GET['depth'] : null
+ );
+ }
+ catch(Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+ }
+ }
+ }
+ else if ($_SERVER['REQUEST_METHOD'] == 'PUT') #add a single record to the server
+ {
+ $putdata = fopen("php://input", "r");
+ $json = '';
+ while ($data = fread($putdata,2048)) {$json .= $data;};
+
+ $wbo = new wbo();
+ if (!$wbo->extract_json($json))
+ report_problem(6, 400);
+
+ if (array_key_exists('HTTP_X_IF_UNMODIFIED_SINCE', $_SERVER)
+ && $db->get_max_timestamp($collection) > round((float)$_SERVER['HTTP_X_IF_UNMODIFIED_SINCE'], 2))
+ report_problem(4, 412);
+
+ #use the url if the json object doesn't have an id
+ if (!$wbo->id() && $id) { $wbo->id($id); }
+
+ $wbo->collection($collection);
+ $wbo->modified($server_time); #current microtime
+
+ if ($wbo->validate())
+ {
+ try
+ {
+ #if there's no payload (as opposed to blank), then update the metadata
+ if ($wbo->payload_exists())
+ $db->store_object($wbo);
+ else
+ $db->update_object($wbo);
+ }
+ catch (Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+ echo json_encode($wbo->modified());
+ }
+ else
+ {
+ report_problem(8, 400);
+ }
+ }
+ else if ($_SERVER['REQUEST_METHOD'] == 'POST')
+ {
+ #stupid php being helpful with input data...
+ $putdata = fopen("php://input", "r");
+ $jsonstring = '';
+ while ($data = fread($putdata,2048)) {$jsonstring .= $data;}
+ $json = json_decode($jsonstring, true);
+
+ if ($json === null)
+ report_problem(6, 400);
+
+ if (array_key_exists('HTTP_X_IF_UNMODIFIED_SINCE', $_SERVER)
+ && $db->get_max_timestamp($collection) > round((float)$_SERVER['HTTP_X_IF_UNMODIFIED_SINCE'], 2))
+ report_problem(4, 412);
+
+
+ $success_ids = array();
+ $failed_ids = array();
+
+
+ try
+ {
+ $db->begin_transaction();
+ }
+ catch(Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+
+ foreach ($json as $wbo_data)
+ {
+ $wbo = new wbo();
+ if (!$wbo->extract_json($wbo_data))
+ {
+ $failed_ids[$wbo->id()] = $wbo->get_error();
+ continue;
+ }
+
+ $wbo->collection($collection);
+ $wbo->modified($server_time);
+
+
+ if ($wbo->validate())
+ {
+ try
+ {
+ #if there's no payload (as opposed to blank), then update the metadata
+ if ($wbo->payload_exists())
+ {
+ $db->store_object($wbo);
+ }
+ else
+ {
+ $db->update_object($wbo);
+ }
+ }
+ catch (Exception $e)
+ {
+ $failed_ids[$wbo->id()] = $e->getMessage();
+ continue;
+ }
+ $success_ids[] = $wbo->id();
+ }
+ else
+ {
+ $failed_ids[$wbo->id()] = $wbo->get_error();
+ }
+ }
+ $db->commit_transaction();
+
+ echo json_encode(array('success' => $success_ids, 'failed' => $failed_ids));
+ }
+ else if ($_SERVER['REQUEST_METHOD'] == 'DELETE')
+ {
+ if (array_key_exists('HTTP_X_IF_UNMODIFIED_SINCE', $_SERVER)
+ && $db->get_max_timestamp($collection) > round((float)$_SERVER['HTTP_X_IF_UNMODIFIED_SINCE'], 2))
+ report_problem(4, 412);
+
+ $timestamp = round(microtime(1), 2);
+ if ($id)
+ {
+ try
+ {
+ $db->delete_object($collection, $id);
+ }
+ catch(Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+ echo json_encode($timestamp);
+ }
+ else
+ {
+ try
+ {
+ $db->delete_objects($collection, null,
+ array_key_exists('parentid', $_GET) ? $_GET['parentid'] : null,
+ array_key_exists('predecessorid', $_GET) ? $_GET['predecessorid'] : null,
+ array_key_exists('newer', $_GET) ? $_GET['newer'] : null,
+ array_key_exists('older', $_GET) ? $_GET['older'] : null,
+ array_key_exists('sort', $_GET) ? $_GET['sort'] : null,
+ array_key_exists('limit', $_GET) ? $_GET['limit'] : null,
+ array_key_exists('offset', $_GET) ? $_GET['offset'] : null,
+ array_key_exists('ids', $_GET) ? explode(',', $_GET['ids']) : null,
+ array_key_exists('index_above', $_GET) ? $_GET['index_above'] : null,
+ array_key_exists('index_below', $_GET) ? $_GET['index_below'] : null
+ );
+ }
+ catch(Exception $e)
+ {
+ report_problem($e->getMessage(), $e->getCode());
+ }
+ echo json_encode($timestamp);
+ }
+ }
+ else
+ {
+ #bad protocol. There are protocols left? HEAD, I guess.
+ report_problem(1, 400);
+ }
+
+
+#The datasets we might be dealing with here are too large for sticking it all into an array, so
+#we need to define a direct-output method for the storage class to use. If we start producing multiples
+#(unlikely), we can put them in their own class.
+
+class WBOJsonOutput
+{
+ private $_full = null;
+ private $_comma_flag = 0;
+ private $_output_format = 'json';
+
+ function __construct ($full = null)
+ {
+ $this->_full = $full;
+ }
+
+ function set_format($format)
+ {
+ $this->_output_format = $format;
+ }
+
+
+ function output($sth)
+ {
+ if (($rowcount = $sth->rowCount()) > 0)
+ {
+ header('X-Weave-Records: ' . $rowcount);
+ }
+ if ($this->_output_format == 'newlines')
+ {
+ return $this->output_newlines($sth);
+ }
+ elseif ($this->_output_format == 'whoisi')
+ {
+ return $this->output_whoisi($sth);
+ }
+ else
+ {
+ return $this->output_json($sth);
+ }
+ }
+
+ function output_json($sth)
+ {
+ echo '[';
+
+ while ($result = $sth->fetch(PDO::FETCH_ASSOC))
+ {
+ if ($this->_comma_flag) { echo ','; } else { $this->_comma_flag = 1; }
+ if ($this->_full)
+ {
+ $wbo = new wbo();
+ $wbo->populate($result);
+ echo $wbo->json();
+ }
+ else
+ echo json_encode($result{'id'});
+ }
+
+ echo ']';
+ return 1;
+ }
+
+ function output_whoisi($sth)
+ {
+ while ($result = $sth->fetch(PDO::FETCH_ASSOC))
+ {
+ if ($this->_full)
+ {
+ $wbo = new wbo();
+ $wbo->populate($result);
+ $output = $wbo->json();
+ }
+ else
+ $output = json_encode($result{'id'});
+ echo pack('N', mb_strlen($output, '8bit')) . $output;
+ }
+ return 1;
+ }
+
+ function output_newlines($sth)
+ {
+ while ($result = $sth->fetch(PDO::FETCH_ASSOC))
+ {
+ if ($this->_full)
+ {
+ $wbo = new wbo();
+ $wbo->populate($result);
+ echo preg_replace('/\n/', '\u000a', $wbo->json());
+ }
+ else
+ echo json_encode($result{'id'});
+ echo "\n";
+ }
+ return 1;
+ }
+}
+?>
diff --git a/weave_basic_object.php b/weave_basic_object.php
new file mode 100644
index 0000000..ef30c69
--- /dev/null
+++ b/weave_basic_object.php
@@ -0,0 +1,241 @@
+<?php
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Weave Basic Object Server
+#
+# The Initial Developer of the Original Code is
+# Mozilla Labs.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Toby Elliott (telliott@mozilla.com)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+class wbo
+{
+ var $wbo_hash = array();
+ var $_collection;
+ var $_error = array();
+
+ function extract_json(&$json)
+ {
+
+ $extracted = is_string($json) ? json_decode($json, true) : $json;
+
+ #need to check the json was valid here...
+ if ($extracted === null)
+ {
+ $this->_error[] = "unable to extract from json";
+ return 0;
+ }
+
+ #must have an id, or all sorts of badness happens. However, it can be added later
+ if (array_key_exists('id', $extracted))
+ {
+ $this->id($extracted['id']);
+ }
+
+ if (array_key_exists('parentid', $extracted))
+ {
+ $this->parentid($extracted['parentid']);
+ }
+
+ if (array_key_exists('predecessorid', $extracted))
+ {
+ $this->predecessorid($extracted['predecessorid']);
+ }
+
+ if (array_key_exists('depth', $extracted))
+ {
+ $this->depth($extracted['depth']);
+ }
+
+ if (array_key_exists('sortindex', $extracted))
+ {
+ $this->sortindex($extracted['sortindex']);
+ }
+
+ if (array_key_exists('payload', $extracted))
+ {
+ $this->payload($extracted['payload']);
+ }
+ return 1;
+ }
+
+ function populate(&$datahash)
+ {
+ $this->id($datahash['id']);
+ $this->collection($datahash['collection']);
+ $this->parentid($datahash['parentid']);
+ $this->modified($datahash['modified']);
+ $this->predecessorid($datahash['predecessorid']);
+ $this->sortindex($datahash['sortindex']);
+ $this->payload($datahash['payload']);
+
+ $this->depth($datahash['depth']);
+ }
+
+ function id($id = null)
+ {
+ if (!is_null($id)) { $this->wbo_hash['id'] = $id; }
+ return array_key_exists('id', $this->wbo_hash) ? $this->wbo_hash['id'] : null;
+ }
+
+ function collection($collection = null)
+ {
+ if (!is_null($collection)){ $this->_collection = $collection; }
+ return $this->_collection;
+ }
+
+ function parentid($parentid = null)
+ {
+ if (!is_null($parentid)){ $this->wbo_hash['parentid'] = $parentid; }
+ return array_key_exists('parentid', $this->wbo_hash) ? $this->wbo_hash['parentid'] : null;
+ }
+
+ function parentid_exists()
+ {
+ return array_key_exists('parentid', $this->wbo_hash);
+ }
+
+ function predecessorid($predecessorid = null)
+ {
+ if (!is_null($predecessorid)){ $this->wbo_hash['predecessorid'] = $predecessorid; }
+ return array_key_exists('predecessorid', $this->wbo_hash) ? $this->wbo_hash['predecessorid'] : null;
+ }
+
+ function predecessorid_exists()
+ {
+ return array_key_exists('predecessorid', $this->wbo_hash);
+ }
+
+ function modified($modified = null)
+ {
+ if (!is_null($modified)){ $this->wbo_hash['modified'] = round((float)$modified, 2); }
+ return array_key_exists('modified', $this->wbo_hash) ? $this->wbo_hash['modified'] : null;
+ }
+
+ function modified_exists()
+ {
+ return array_key_exists('modified', $this->wbo_hash);
+ }
+
+ function payload($payload = null)
+ {
+ if (!is_null($payload)){ $this->wbo_hash['payload'] = $payload; }
+ return array_key_exists('payload', $this->wbo_hash) ? $this->wbo_hash['payload'] : null;
+ }
+
+ function payload_exists()
+ {
+ return array_key_exists('payload', $this->wbo_hash);
+ }
+
+ function payload_size()
+ {
+ return mb_strlen($this->wbo_hash['payload'], '8bit');
+ }
+
+ function sortindex($index = null)
+ {
+ if (!is_null($index)){ $this->wbo_hash['sortindex'] = (int)$index; }
+ return array_key_exists('sortindex', $this->wbo_hash) ? $this->wbo_hash['sortindex'] : null;
+ }
+
+ function sortindex_exists()
+ {
+ return array_key_exists('sortindex', $this->wbo_hash);
+ }
+
+ function depth($depth = null)
+ {
+ if (!is_null($depth)){ $this->wbo_hash['depth'] = (int)$depth; }
+ return array_key_exists('depth', $this->wbo_hash) ? $this->wbo_hash['depth'] : null;
+ }
+
+ function depth_exists()
+ {
+ return array_key_exists('depth', $this->wbo_hash);
+ }
+
+
+ function validate()
+ {
+
+ if (!$this->id() || strlen($this->id()) > 64)
+ { $this->_error[] = "invalid id"; }
+
+ if ($this->parentid_exists() && strlen($this->parentid()) > 64)
+ { $this->_error[] = "invalid parentid"; }
+
+ if ($this->predecessorid_exists() && strlen($this->predecessorid()) > 64)
+ { $this->_error[] = "invalid predecessorid"; }
+
+ if (!is_numeric($this->modified()))
+ { $this->_error[] = "invalid modified date"; }
+
+ if (!$this->modified())
+ { $this->_error[] = "no modification date"; }
+
+ if (!$this->_collection || strlen($this->_collection) > 64)
+ { $this->_error[] = "invalid collection"; }
+
+ if ($this->depth_exists() && !is_numeric($this->depth()))
+ { $this->_error[] = "invalid depth"; }
+
+ if ($this->sortindex_exists() && !is_numeric($this->sortindex()))
+ { $this->_error[] = "invalid sortindex"; }
+
+ if ($this->payload_exists())
+ {
+ if (!is_string($this->wbo_hash['payload']))
+ { $this->_error[] = "payload needs to be json-encoded"; }
+ }
+
+ return !$this->get_error();
+ }
+
+ function get_error()
+ {
+ return $this->_error;
+ }
+
+ function clear_error()
+ {
+ $this->_error = array();
+ }
+
+ function json()
+ {
+ return json_encode($this->wbo_hash);
+ }
+}
+
+
+?> \ No newline at end of file
diff --git a/weave_storage.php b/weave_storage.php
new file mode 100644
index 0000000..c82873d
--- /dev/null
+++ b/weave_storage.php
@@ -0,0 +1,787 @@
+<?php
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Weave Basic Object Server
+#
+# The Initial Developer of the Original Code is
+# Mozilla Labs.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Toby Elliott (telliott@mozilla.com)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+require_once 'weave_basic_object.php';
+
+class WeaveStorage
+{
+ private $_username;
+ private $_dbh;
+
+ function __construct($username)
+ {
+ $this->_username = $username;
+ $path = explode('/', $_SERVER['SCRIPT_FILENAME']);
+ $db_name = 'weave_db';
+ array_pop($path);
+ array_push($path, $db_name);
+ $db_name = implode('/', $path);
+
+ $create_tables = !file_exists($db_name);
+
+ try
+ {
+ $this->_dbh = new PDO('sqlite:' . $db_name);
+ $this->_dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ }
+ catch( PDOException $exception )
+ {
+ throw new Exception("Database unavailable", 503);
+ }
+
+ if ($create_tables)
+ $this->setup_db();
+ }
+
+ function get_connection()
+ {
+ return $this->_dbh;
+ }
+
+ function begin_transaction()
+ {
+ try
+ {
+ $this->_dbh->beginTransaction();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("begin_transaction: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+ return 1;
+ }
+
+ function commit_transaction()
+ {
+ $this->_dbh->commit();
+ return 1;
+ }
+
+ function get_max_timestamp($collection)
+ {
+ if (!$collection)
+ {
+ return 0;
+ }
+
+ try
+ {
+ $select_stmt = 'select max(modified) from wbo where username = :username and collection = :collection';
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->bindParam(':collection', $collection);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("get_max_timestamp: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+ $result = $sth->fetchColumn();
+ return round((float)$result, 2);
+ }
+
+ function get_collection_list()
+ {
+ try
+ {
+ $select_stmt = 'select distinct(collection) from wbo where username = :username';
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("get_collection_list: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+
+ $collections = array();
+ while ($result = $sth->fetchColumn())
+ {
+ $collections[] = $result;
+ }
+
+ return $collections;
+ }
+
+
+ function get_collection_list_with_timestamps()
+ {
+ try
+ {
+ $select_stmt = 'select collection, max(modified) as timestamp from wbo where username = :username group by collection';
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("get_collection_list: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+ $collections = array();
+ while ($result = $sth->fetch(PDO::FETCH_NUM))
+ {
+ $collections[$result[0]] = (float)$result[1];
+ }
+
+ return $collections;
+ }
+
+ function get_collection_list_with_counts()
+ {
+ try
+ {
+ $select_stmt = 'select collection, count(*) as ct from wbo where username = :username group by collection';
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("get_collection_list_with_counts: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+
+ $collections = array();
+ while ($result = $sth->fetch(PDO::FETCH_NUM))
+ {
+ $collections[$result[0]] = (int)$result[1];
+ }
+
+ return $collections;
+ }
+
+ function store_object(&$wbo)
+ {
+
+ try
+ {
+ $insert_stmt = 'replace into wbo (username, id, collection, parentid, predecessorid, sortindex, modified, payload, payload_size, depth)
+ values (:username, :id, :collection, :parentid, :predecessorid, :sortindex, :modified, :payload, :payload_size, :depth)';
+ $sth = $this->_dbh->prepare($insert_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->bindParam(':id', $wbo->id());
+ $sth->bindParam(':collection', $wbo->collection());
+ $sth->bindParam(':parentid', $wbo->parentid());
+ $sth->bindParam(':predecessorid', $wbo->predecessorid());
+ $sth->bindParam(':depth', $wbo->depth());
+ $sth->bindParam(':sortindex', $wbo->sortindex());
+ $sth->bindParam(':modified', $wbo->modified());
+ $sth->bindParam(':payload', $wbo->payload());
+ $sth->bindParam(':payload_size', $wbo->payload_size());
+
+ $sth->execute();
+
+ }
+ catch( PDOException $exception )
+ {
+ error_log("store_object: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+ return 1;
+ }
+
+
+ function update_object(&$wbo)
+ {
+ $update = "update wbo set ";
+ $params = array();
+ $update_list = array();
+
+ #make sure we have an id and collection. No point in continuing otherwise
+ if (!$wbo->id() || !$wbo->collection())
+ {
+ error_log('Trying to update without a valid id or collection!');
+ return 0;
+ }
+
+ if ($wbo->parentid_exists())
+ {
+ $update_list[] = "parentid = ?";
+ $params[] = $wbo->parentid();
+ }
+
+ if ($wbo->parentid_exists())
+ {
+ $update_list[] = "predecessorid = ?";
+ $params[] = $wbo->predecessorid();
+ }
+
+ if ($wbo->depth_exists())
+ {
+ $update_list[] = "depth = ?";
+ $params[] = $wbo->depth();
+ }
+
+ if ($wbo->sortindex_exists())
+ {
+ $update_list[] = "sortindex = ?";
+ $params[] = $wbo->sortindex();
+ }
+
+ if ($wbo->payload_exists())
+ {
+ $update_list[] = "payload = ?";
+ $update_list[] = "payload_size = ?";
+ $params[] = $wbo->payload();
+ $params[] = $wbo->payload_size();
+ }
+
+ # Don't modify the timestamp on a depth-only change
+ if ($wbo->parentid_exists() || $wbo->payload_exists())
+ {
+ #better make sure we have a modified date. Should have been handled earlier
+ if (!$wbo->modified_exists())
+ {
+ error_log("Called update_object with no defined timestamp. Please check");
+ $wbo->modified(microtime(1));
+ }
+ $update_list[] = "modified = ?";
+ $params[] = $wbo->modified();
+
+ }
+
+
+ if (count($params) == 0)
+ {
+ return 0;
+ }
+
+ $update .= join($update_list, ",");
+
+ $update .= " where username = ? and collection = ? and id = ?";
+ $params[] = $this->_username;
+ $params[] = $wbo->collection();
+ $params[] = $wbo->id();
+
+ try
+ {
+ $sth = $this->_dbh->prepare($update);
+ $sth->execute($params);
+ }
+ catch( PDOException $exception )
+ {
+ error_log("update_object: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+ return 1;
+ }
+
+ function delete_object($collection, $id)
+ {
+ try
+ {
+ $delete_stmt = 'delete from wbo where username = :username and collection = :collection and id = :id';
+ $sth = $this->_dbh->prepare($delete_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->bindParam(':collection', $collection);
+ $sth->bindParam(':id', $id);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("delete_object: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+ return 1;
+ }
+
+ function delete_objects($collection, $id = null, $parentid = null, $predecessorid = null, $newer = null,
+ $older = null, $sort = null, $limit = null, $offset = null, $ids = null,
+ $index_above = null, $index_below = null)
+ {
+ $params = array();
+ $select_stmt = '';
+
+ if ($limit || $offset || $sort)
+ {
+ #sqlite can't do sort or limit deletes without special compiled versions
+ #so, we need to grab the set, then delete it manually.
+
+ $params = $this->retrieve_objects($collection, $id, 0, 0, $parentid, $predecessorid, $newer, $older, $sort, $limit, $offset, $ids, $index_above, $index_below);
+ if (!count($params))
+ {
+ return 1; #nothing to delete
+ }
+ $paramqs = array();
+ $select_stmt = "delete from wbo where username = ? and collection = ? and id in (" . join(", ", array_pad($paramqs, count($params), '?')) . ")";
+ array_unshift($params, $collection);
+ array_unshift($params, $username);
+ }
+ else
+ {
+
+ $select_stmt = "delete from wbo where username = ? and collection = ?";
+ $params[] = $this->_username;
+ $params[] = $collection;
+
+
+ if ($id)
+ {
+ $select_stmt .= " and id = ?";
+ $params[] = $id;
+ }
+
+ if ($ids && count($ids) > 0)
+ {
+ $qmarks = array();
+ $select_stmt .= " and id in (";
+ foreach ($ids as $temp)
+ {
+ $params[] = $temp;
+ $qmarks[] = '?';
+ }
+ $select_stmt .= implode(",", $qmarks);
+ $select_stmt .= ')';
+ }
+
+ if ($parentid)
+ {
+ $select_stmt .= " and parentid = ?";
+ $params[] = $parentid;
+ }
+
+ if ($predecessorid)
+ {
+ $select_stmt .= " and predecessorid = ?";
+ $params[] = $parentid;
+ }
+
+ if ($index_above)
+ {
+ $select_stmt .= " and sortindex > ?";
+ $params[] = $parentid;
+ }
+
+ if ($index_below)
+ {
+ $select_stmt .= " and sortindex < ?";
+ $params[] = $parentid;
+ }
+
+ if ($newer)
+ {
+ $select_stmt .= " and modified > ?";
+ $params[] = $newer;
+ }
+
+ if ($older)
+ {
+ $select_stmt .= " and modified < ?";
+ $params[] = $older;
+ }
+
+ if ($sort == 'index')
+ {
+ $select_stmt .= " order by sortindex desc";
+ }
+ else if ($sort == 'newest')
+ {
+ $select_stmt .= " order by modified desc";
+ }
+ else if ($sort == 'oldest')
+ {
+ $select_stmt .= " order by modified";
+ }
+
+ if ($limit)
+ {
+ $select_stmt .= " limit " . intval($limit);
+ if ($offset)
+ {
+ $select_stmt .= " offset " . intval($offset);
+ }
+ }
+ }
+
+ try
+ {
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->execute($params);
+ }
+ catch( PDOException $exception )
+ {
+ error_log("delete_objects: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+ return 1;
+ }
+
+ function retrieve_object($collection, $id)
+ {
+ try
+ {
+ $select_stmt = 'select * from wbo where username = :username and collection = :collection and id = :id';
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->bindParam(':collection', $collection);
+ $sth->bindParam(':id', $id);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("retrieve_object: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+ $result = $sth->fetch(PDO::FETCH_ASSOC);
+
+ $wbo = new wbo();
+ $wbo->populate($result);
+ return $wbo;
+ }
+
+ function retrieve_objects($collection, $id = null, $full = null, $direct_output = null, $parentid = null,
+ $predecessorid = null, $newer = null, $older = null, $sort = null,
+ $limit = null, $offset = null, $ids = null,
+ $index_above = null, $index_below = null, $depth = null)
+ {
+ $full_list = $full ? '*' : 'id';
+
+
+ $select_stmt = "select $full_list from wbo where username = ? and collection = ?";
+ $params[] = $this->_username;
+ $params[] = $collection;
+
+
+ if ($id)
+ {
+ $select_stmt .= " and id = ?";
+ $params[] = $id;
+ }
+
+ if ($ids && count($ids) > 0)
+ {
+ $qmarks = array();
+ $select_stmt .= " and id in (";
+ foreach ($ids as $temp)
+ {
+ $params[] = $temp;
+ $qmarks[] = '?';
+ }
+ $select_stmt .= implode(",", $qmarks);
+ $select_stmt .= ')';
+ }
+
+ if ($ids && count($ids) > 0)
+ {
+ $qmarks = array();
+ $select_stmt .= " and id in (";
+ foreach ($ids as $temp)
+ {
+ $params[] = $temp;
+ $qmarks[] = '?';
+ }
+ $select_stmt .= implode(",", $qmarks);
+ $select_stmt .= ')';
+ }
+
+ if ($parentid)
+ {
+ $select_stmt .= " and parentid = ?";
+ $params[] = $parentid;
+ }
+
+
+ if ($predecessorid)
+ {
+ $select_stmt .= " and predecessorid = ?";
+ $params[] = $predecessorid;
+ }
+
+ if ($index_above)
+ {
+ $select_stmt .= " and sortindex > ?";
+ $params[] = $parentid;
+ }
+
+ if ($index_below)
+ {
+ $select_stmt .= " and sortindex < ?";
+ $params[] = $parentid;
+ }
+
+ if ($newer)
+ {
+ $select_stmt .= " and modified > ?";
+ $params[] = $newer;
+ }
+
+ if ($older)
+ {
+ $select_stmt .= " and modified < ?";
+ $params[] = $older;
+ }
+
+
+ if ($depth)
+ {
+ $select_stmt .= " and depth = ?";
+ $params[] = $depth;
+ }
+
+ if ($sort == 'index')
+ {
+ $select_stmt .= " order by sortindex desc";
+ }
+ else if ($sort == 'newest')
+ {
+ $select_stmt .= " order by modified desc";
+ }
+ else if ($sort == 'oldest')
+ {
+ $select_stmt .= " order by modified";
+ }
+
+ if ($limit)
+ {
+ $select_stmt .= " limit " . intval($limit);
+ if ($offset)
+ {
+ $select_stmt .= " offset " . intval($offset);
+ }
+ }
+
+ try
+ {
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->execute($params);
+ }
+ catch( PDOException $exception )
+ {
+ error_log("retrieve_collection: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+ if ($direct_output)
+ return $direct_output->output($sth);
+
+ $ids = array();
+ while ($result = $sth->fetch(PDO::FETCH_ASSOC))
+ {
+ if ($full)
+ {
+ $wbo = new wbo();
+ $wbo->populate($result);
+ $ids[] = $wbo;
+ }
+ else
+ $ids[] = $result{'id'};
+ }
+ return $ids;
+ }
+
+ function get_storage_total()
+ {
+ try
+ {
+ $select_stmt = 'select round(sum(length(payload))/1024) from wbo where username = :username';
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("get_storage_total: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+ return (int)$sth->fetchColumn();
+ }
+
+ function get_user_quota()
+ {
+ return null;
+ }
+
+ function delete_user($username)
+ {
+ if (!$username)
+ {
+ throw new Exception("3", 404);
+ }
+
+ try
+ {
+ $delete_stmt = 'delete from users where username = :username';
+ $sth = $this->_dbh->prepare($delete_stmt);
+ $sth->bindParam(':username', $username);
+ $sth->execute();
+ $sth->closeCursor();
+
+ $delete_wbo_stmt = 'delete from wbo where username = :username';
+ $sth = $this->_dbh->prepare($delete_wbo_stmt);
+ $sth->bindParam(':username', $username);
+ $sth->execute();
+
+ }
+ catch( PDOException $exception )
+ {
+ error_log("delete_user: " . $exception->getMessage());
+ return 0;
+ }
+ return 1;
+ }
+
+ function create_user($username, $password)
+ {
+ try
+ {
+ $create_statement = "insert into users values (:username, :md5)";
+
+ $sth = $this->_dbh->prepare($create_statement);
+ $sth->bindParam(':username', $username);
+ $sth->bindParam(':md5', md5($password));
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("create_user:" . $exception->getMessage());
+ return 0;
+ }
+ return 1;
+ }
+
+ function change_password($username, $password)
+ {
+ try
+ {
+ $update_statement = "update users set md5 = :md5 where username = :username";
+
+ $sth = $this->_dbh->prepare($update_statement);
+ $sth->bindParam(':username', $username);
+ $sth->bindParam(':md5', md5($password));
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("change_password:" . $exception->getMessage());
+ return 0;
+ }
+ return 1;
+ }
+
+ function authenticate_user($password)
+ {
+ try
+ {
+ $select_stmt = 'select username from users where username = :username and md5 = :md5';
+ $sth = $this->_dbh->prepare($select_stmt);
+ $sth->bindParam(':username', $this->_username);
+ $sth->bindParam(':md5', md5($password));
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("authenticate_user: " . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+
+ if (!$result = $sth->fetch(PDO::FETCH_ASSOC))
+ {
+ return null;
+ }
+
+ return 1;
+ }
+
+ function setup_db()
+ {
+
+ try
+ {
+ $create_statement = <<< end
+create table wbo
+(
+ username text,
+ id text,
+ collection text,
+ parentid text,
+ predecessorid int,
+ modified real,
+ sortindex int,
+ depth int,
+ payload text,
+ payload_size int,
+ primary key (username,collection,id)
+)
+end;
+
+ $create_statement2 = <<< end
+create table users
+(
+ username text,
+ md5 text,
+ primary key (username)
+)
+end;
+
+ $index1 = 'create index parentindex on wbo (username, parentid)';
+ $index2 = 'create index predecessorindex on wbo (username, predecessorid)';
+ $index3 = 'create index modifiedindex on wbo (username, collection, modified)';
+
+
+ $sth = $this->_dbh->prepare($create_statement);
+ $sth->execute();
+ $sth = $this->_dbh->prepare($create_statement2);
+ $sth->execute();
+ $sth = $this->_dbh->prepare($index1);
+ $sth->execute();
+ $sth = $this->_dbh->prepare($index2);
+ $sth->execute();
+ $sth = $this->_dbh->prepare($index3);
+ $sth->execute();
+ }
+ catch( PDOException $exception )
+ {
+ error_log("initialize_user_db:" . $exception->getMessage());
+ throw new Exception("Database unavailable", 503);
+ }
+ }
+}
+
+
+ ?> \ No newline at end of file