summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMichael Gapczynski <GapczynskiM@gmail.com>2011-07-04 16:36:30 -0400
committerMichael Gapczynski <GapczynskiM@gmail.com>2011-07-04 16:36:30 -0400
commit05389827fd8bc70b1092dc2bae1ed0335536cf4c (patch)
tree84dea83406b3b5d5cc0cf270a4e95875f3956300 /lib
parente24e2d0e16a360753cc0430f2ba75f903c669b14 (diff)
parent53ae56097de2098a2ffd1a8dd1076825bc93305d (diff)
downloadnextcloud-server-05389827fd8bc70b1092dc2bae1ed0335536cf4c.tar.gz
nextcloud-server-05389827fd8bc70b1092dc2bae1ed0335536cf4c.zip
Merge branch 'master' into sharing
Conflicts: .gitignore lib/base.php
Diffstat (limited to 'lib')
-rw-r--r--lib/HTTP/WebDAV/Server/Filesystem.php7
-rw-r--r--lib/MDB2/Driver/Datatype/sqlite3.php412
-rw-r--r--lib/MDB2/Driver/Function/sqlite3.php161
-rw-r--r--lib/MDB2/Driver/Manager/sqlite3.php1364
-rw-r--r--lib/MDB2/Driver/Native/sqlite3.php58
-rw-r--r--lib/MDB2/Driver/Reverse/sqlite3.php607
-rw-r--r--lib/MDB2/Driver/sqlite3.php1361
-rw-r--r--lib/User/backend.php126
-rw-r--r--lib/User/database.php97
-rw-r--r--lib/User/example.php97
-rw-r--r--lib/app.php138
-rw-r--r--lib/base.php33
-rw-r--r--lib/config.php5
-rw-r--r--lib/database.php52
-rw-r--r--lib/files.php15
-rw-r--r--lib/filestorage.php2
-rw-r--r--lib/filesystem.php231
-rwxr-xr-x[-rw-r--r--]lib/helper.php43
-rw-r--r--lib/installer.php83
-rw-r--r--lib/l10n.php269
-rw-r--r--lib/log.php14
-rw-r--r--lib/ocsclient.php128
-rw-r--r--lib/preferences.php8
-rw-r--r--lib/setup.php36
-rw-r--r--lib/template.php49
-rw-r--r--lib/testcase.php93
-rw-r--r--lib/user.php121
27 files changed, 5194 insertions, 416 deletions
diff --git a/lib/HTTP/WebDAV/Server/Filesystem.php b/lib/HTTP/WebDAV/Server/Filesystem.php
index 164892c9d86..5f261643624 100644
--- a/lib/HTTP/WebDAV/Server/Filesystem.php
+++ b/lib/HTTP/WebDAV/Server/Filesystem.php
@@ -687,9 +687,14 @@ VALUES (?,?,?,?,?,'timeout',?,?)");
function UNLOCK(&$options)
{
$query = OC_DB::prepare("DELETE FROM *PREFIX*locks
+ WHERE path = '$options[path]'
+ AND token = '$options[token]'");
+ /*
+ $query = OC_DB::prepare("DELETE FROM *PREFIX*locks
WHERE path = ?
AND token = ?");
- $query->execute(array($options[path]),$options[token]);
+ */
+ $query->execute();#array($options[path]),$options[token]);
return PEAR::isError($result) ? "409 Conflict" : "204 No Content";
}
diff --git a/lib/MDB2/Driver/Datatype/sqlite3.php b/lib/MDB2/Driver/Datatype/sqlite3.php
new file mode 100644
index 00000000000..378d5540cbe
--- /dev/null
+++ b/lib/MDB2/Driver/Datatype/sqlite3.php
@@ -0,0 +1,412 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2011 Robin Appelman |
+// | All rights reserved. |
+// +----------------------------------------------------------------------+
+// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
+// | API as well as database abstraction for PHP applications. |
+// | This LICENSE is in the BSD license style. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | |
+// | Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution. |
+// | |
+// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
+// | Lukas Smith nor the names of his contributors may be used to endorse |
+// | or promote products derived from this software without specific prior|
+// | written permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
+// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
+// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
+// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
+// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
+// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
+// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
+// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
+// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
+// | POSSIBILITY OF SUCH DAMAGE. |
+// +----------------------------------------------------------------------+
+// | Author: Robin Appelman <icewind1991@gmail.com |
+// +----------------------------------------------------------------------+
+//
+// $Id: sqlite.php,v 1.67 2008/02/22 19:58:06 quipo Exp $
+//
+
+require_once('MDB2/Driver/Datatype/Common.php');
+
+/**
+ * MDB2 SQLite driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Datatype_sqlite3 extends MDB2_Driver_Datatype_Common
+{
+ // {{{ _getCollationFieldDeclaration()
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set the COLLATION
+ * of a field declaration to be used in statements like CREATE TABLE.
+ *
+ * @param string $collation name of the collation
+ *
+ * @return string DBMS specific SQL code portion needed to set the COLLATION
+ * of a field declaration.
+ */
+ function _getCollationFieldDeclaration($collation)
+ {
+ return 'COLLATE '.$collation;
+ }
+
+ // }}}
+ // {{{ getTypeDeclaration()
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to declare an text type
+ * field to be used in statements like CREATE TABLE.
+ *
+ * @param array $field associative array with the name of the properties
+ * of the field being declared as array indexes. Currently, the types
+ * of supported field properties are as follows:
+ *
+ * length
+ * Integer value that determines the maximum length of the text
+ * field. If this argument is missing the field should be
+ * declared to have the longest length allowed by the DBMS.
+ *
+ * default
+ * Text value to be used as default for this field.
+ *
+ * notnull
+ * Boolean flag that indicates whether this field is constrained
+ * to not be set to null.
+ * @return string DBMS specific SQL code portion that should be used to
+ * declare the specified field.
+ * @access public
+ */
+ function getTypeDeclaration($field)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ switch ($field['type']) {
+ case 'text':
+ $length = !empty($field['length'])
+ ? $field['length'] : false;
+ $fixed = !empty($field['fixed']) ? $field['fixed'] : false;
+ return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
+ : ($length ? 'VARCHAR('.$length.')' : 'TEXT');
+ case 'clob':
+ if (!empty($field['length'])) {
+ $length = $field['length'];
+ if ($length <= 255) {
+ return 'TINYTEXT';
+ } elseif ($length <= 65532) {
+ return 'TEXT';
+ } elseif ($length <= 16777215) {
+ return 'MEDIUMTEXT';
+ }
+ }
+ return 'LONGTEXT';
+ case 'blob':
+ if (!empty($field['length'])) {
+ $length = $field['length'];
+ if ($length <= 255) {
+ return 'TINYBLOB';
+ } elseif ($length <= 65532) {
+ return 'BLOB';
+ } elseif ($length <= 16777215) {
+ return 'MEDIUMBLOB';
+ }
+ }
+ return 'LONGBLOB';
+ case 'integer':
+ if (!empty($field['length'])) {
+ $length = $field['length'];
+ if ($length <= 2) {
+ return 'SMALLINT';
+ } elseif ($length == 3 || $length == 4) {
+ return 'INTEGER';
+ } elseif ($length > 4) {
+ return 'BIGINT';
+ }
+ }
+ return 'INTEGER';
+ case 'boolean':
+ return 'BOOLEAN';
+ case 'date':
+ return 'DATE';
+ case 'time':
+ return 'TIME';
+ case 'timestamp':
+ return 'DATETIME';
+ case 'float':
+ return 'DOUBLE'.($db->options['fixed_float'] ? '('.
+ ($db->options['fixed_float']+2).','.$db->options['fixed_float'].')' : '');
+ case 'decimal':
+ $length = !empty($field['length']) ? $field['length'] : 18;
+ $scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
+ return 'DECIMAL('.$length.','.$scale.')';
+ }
+ return '';
+ }
+
+ // }}}
+ // {{{ _getIntegerDeclaration()
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to declare an integer type
+ * field to be used in statements like CREATE TABLE.
+ *
+ * @param string $name name the field to be declared.
+ * @param string $field associative array with the name of the properties
+ * of the field being declared as array indexes.
+ * Currently, the types of supported field
+ * properties are as follows:
+ *
+ * unsigned
+ * Boolean flag that indicates whether the field
+ * should be declared as unsigned integer if
+ * possible.
+ *
+ * default
+ * Integer value to be used as default for this
+ * field.
+ *
+ * notnull
+ * Boolean flag that indicates whether this field is
+ * constrained to not be set to null.
+ * @return string DBMS specific SQL code portion that should be used to
+ * declare the specified field.
+ * @access protected
+ */
+ function _getIntegerDeclaration($name, $field)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $default = $autoinc = '';
+ if (!empty($field['autoincrement'])) {
+ $autoinc = ' PRIMARY KEY AUTOINCREMENT';
+ } elseif (array_key_exists('default', $field)) {
+ if ($field['default'] === '') {
+ $field['default'] = empty($field['notnull']) ? null : 0;
+ }
+ $default = ' DEFAULT '.$this->quote($field['default'], 'integer');
+ }
+
+ $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
+ $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
+ $name = $db->quoteIdentifier($name, true);
+ if($autoinc){
+ return $name.' '.$this->getTypeDeclaration($field).$autoinc;
+ }else{
+ return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
+ }
+ }
+
+ // }}}
+ // {{{ matchPattern()
+
+ /**
+ * build a pattern matching string
+ *
+ * @access public
+ *
+ * @param array $pattern even keys are strings, odd are patterns (% and _)
+ * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
+ * @param string $field optional field name that is being matched against
+ * (might be required when emulating ILIKE)
+ *
+ * @return string SQL pattern
+ */
+ function matchPattern($pattern, $operator = null, $field = null)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $match = '';
+ if (!is_null($operator)) {
+ $field = is_null($field) ? '' : $field.' ';
+ $operator = strtoupper($operator);
+ switch ($operator) {
+ // case insensitive
+ case 'ILIKE':
+ $match = $field.'LIKE ';
+ break;
+ // case sensitive
+ case 'LIKE':
+ $match = $field.'LIKE ';
+ break;
+ default:
+ return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'not a supported operator type:'. $operator, __FUNCTION__);
+ }
+ }
+ $match.= "'";
+ foreach ($pattern as $key => $value) {
+ if ($key % 2) {
+ $match.= $value;
+ } else {
+ $match.= $db->escapePattern($db->escape($value));
+ }
+ }
+ $match.= "'";
+ $match.= $this->patternEscapeString();
+ return $match;
+ }
+
+ // }}}
+ // {{{ _mapNativeDatatype()
+
+ /**
+ * Maps a native array description of a field to a MDB2 datatype and length
+ *
+ * @param array $field native field description
+ * @return array containing the various possible types, length, sign, fixed
+ * @access public
+ */
+ function _mapNativeDatatype($field)
+ {
+ $db_type = strtolower($field['type']);
+ $length = !empty($field['length']) ? $field['length'] : null;
+ $unsigned = !empty($field['unsigned']) ? $field['unsigned'] : null;
+ $fixed = null;
+ $type = array();
+ switch ($db_type) {
+ case 'boolean':
+ $type[] = 'boolean';
+ break;
+ case 'tinyint':
+ $type[] = 'integer';
+ $type[] = 'boolean';
+ if (preg_match('/^(is|has)/', $field['name'])) {
+ $type = array_reverse($type);
+ }
+ $unsigned = preg_match('/ unsigned/i', $field['type']);
+ $length = 1;
+ break;
+ case 'smallint':
+ $type[] = 'integer';
+ $unsigned = preg_match('/ unsigned/i', $field['type']);
+ $length = 2;
+ break;
+ case 'mediumint':
+ $type[] = 'integer';
+ $unsigned = preg_match('/ unsigned/i', $field['type']);
+ $length = 3;
+ break;
+ case 'int':
+ case 'integer':
+ case 'serial':
+ $type[] = 'integer';
+ $unsigned = preg_match('/ unsigned/i', $field['type']);
+ $length = 4;
+ break;
+ case 'bigint':
+ case 'bigserial':
+ $type[] = 'integer';
+ $unsigned = preg_match('/ unsigned/i', $field['type']);
+ $length = 8;
+ break;
+ case 'clob':
+ $type[] = 'clob';
+ $fixed = false;
+ break;
+ case 'tinytext':
+ case 'mediumtext':
+ case 'longtext':
+ case 'text':
+ case 'varchar':
+ case 'varchar2':
+ $fixed = false;
+ case 'char':
+ $type[] = 'text';
+ if ($length == '1') {
+ $type[] = 'boolean';
+ if (preg_match('/^(is|has)/', $field['name'])) {
+ $type = array_reverse($type);
+ }
+ } elseif (strstr($db_type, 'text')) {
+ $type[] = 'clob';
+ $type = array_reverse($type);
+ }
+ if ($fixed !== false) {
+ $fixed = true;
+ }
+ break;
+ case 'date':
+ $type[] = 'date';
+ $length = null;
+ break;
+ case 'datetime':
+ case 'timestamp':
+ $type[] = 'timestamp';
+ $length = null;
+ break;
+ case 'time':
+ $type[] = 'time';
+ $length = null;
+ break;
+ case 'float':
+ case 'double':
+ case 'real':
+ $type[] = 'float';
+ break;
+ case 'decimal':
+ case 'numeric':
+ $type[] = 'decimal';
+ $length = $length.','.$field['decimal'];
+ break;
+ case 'tinyblob':
+ case 'mediumblob':
+ case 'longblob':
+ case 'blob':
+ $type[] = 'blob';
+ $length = null;
+ break;
+ case 'year':
+ $type[] = 'integer';
+ $type[] = 'date';
+ $length = null;
+ break;
+ default:
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+ return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'unknown database attribute type: '.$db_type, __FUNCTION__);
+ }
+
+ if ((int)$length <= 0) {
+ $length = null;
+ }
+
+ return array($type, $length, $unsigned, $fixed);
+ }
+
+ // }}}
+}
+
+?> \ No newline at end of file
diff --git a/lib/MDB2/Driver/Function/sqlite3.php b/lib/MDB2/Driver/Function/sqlite3.php
new file mode 100644
index 00000000000..0d6b329f0ad
--- /dev/null
+++ b/lib/MDB2/Driver/Function/sqlite3.php
@@ -0,0 +1,161 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2011 Robin Appelman |
+// | All rights reserved. |
+// +----------------------------------------------------------------------+
+// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
+// | API as well as database abstraction for PHP applications. |
+// | This LICENSE is in the BSD license style. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | |
+// | Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution. |
+// | |
+// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
+// | Lukas Smith nor the names of his contributors may be used to endorse |
+// | or promote products derived from this software without specific prior|
+// | written permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
+// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
+// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
+// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
+// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
+// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
+// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
+// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
+// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
+// | POSSIBILITY OF SUCH DAMAGE. |
+// +----------------------------------------------------------------------+
+// | Author: Robin Appelman <icewind1991@gmail.com> |
+// +----------------------------------------------------------------------+
+//
+//
+
+require_once('MDB2/Driver/Function/Common.php');
+
+/**
+ * MDB2 SQLite driver for the function modules
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Function_sqlite3 extends MDB2_Driver_Function_Common
+{
+ // {{{ constructor
+
+ /**
+ * Constructor
+ */
+ function __construct($db_index)
+ {
+ parent::__construct($db_index);
+ // create all sorts of UDFs
+ }
+
+ // {{{ now()
+
+ /**
+ * Return string to call a variable with the current timestamp inside an SQL statement
+ * There are three special variables for current date and time.
+ *
+ * @return string to call a variable with the current timestamp
+ * @access public
+ */
+ function now($type = 'timestamp')
+ {
+ switch ($type) {
+ case 'time':
+ return 'time(\'now\')';
+ case 'date':
+ return 'date(\'now\')';
+ case 'timestamp':
+ default:
+ return 'datetime(\'now\')';
+ }
+ }
+
+ // }}}
+ // {{{ unixtimestamp()
+
+ /**
+ * return string to call a function to get the unix timestamp from a iso timestamp
+ *
+ * @param string $expression
+ *
+ * @return string to call a variable with the timestamp
+ * @access public
+ */
+ function unixtimestamp($expression)
+ {
+ return 'strftime("%s",'. $expression.', "utc")';
+ }
+
+ // }}}
+ // {{{ substring()
+
+ /**
+ * return string to call a function to get a substring inside an SQL statement
+ *
+ * @return string to call a function to get a substring
+ * @access public
+ */
+ function substring($value, $position = 1, $length = null)
+ {
+ if (!is_null($length)) {
+ return "substr($value,$position,$length)";
+ }
+ return "substr($value,$position,length($value))";
+ }
+
+ // }}}
+ // {{{ random()
+
+ /**
+ * return string to call a function to get random value inside an SQL statement
+ *
+ * @return return string to generate float between 0 and 1
+ * @access public
+ */
+ function random()
+ {
+ return '((RANDOM()+2147483648)/4294967296)';
+ }
+
+ // }}}
+ // {{{ replace()
+
+ /**
+ * return string to call a function to get a replacement inside an SQL statement.
+ *
+ * @return string to call a function to get a replace
+ * @access public
+ */
+ function replace($str, $from_str, $to_str)
+ {
+ $db =& $this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'method not implemented', __FUNCTION__);
+ return $error;
+ }
+
+ // }}}
+}
+?>
diff --git a/lib/MDB2/Driver/Manager/sqlite3.php b/lib/MDB2/Driver/Manager/sqlite3.php
new file mode 100644
index 00000000000..7096126a523
--- /dev/null
+++ b/lib/MDB2/Driver/Manager/sqlite3.php
@@ -0,0 +1,1364 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2011 Robin Appelman |
+// | All rights reserved. |
+// +----------------------------------------------------------------------+
+// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
+// | API as well as database abstraction for PHP applications. |
+// | This LICENSE is in the BSD license style. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | |
+// | Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution. |
+// | |
+// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
+// | Lukas Smith nor the names of his contributors may be used to endorse |
+// | or promote products derived from this software without specific prior|
+// | written permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
+// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
+// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
+// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
+// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
+// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
+// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
+// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
+// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
+// | POSSIBILITY OF SUCH DAMAGE. |
+// +----------------------------------------------------------------------+
+// | Author: Robin Appelman <icewind1991@gmail.com> |
+// +----------------------------------------------------------------------+
+//
+//
+
+require_once('MDB2/Driver/Manager/Common.php');
+
+/**
+ * MDB2 SQLite driver for the management modules
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ * @author Lorenzo Alberton <l.alberton@quipo.it>
+ */
+class MDB2_Driver_Manager_sqlite3 extends MDB2_Driver_Manager_Common
+{
+ // {{{ createDatabase()
+
+ /**
+ * create a new database
+ *
+ * @param string $name name of the database that should be created
+ * @param array $options array with charset info
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function createDatabase($name, $options = array())
+ {
+ global $SERVERROOT;
+ $datadir=OC_CONFIG::getValue( "datadirectory", "$SERVERROOT/data" );
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $database_file = $db->_getDatabaseFile($name);
+ if (file_exists($database_file)) {
+ return $db->raiseError(MDB2_ERROR_ALREADY_EXISTS, null, null,
+ 'database already exists', __FUNCTION__);
+ }
+ $php_errormsg = '';
+ $database_file="$datadir/$database_file.db";
+ $handle=new SQLite3($database_file);
+ if (!$handle) {
+ return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
+ (isset($php_errormsg) ? $php_errormsg : 'could not create the database file'), __FUNCTION__);
+ }
+ //sqlite doesn't support the latin1 we use
+// if (!empty($options['charset'])) {
+// $query = 'PRAGMA encoding = ' . $db->quote($options['charset'], 'text');
+// $handle->exec($query);
+// }
+ $handle->close();
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ dropDatabase()
+
+ /**
+ * drop an existing database
+ *
+ * @param string $name name of the database that should be dropped
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function dropDatabase($name)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $database_file = $db->_getDatabaseFile($name);
+ if (!@file_exists($database_file)) {
+ return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null,
+ 'database does not exist', __FUNCTION__);
+ }
+ $result = @unlink($database_file);
+ if (!$result) {
+ return $db->raiseError(MDB2_ERROR_CANNOT_DROP, null, null,
+ (isset($php_errormsg) ? $php_errormsg : 'could not remove the database file'), __FUNCTION__);
+ }
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ _getAdvancedFKOptions()
+
+ /**
+ * Return the FOREIGN KEY query section dealing with non-standard options
+ * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
+ *
+ * @param array $definition
+ * @return string
+ * @access protected
+ */
+ function _getAdvancedFKOptions($definition)
+ {
+ $query = '';
+ if (!empty($definition['match'])) {
+ $query .= ' MATCH '.$definition['match'];
+ }
+ if (!empty($definition['onupdate']) && (strtoupper($definition['onupdate']) != 'NO ACTION')) {
+ $query .= ' ON UPDATE '.$definition['onupdate'];
+ }
+ if (!empty($definition['ondelete']) && (strtoupper($definition['ondelete']) != 'NO ACTION')) {
+ $query .= ' ON DELETE '.$definition['ondelete'];
+ }
+ if (!empty($definition['deferrable'])) {
+ $query .= ' DEFERRABLE';
+ } else {
+ $query .= ' NOT DEFERRABLE';
+ }
+ if (!empty($definition['initiallydeferred'])) {
+ $query .= ' INITIALLY DEFERRED';
+ } else {
+ $query .= ' INITIALLY IMMEDIATE';
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ _getCreateTableQuery()
+
+ /**
+ * Create a basic SQL query for a new table creation
+ * @param string $name Name of the database that should be created
+ * @param array $fields Associative array that contains the definition of each field of the new table
+ * @param array $options An associative array of table options
+ * @return mixed string (the SQL query) on success, a MDB2 error on failure
+ * @see createTable()
+ */
+ function _getCreateTableQuery($name, $fields, $options = array())
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ if (!$name) {
+ return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
+ 'no valid table name specified', __FUNCTION__);
+ }
+ if (empty($fields)) {
+ return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
+ 'no fields specified for table "'.$name.'"', __FUNCTION__);
+ }
+ $query_fields = $this->getFieldDeclarationList($fields);
+ if (PEAR::isError($query_fields)) {
+ return $query_fields;
+ }
+ if (!empty($options['primary'])) {
+ $query_fields.= ', PRIMARY KEY ('.implode(', ', array_keys($options['primary'])).')';
+ }
+ if (!empty($options['foreign_keys'])) {
+ foreach ($options['foreign_keys'] as $fkname => $fkdef) {
+ if (empty($fkdef)) {
+ continue;
+ }
+ $query_fields.= ', CONSTRAINT '.$fkname.' FOREIGN KEY ('.implode(', ', array_keys($fkdef['fields'])).')';
+ $query_fields.= ' REFERENCES '.$fkdef['references']['table'].' ('.implode(', ', array_keys($fkdef['references']['fields'])).')';
+ $query_fields.= $this->_getAdvancedFKOptions($fkdef);
+ }
+ }
+
+ $name = $db->quoteIdentifier($name, true);
+ $result = 'CREATE ';
+ if (!empty($options['temporary'])) {
+ $result .= $this->_getTemporaryTableQuery();
+ }
+ $result .= " TABLE $name ($query_fields)";
+ return $result;
+ }
+
+ // }}}
+ // {{{ createTable()
+
+ /**
+ * create a new table
+ *
+ * @param string $name Name of the database that should be created
+ * @param array $fields Associative array that contains the definition
+ * of each field of the new table
+ * @param array $options An associative array of table options
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function createTable($name, $fields, $options = array())
+ {
+ $result = parent::createTable($name, $fields, $options);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ // create triggers to enforce FOREIGN KEY constraints
+ if (!empty($options['foreign_keys'])) {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+ foreach ($options['foreign_keys'] as $fkname => $fkdef) {
+ if (empty($fkdef)) {
+ continue;
+ }
+ //set actions to default if not set
+ $fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']);
+ $fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']);
+
+ $trigger_names = array(
+ 'insert' => $fkname.'_insert_trg',
+ 'update' => $fkname.'_update_trg',
+ 'pk_update' => $fkname.'_pk_update_trg',
+ 'pk_delete' => $fkname.'_pk_delete_trg',
+ );
+
+ //create the [insert|update] triggers on the FK table
+ $table_fields = array_keys($fkdef['fields']);
+ $referenced_fields = array_keys($fkdef['references']['fields']);
+ $query = 'CREATE TRIGGER %s BEFORE %s ON '.$name
+ .' FOR EACH ROW BEGIN'
+ .' SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')'
+ .' WHERE (SELECT ';
+ $aliased_fields = array();
+ foreach ($referenced_fields as $field) {
+ $aliased_fields[] = $fkdef['references']['table'] .'.'.$field .' AS '.$field;
+ }
+ $query .= implode(',', $aliased_fields)
+ .' FROM '.$fkdef['references']['table']
+ .' WHERE ';
+ $conditions = array();
+ for ($i=0; $i<count($table_fields); $i++) {
+ $conditions[] = $referenced_fields[$i] .' = NEW.'.$table_fields[$i];
+ }
+ $query .= implode(' AND ', $conditions).') IS NULL; END;';
+ $result = $db->exec(sprintf($query, $trigger_names['insert'], 'INSERT', 'insert'));
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ $result = $db->exec(sprintf($query, $trigger_names['update'], 'UPDATE', 'update'));
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ //create the ON [UPDATE|DELETE] triggers on the primary table
+ $restrict_action = 'SELECT RAISE(ROLLBACK, \'%s on table "'.$name.'" violates FOREIGN KEY constraint "'.$fkname.'"\')'
+ .' WHERE (SELECT ';
+ $aliased_fields = array();
+ foreach ($table_fields as $field) {
+ $aliased_fields[] = $name .'.'.$field .' AS '.$field;
+ }
+ $restrict_action .= implode(',', $aliased_fields)
+ .' FROM '.$name
+ .' WHERE ';
+ $conditions = array();
+ $new_values = array();
+ $null_values = array();
+ for ($i=0; $i<count($table_fields); $i++) {
+ $conditions[] = $table_fields[$i] .' = OLD.'.$referenced_fields[$i];
+ $new_values[] = $table_fields[$i] .' = NEW.'.$referenced_fields[$i];
+ $null_values[] = $table_fields[$i] .' = NULL';
+ }
+ $conditions2 = array();
+ for ($i=0; $i<count($referenced_fields); $i++) {
+ $conditions2[] = 'NEW.'.$referenced_fields[$i] .' <> OLD.'.$referenced_fields[$i];
+ }
+ $restrict_action .= implode(' AND ', $conditions).') IS NOT NULL'
+ .' AND (' .implode(' OR ', $conditions2) .')';
+
+ $cascade_action_update = 'UPDATE '.$name.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions);
+ $cascade_action_delete = 'DELETE FROM '.$name.' WHERE '.implode(' AND ', $conditions);
+ $setnull_action = 'UPDATE '.$name.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions);
+
+ if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) {
+ $db->loadModule('Reverse', null, true);
+ $default_values = array();
+ foreach ($table_fields as $table_field) {
+ $field_definition = $db->reverse->getTableFieldDefinition($name, $field);
+ if (PEAR::isError($field_definition)) {
+ return $field_definition;
+ }
+ $default_values[] = $table_field .' = '. $field_definition[0]['default'];
+ }
+ $setdefault_action = 'UPDATE '.$name.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions);
+ }
+
+ $query = 'CREATE TRIGGER %s'
+ .' %s ON '.$fkdef['references']['table']
+ .' FOR EACH ROW BEGIN ';
+
+ if ('CASCADE' == $fkdef['onupdate']) {
+ $sql_update = sprintf($query, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . $cascade_action_update. '; END;';
+ } elseif ('SET NULL' == $fkdef['onupdate']) {
+ $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action. '; END;';
+ } elseif ('SET DEFAULT' == $fkdef['onupdate']) {
+ $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action. '; END;';
+ } elseif ('NO ACTION' == $fkdef['onupdate']) {
+ $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update') . '; END;';
+ } elseif ('RESTRICT' == $fkdef['onupdate']) {
+ $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . '; END;';
+ }
+ if ('CASCADE' == $fkdef['ondelete']) {
+ $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . $cascade_action_delete. '; END;';
+ } elseif ('SET NULL' == $fkdef['ondelete']) {
+ $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action. '; END;';
+ } elseif ('SET DEFAULT' == $fkdef['ondelete']) {
+ $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action. '; END;';
+ } elseif ('NO ACTION' == $fkdef['ondelete']) {
+ $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete') . '; END;';
+ } elseif ('RESTRICT' == $fkdef['ondelete']) {
+ $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . '; END;';
+ }
+
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $result = $db->exec($sql_delete);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $result = $db->exec($sql_update);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ }
+ }
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ dropTable()
+
+ /**
+ * drop an existing table
+ *
+ * @param string $name name of the table that should be dropped
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function dropTable($name)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ //delete the triggers associated to existing FK constraints
+ $constraints = $this->listTableConstraints($name);
+ if (!PEAR::isError($constraints) && !empty($constraints)) {
+ $db->loadModule('Reverse', null, true);
+ foreach ($constraints as $constraint) {
+ $definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
+ if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
+ $result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ }
+ }
+ }
+
+ $name = $db->quoteIdentifier($name, true);
+ return $db->exec("DROP TABLE $name");
+ }
+
+ // }}}
+ // {{{ vacuum()
+
+ /**
+ * Optimize (vacuum) all the tables in the db (or only the specified table)
+ * and optionally run ANALYZE.
+ *
+ * @param string $table table name (all the tables if empty)
+ * @param array $options an array with driver-specific options:
+ * - timeout [int] (in seconds) [mssql-only]
+ * - analyze [boolean] [pgsql and mysql]
+ * - full [boolean] [pgsql-only]
+ * - freeze [boolean] [pgsql-only]
+ *
+ * @return mixed MDB2_OK success, a MDB2 error on failure
+ * @access public
+ */
+ function vacuum($table = null, $options = array())
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $query = 'VACUUM';
+ if (!empty($table)) {
+ $query .= ' '.$db->quoteIdentifier($table, true);
+ }
+ return $db->exec($query);
+ }
+
+ // }}}
+ // {{{ alterTable()
+
+ /**
+ * alter an existing table
+ *
+ * @param string $name name of the table that is intended to be changed.
+ * @param array $changes associative array that contains the details of each type
+ * of change that is intended to be performed. The types of
+ * changes that are currently supported are defined as follows:
+ *
+ * name
+ *
+ * New name for the table.
+ *
+ * add
+ *
+ * Associative array with the names of fields to be added as
+ * indexes of the array. The value of each entry of the array
+ * should be set to another associative array with the properties
+ * of the fields to be added. The properties of the fields should
+ * be the same as defined by the MDB2 parser.
+ *
+ *
+ * remove
+ *
+ * Associative array with the names of fields to be removed as indexes
+ * of the array. Currently the values assigned to each entry are ignored.
+ * An empty array should be used for future compatibility.
+ *
+ * rename
+ *
+ * Associative array with the names of fields to be renamed as indexes
+ * of the array. The value of each entry of the array should be set to
+ * another associative array with the entry named name with the new
+ * field name and the entry named Declaration that is expected to contain
+ * the portion of the field declaration already in DBMS specific SQL code
+ * as it is used in the CREATE TABLE statement.
+ *
+ * change
+ *
+ * Associative array with the names of the fields to be changed as indexes
+ * of the array. Keep in mind that if it is intended to change either the
+ * name of a field and any other properties, the change array entries
+ * should have the new names of the fields as array indexes.
+ *
+ * The value of each entry of the array should be set to another associative
+ * array with the properties of the fields to that are meant to be changed as
+ * array entries. These entries should be assigned to the new values of the
+ * respective properties. The properties of the fields should be the same
+ * as defined by the MDB2 parser.
+ *
+ * Example
+ * array(
+ * 'name' => 'userlist',
+ * 'add' => array(
+ * 'quota' => array(
+ * 'type' => 'integer',
+ * 'unsigned' => 1
+ * )
+ * ),
+ * 'remove' => array(
+ * 'file_limit' => array(),
+ * 'time_limit' => array()
+ * ),
+ * 'change' => array(
+ * 'name' => array(
+ * 'length' => '20',
+ * 'definition' => array(
+ * 'type' => 'text',
+ * 'length' => 20,
+ * ),
+ * )
+ * ),
+ * 'rename' => array(
+ * 'sex' => array(
+ * 'name' => 'gender',
+ * 'definition' => array(
+ * 'type' => 'text',
+ * 'length' => 1,
+ * 'default' => 'M',
+ * ),
+ * )
+ * )
+ * )
+ *
+ * @param boolean $check indicates whether the function should just check if the DBMS driver
+ * can perform the requested table alterations if the value is true or
+ * actually perform them otherwise.
+ * @access public
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ */
+ function alterTable($name, $changes, $check, $options = array())
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ foreach ($changes as $change_name => $change) {
+ switch ($change_name) {
+ case 'add':
+ case 'remove':
+ case 'change':
+ case 'name':
+ case 'rename':
+ break;
+ default:
+ return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
+ 'change type "'.$change_name.'" not yet supported', __FUNCTION__);
+ }
+ }
+
+ if ($check) {
+ return MDB2_OK;
+ }
+
+ $db->loadModule('Reverse', null, true);
+
+ // actually sqlite 2.x supports no ALTER TABLE at all .. so we emulate it
+ $fields = $db->manager->listTableFields($name);
+ if (PEAR::isError($fields)) {
+ return $fields;
+ }
+
+ $fields = array_flip($fields);
+ foreach ($fields as $field => $value) {
+ $definition = $db->reverse->getTableFieldDefinition($name, $field);
+ if (PEAR::isError($definition)) {
+ return $definition;
+ }
+ $fields[$field] = $definition[0];
+ }
+
+ $indexes = $db->manager->listTableIndexes($name);
+ if (PEAR::isError($indexes)) {
+ return $indexes;
+ }
+
+ $indexes = array_flip($indexes);
+ foreach ($indexes as $index => $value) {
+ $definition = $db->reverse->getTableIndexDefinition($name, $index);
+ if (PEAR::isError($definition)) {
+ return $definition;
+ }
+ $indexes[$index] = $definition;
+ }
+
+ $constraints = $db->manager->listTableConstraints($name);
+ if (PEAR::isError($constraints)) {
+ return $constraints;
+ }
+
+ if (!array_key_exists('foreign_keys', $options)) {
+ $options['foreign_keys'] = array();
+ }
+ $constraints = array_flip($constraints);
+ foreach ($constraints as $constraint => $value) {
+ if (!empty($definition['primary'])) {
+ if (!array_key_exists('primary', $options)) {
+ $options['primary'] = $definition['fields'];
+ //remove from the $constraint array, it's already handled by createTable()
+ unset($constraints[$constraint]);
+ }
+ } else {
+ $c_definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
+ if (PEAR::isError($c_definition)) {
+ return $c_definition;
+ }
+ if (!empty($c_definition['foreign'])) {
+ if (!array_key_exists($constraint, $options['foreign_keys'])) {
+ $options['foreign_keys'][$constraint] = $c_definition;
+ }
+ //remove from the $constraint array, it's already handled by createTable()
+ unset($constraints[$constraint]);
+ } else {
+ $constraints[$constraint] = $c_definition;
+ }
+ }
+ }
+
+ $name_new = $name;
+ $create_order = $select_fields = array_keys($fields);
+ foreach ($changes as $change_name => $change) {
+ switch ($change_name) {
+ case 'add':
+ foreach ($change as $field_name => $field) {
+ $fields[$field_name] = $field;
+ $create_order[] = $field_name;
+ }
+ break;
+ case 'remove':
+ foreach ($change as $field_name => $field) {
+ unset($fields[$field_name]);
+ $select_fields = array_diff($select_fields, array($field_name));
+ $create_order = array_diff($create_order, array($field_name));
+ }
+ break;
+ case 'change':
+ foreach ($change as $field_name => $field) {
+ $fields[$field_name] = $field['definition'];
+ }
+ break;
+ case 'name':
+ $name_new = $change;
+ break;
+ case 'rename':
+ foreach ($change as $field_name => $field) {
+ unset($fields[$field_name]);
+ $fields[$field['name']] = $field['definition'];
+ $create_order[array_search($field_name, $create_order)] = $field['name'];
+ }
+ break;
+ default:
+ return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
+ 'change type "'.$change_name.'" not yet supported', __FUNCTION__);
+ }
+ }
+
+ $data = null;
+ if (!empty($select_fields)) {
+ $query = 'SELECT '.implode(', ', $select_fields).' FROM '.$db->quoteIdentifier($name, true);
+ $data = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED);
+ }
+
+ $result = $this->dropTable($name);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ $result = $this->createTable($name_new, $fields, $options);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ foreach ($indexes as $index => $definition) {
+ $this->createIndex($name_new, $index, $definition);
+ }
+
+ foreach ($constraints as $constraint => $definition) {
+ $this->createConstraint($name_new, $constraint, $definition);
+ }
+
+ if (!empty($select_fields) && !empty($data)) {
+ $query = 'INSERT INTO '.$db->quoteIdentifier($name_new, true);
+ $query.= '('.implode(', ', array_slice(array_keys($fields), 0, count($select_fields))).')';
+ $query.=' VALUES (?'.str_repeat(', ?', (count($select_fields) - 1)).')';
+ $stmt =$db->prepare($query, null, MDB2_PREPARE_MANIP);
+ if (PEAR::isError($stmt)) {
+ return $stmt;
+ }
+ foreach ($data as $row) {
+ $result = $stmt->execute($row);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ }
+ }
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ listDatabases()
+
+ /**
+ * list all databases
+ *
+ * @return mixed array of database names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listDatabases()
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'list databases is not supported', __FUNCTION__);
+ }
+
+ // }}}
+ // {{{ listUsers()
+
+ /**
+ * list all users
+ *
+ * @return mixed array of user names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listUsers()
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'list databases is not supported', __FUNCTION__);
+ }
+
+ // }}}
+ // {{{ listViews()
+
+ /**
+ * list all views in the current database
+ *
+ * @return mixed array of view names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listViews($dummy=null)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $query = "SELECT name FROM sqlite_master WHERE type='view' AND sql NOT NULL";
+ $result = $db->queryCol($query);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ listTableViews()
+
+ /**
+ * list the views in the database that reference a given table
+ *
+ * @param string table for which all referenced views should be found
+ * @return mixed array of view names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listTableViews($table)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $query = "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL";
+ $views = $db->queryAll($query, array('text', 'text'), MDB2_FETCHMODE_ASSOC);
+ if (PEAR::isError($views)) {
+ return $views;
+ }
+ $result = array();
+ foreach ($views as $row) {
+ if (preg_match("/^create view .* \bfrom\b\s+\b{$table}\b /i", $row['sql'])) {
+ if (!empty($row['name'])) {
+ $result[$row['name']] = true;
+ }
+ }
+ }
+
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $result = array_change_key_case($result, $db->options['field_case']);
+ }
+ return array_keys($result);
+ }
+
+ // }}}
+ // {{{ listTables()
+
+ /**
+ * list all tables in the current database
+ *
+ * @return mixed array of table names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listTables($dummy=null)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name";
+ $table_names = $db->queryCol($query);
+ if (PEAR::isError($table_names)) {
+ return $table_names;
+ }
+ $result = array();
+ foreach ($table_names as $table_name) {
+ if (!$this->_fixSequenceName($table_name, true)) {
+ $result[] = $table_name;
+ }
+ }
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ listTableFields()
+
+ /**
+ * list all fields in a table in the current database
+ *
+ * @param string $table name of table that should be used in method
+ * @return mixed array of field names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listTableFields($table)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $result = $db->loadModule('Reverse', null, true);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
+ } else {
+ $query.= 'name='.$db->quote($table, 'text');
+ }
+ $sql = $db->queryOne($query);
+ if (PEAR::isError($sql)) {
+ return $sql;
+ }
+ $columns = $db->reverse->_getTableColumns($sql);
+ $fields = array();
+ foreach ($columns as $column) {
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ if ($db->options['field_case'] == CASE_LOWER) {
+ $column['name'] = strtolower($column['name']);
+ } else {
+ $column['name'] = strtoupper($column['name']);
+ }
+ } else {
+ $column = array_change_key_case($column, $db->options['field_case']);
+ }
+ $fields[] = $column['name'];
+ }
+ return $fields;
+ }
+
+ // }}}
+ // {{{ listTableTriggers()
+
+ /**
+ * list all triggers in the database that reference a given table
+ *
+ * @param string table for which all referenced triggers should be found
+ * @return mixed array of trigger names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listTableTriggers($table = null)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $query = "SELECT name FROM sqlite_master WHERE type='trigger' AND sql NOT NULL";
+ if (!is_null($table)) {
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= ' AND LOWER(tbl_name)='.$db->quote(strtolower($table), 'text');
+ } else {
+ $query.= ' AND tbl_name='.$db->quote($table, 'text');
+ }
+ }
+ $result = $db->queryCol($query);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ createIndex()
+
+ /**
+ * Get the stucture of a field into an array
+ *
+ * @param string $table name of the table on which the index is to be created
+ * @param string $name name of the index to be created
+ * @param array $definition associative array that defines properties of the index to be created.
+ * Currently, only one property named FIELDS is supported. This property
+ * is also an associative with the names of the index fields as array
+ * indexes. Each entry of this array is set to another type of associative
+ * array that specifies properties of the index that are specific to
+ * each field.
+ *
+ * Currently, only the sorting property is supported. It should be used
+ * to define the sorting direction of the index. It may be set to either
+ * ascending or descending.
+ *
+ * Not all DBMS support index sorting direction configuration. The DBMS
+ * drivers of those that do not support it ignore this property. Use the
+ * function support() to determine whether the DBMS driver can manage indexes.
+
+ * Example
+ * array(
+ * 'fields' => array(
+ * 'user_name' => array(
+ * 'sorting' => 'ascending'
+ * ),
+ * 'last_login' => array()
+ * )
+ * )
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function createIndex($table, $name, $definition)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $table = $db->quoteIdentifier($table, true);
+ $name = $db->getIndexName($name);
+ $query = "CREATE INDEX $name ON $table";
+ $fields = array();
+ foreach ($definition['fields'] as $field_name => $field) {
+ $field_string = $field_name;
+ if (!empty($field['sorting'])) {
+ switch ($field['sorting']) {
+ case 'ascending':
+ $field_string.= ' ASC';
+ break;
+ case 'descending':
+ $field_string.= ' DESC';
+ break;
+ }
+ }
+ $fields[] = $field_string;
+ }
+ $query .= ' ('.implode(', ', $fields) . ')';
+ return $db->exec($query);
+ }
+
+ // }}}
+ // {{{ dropIndex()
+
+ /**
+ * drop existing index
+ *
+ * @param string $table name of table that should be used in method
+ * @param string $name name of the index to be dropped
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function dropIndex($table, $name)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $name = $db->getIndexName($name);
+ return $db->exec("DROP INDEX $name");
+ }
+
+ // }}}
+ // {{{ listTableIndexes()
+
+ /**
+ * list all indexes in a table
+ *
+ * @param string $table name of table that should be used in method
+ * @return mixed array of index names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listTableIndexes($table)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $table = $db->quote($table, 'text');
+ $query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(tbl_name)='.strtolower($table);
+ } else {
+ $query.= "tbl_name=$table";
+ }
+ $query.= " AND sql NOT NULL ORDER BY name";
+ $indexes = $db->queryCol($query, 'text');
+ if (PEAR::isError($indexes)) {
+ return $indexes;
+ }
+
+ $result = array();
+ foreach ($indexes as $sql) {
+ if (preg_match("/^create index ([^ ]+) on /i", $sql, $tmp)) {
+ $index = $this->_fixIndexName($tmp[1]);
+ if (!empty($index)) {
+ $result[$index] = true;
+ }
+ }
+ }
+
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $result = array_change_key_case($result, $db->options['field_case']);
+ }
+ return array_keys($result);
+ }
+
+ // }}}
+ // {{{ createConstraint()
+
+ /**
+ * create a constraint on a table
+ *
+ * @param string $table name of the table on which the constraint is to be created
+ * @param string $name name of the constraint to be created
+ * @param array $definition associative array that defines properties of the constraint to be created.
+ * Currently, only one property named FIELDS is supported. This property
+ * is also an associative with the names of the constraint fields as array
+ * constraints. Each entry of this array is set to another type of associative
+ * array that specifies properties of the constraint that are specific to
+ * each field.
+ *
+ * Example
+ * array(
+ * 'fields' => array(
+ * 'user_name' => array(),
+ * 'last_login' => array()
+ * )
+ * )
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function createConstraint($table, $name, $definition)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ if (!empty($definition['primary'])) {
+ return $db->manager->alterTable($table, array(), false, array('primary' => $definition['fields']));
+ }
+
+ if (!empty($definition['foreign'])) {
+ return $db->manager->alterTable($table, array(), false, array('foreign_keys' => array($name => $definition)));
+ }
+
+ $table = $db->quoteIdentifier($table, true);
+ $name = $db->getIndexName($name);
+ $query = "CREATE UNIQUE INDEX $name ON $table";
+ $fields = array();
+ foreach ($definition['fields'] as $field_name => $field) {
+ $field_string = $field_name;
+ if (!empty($field['sorting'])) {
+ switch ($field['sorting']) {
+ case 'ascending':
+ $field_string.= ' ASC';
+ break;
+ case 'descending':
+ $field_string.= ' DESC';
+ break;
+ }
+ }
+ $fields[] = $field_string;
+ }
+ $query .= ' ('.implode(', ', $fields) . ')';
+ return $db->exec($query);
+ }
+
+ // }}}
+ // {{{ dropConstraint()
+
+ /**
+ * drop existing constraint
+ *
+ * @param string $table name of table that should be used in method
+ * @param string $name name of the constraint to be dropped
+ * @param string $primary hint if the constraint is primary
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function dropConstraint($table, $name, $primary = false)
+ {
+ if ($primary || $name == 'PRIMARY') {
+ return $this->alterTable($table, array(), false, array('primary' => null));
+ }
+
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ //is it a FK constraint? If so, also delete the associated triggers
+ $db->loadModule('Reverse', null, true);
+ $definition = $db->reverse->getTableConstraintDefinition($table, $name);
+ if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
+ //first drop the FK enforcing triggers
+ $result = $this->_dropFKTriggers($table, $name, $definition['references']['table']);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ //then drop the constraint itself
+ return $this->alterTable($table, array(), false, array('foreign_keys' => array($name => null)));
+ }
+
+ $name = $db->getIndexName($name);
+ return $db->exec("DROP INDEX $name");
+ }
+
+ // }}}
+ // {{{ _dropFKTriggers()
+
+ /**
+ * Drop the triggers created to enforce the FOREIGN KEY constraint on the table
+ *
+ * @param string $table table name
+ * @param string $fkname FOREIGN KEY constraint name
+ * @param string $referenced_table referenced table name
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access private
+ */
+ function _dropFKTriggers($table, $fkname, $referenced_table)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $triggers = $this->listTableTriggers($table);
+ $triggers2 = $this->listTableTriggers($referenced_table);
+ if (!PEAR::isError($triggers2) && !PEAR::isError($triggers)) {
+ $triggers = array_merge($triggers, $triggers2);
+ $pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i';
+ foreach ($triggers as $trigger) {
+ if (preg_match($pattern, $trigger)) {
+ $result = $db->exec('DROP TRIGGER '.$trigger);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ }
+ }
+ }
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ listTableConstraints()
+
+ /**
+ * list all constraints in a table
+ *
+ * @param string $table name of table that should be used in method
+ * @return mixed array of constraint names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listTableConstraints($table)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $table = $db->quote($table, 'text');
+ $query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(tbl_name)='.strtolower($table);
+ } else {
+ $query.= "tbl_name=$table";
+ }
+ $query.= " AND sql NOT NULL ORDER BY name";
+ $indexes = $db->queryCol($query, 'text');
+ if (PEAR::isError($indexes)) {
+ return $indexes;
+ }
+
+ $result = array();
+ foreach ($indexes as $sql) {
+ if (preg_match("/^create unique index ([^ ]+) on /i", $sql, $tmp)) {
+ $index = $this->_fixIndexName($tmp[1]);
+ if (!empty($index)) {
+ $result[$index] = true;
+ }
+ }
+ }
+
+ // also search in table definition for PRIMARY KEYs...
+ $query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(name)='.strtolower($table);
+ } else {
+ $query.= "name=$table";
+ }
+ $query.= " AND sql NOT NULL ORDER BY name";
+ $table_def = $db->queryOne($query, 'text');
+ if (PEAR::isError($table_def)) {
+ return $table_def;
+ }
+ if (preg_match("/\bPRIMARY\s+KEY\b/i", $table_def, $tmp)) {
+ $result['primary'] = true;
+ }
+
+ // ...and for FOREIGN KEYs
+ if (preg_match_all("/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN\s+KEY/imsx", $table_def, $tmp)) {
+ foreach ($tmp[1] as $fk) {
+ $result[$fk] = true;
+ }
+ }
+
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $result = array_change_key_case($result, $db->options['field_case']);
+ }
+ return array_keys($result);
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * create sequence
+ *
+ * @param string $seq_name name of the sequence to be created
+ * @param string $start start value of the sequence; default is 1
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function createSequence($seq_name, $start = 1)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
+ $seqcol_name = $db->quoteIdentifier($db->options['seqcol_name'], true);
+ $query = "CREATE TABLE $sequence_name ($seqcol_name INTEGER PRIMARY KEY DEFAULT 0 NOT NULL)";
+ $res = $db->exec($query);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ if ($start == 1) {
+ return MDB2_OK;
+ }
+ $res = $db->exec("INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')');
+ if (!PEAR::isError($res)) {
+ return MDB2_OK;
+ }
+ // Handle error
+ $result = $db->exec("DROP TABLE $sequence_name");
+ if (PEAR::isError($result)) {
+ return $db->raiseError($result, null, null,
+ 'could not drop inconsistent sequence table', __FUNCTION__);
+ }
+ return $db->raiseError($res, null, null,
+ 'could not create sequence table', __FUNCTION__);
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * drop existing sequence
+ *
+ * @param string $seq_name name of the sequence to be dropped
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function dropSequence($seq_name)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
+ return $db->exec("DROP TABLE $sequence_name");
+ }
+
+ // }}}
+ // {{{ listSequences()
+
+ /**
+ * list all sequences in the current database
+ *
+ * @return mixed array of sequence names on success, a MDB2 error on failure
+ * @access public
+ */
+ function listSequences($dummy=null)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $query = "SELECT name FROM sqlite_master WHERE type='table' AND sql NOT NULL ORDER BY name";
+ $table_names = $db->queryCol($query);
+ if (PEAR::isError($table_names)) {
+ return $table_names;
+ }
+ $result = array();
+ foreach ($table_names as $table_name) {
+ if ($sqn = $this->_fixSequenceName($table_name, true)) {
+ $result[] = $sqn;
+ }
+ }
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
+ }
+ return $result;
+ }
+
+ // }}}
+}
+?> \ No newline at end of file
diff --git a/lib/MDB2/Driver/Native/sqlite3.php b/lib/MDB2/Driver/Native/sqlite3.php
new file mode 100644
index 00000000000..81dc67dea65
--- /dev/null
+++ b/lib/MDB2/Driver/Native/sqlite3.php
@@ -0,0 +1,58 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2011 Robin Appelman |
+// | All rights reserved. |
+// +----------------------------------------------------------------------+
+// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
+// | API as well as database abstraction for PHP applications. |
+// | This LICENSE is in the BSD license style. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | |
+// | Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution. |
+// | |
+// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
+// | Lukas Smith nor the names of his contributors may be used to endorse |
+// | or promote products derived from this software without specific prior|
+// | written permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
+// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
+// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
+// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
+// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
+// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
+// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
+// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
+// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
+// | POSSIBILITY OF SUCH DAMAGE. |
+// +----------------------------------------------------------------------+
+// | Author: Robin Appelman <icewind1991@gmail.com> |
+// +----------------------------------------------------------------------+
+//
+//
+require_once 'MDB2/Driver/Native/Common.php';
+
+/**
+ * MDB2 SQLite driver for the native module
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Native_sqlite extends MDB2_Driver_Native_Common
+{
+}
+?> \ No newline at end of file
diff --git a/lib/MDB2/Driver/Reverse/sqlite3.php b/lib/MDB2/Driver/Reverse/sqlite3.php
new file mode 100644
index 00000000000..d488977b158
--- /dev/null
+++ b/lib/MDB2/Driver/Reverse/sqlite3.php
@@ -0,0 +1,607 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2011 Robin Appelman |
+// | All rights reserved. |
+// +----------------------------------------------------------------------+
+// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
+// | API as well as database abstraction for PHP applications. |
+// | This LICENSE is in the BSD license style. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | |
+// | Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution. |
+// | |
+// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
+// | Lukas Smith nor the names of his contributors may be used to endorse |
+// | or promote products derived from this software without specific prior|
+// | written permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
+// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
+// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
+// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
+// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
+// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
+// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
+// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
+// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
+// | POSSIBILITY OF SUCH DAMAGE. |
+// +----------------------------------------------------------------------+
+// | Author: Robin Appelman <icewind1991@gmail.com> |
+// +----------------------------------------------------------------------+
+//
+//
+
+require_once('MDB2/Driver/Reverse/Common.php');
+
+/**
+ * MDB2 SQlite driver for the schema reverse engineering module
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Reverse_sqlite3 extends MDB2_Driver_Reverse_Common
+{
+ /**
+ * Remove SQL comments from the field definition
+ *
+ * @access private
+ */
+ function _removeComments($sql) {
+ $lines = explode("\n", $sql);
+ foreach ($lines as $k => $line) {
+ $pieces = explode('--', $line);
+ if (count($pieces) > 1 && (substr_count($pieces[0], '\'') % 2) == 0) {
+ $lines[$k] = substr($line, 0, strpos($line, '--'));
+ }
+ }
+ return implode("\n", $lines);
+ }
+
+ /**
+ *
+ */
+ function _getTableColumns($sql)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+ $start_pos = strpos($sql, '(');
+ $end_pos = strrpos($sql, ')');
+ $column_def = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
+ // replace the decimal length-places-separator with a colon
+ $column_def = preg_replace('/(\d),(\d)/', '\1:\2', $column_def);
+ $column_def = $this->_removeComments($column_def);
+ $column_sql = explode(',', $column_def);
+ $columns = array();
+ $count = count($column_sql);
+ if ($count == 0) {
+ return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'unexpected empty table column definition list', __FUNCTION__);
+ }
+ $regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i';
+ $regexp2 = '/^\s*([^ ]+) +(PRIMARY|UNIQUE|CHECK)$/i';
+ for ($i=0, $j=0; $i<$count; ++$i) {
+ if (!preg_match($regexp, trim($column_sql[$i]), $matches)) {
+ if (!preg_match($regexp2, trim($column_sql[$i]))) {
+ continue;
+ }
+ return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'unexpected table column SQL definition: "'.$column_sql[$i].'"', __FUNCTION__);
+ }
+ $columns[$j]['name'] = trim($matches[1], implode('', $db->identifier_quoting));
+ $columns[$j]['type'] = strtolower($matches[2]);
+ if (isset($matches[4]) && strlen($matches[4])) {
+ $columns[$j]['length'] = $matches[4];
+ }
+ if (isset($matches[6]) && strlen($matches[6])) {
+ $columns[$j]['decimal'] = $matches[6];
+ }
+ if (isset($matches[8]) && strlen($matches[8])) {
+ $columns[$j]['unsigned'] = true;
+ }
+ if (isset($matches[9]) && strlen($matches[9])) {
+ $columns[$j]['autoincrement'] = true;
+ }
+ if (isset($matches[12]) && strlen($matches[12])) {
+ $default = $matches[12];
+ if (strlen($default) && $default[0]=="'") {
+ $default = str_replace("''", "'", substr($default, 1, strlen($default)-2));
+ }
+ if ($default === 'NULL') {
+ $default = null;
+ }
+ $columns[$j]['default'] = $default;
+ }
+ if (isset($matches[7]) && strlen($matches[7])) {
+ $columns[$j]['notnull'] = ($matches[7] === ' NOT NULL');
+ } else if (isset($matches[9]) && strlen($matches[9])) {
+ $columns[$j]['notnull'] = ($matches[9] === ' NOT NULL');
+ } else if (isset($matches[13]) && strlen($matches[13])) {
+ $columns[$j]['notnull'] = ($matches[13] === ' NOT NULL');
+ }
+ ++$j;
+ }
+ return $columns;
+ }
+
+ // {{{ getTableFieldDefinition()
+
+ /**
+ * Get the stucture of a field into an array
+ *
+ * @param string $table_name name of table that should be used in method
+ * @param string $field_name name of field that should be used in method
+ * @return mixed data array on success, a MDB2 error on failure.
+ * The returned array contains an array for each field definition,
+ * with (some of) these indices:
+ * [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type]
+ * @access public
+ */
+ function getTableFieldDefinition($table_name, $field_name)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ list($schema, $table) = $this->splitTableSchema($table_name);
+
+ $result = $db->loadModule('Datatype', null, true);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
+ } else {
+ $query.= 'name='.$db->quote($table, 'text');
+ }
+ $sql = $db->queryOne($query);
+ if (PEAR::isError($sql)) {
+ return $sql;
+ }
+ $columns = $this->_getTableColumns($sql);
+ foreach ($columns as $column) {
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ if ($db->options['field_case'] == CASE_LOWER) {
+ $column['name'] = strtolower($column['name']);
+ } else {
+ $column['name'] = strtoupper($column['name']);
+ }
+ } else {
+ $column = array_change_key_case($column, $db->options['field_case']);
+ }
+ if ($field_name == $column['name']) {
+ $mapped_datatype = $db->datatype->mapNativeDatatype($column);
+ if (PEAR::isError($mapped_datatype)) {
+ return $mapped_datatype;
+ }
+ list($types, $length, $unsigned, $fixed) = $mapped_datatype;
+ $notnull = false;
+ if (!empty($column['notnull'])) {
+ $notnull = $column['notnull'];
+ }
+ $default = false;
+ if (array_key_exists('default', $column)) {
+ $default = $column['default'];
+ if (is_null($default) && $notnull) {
+ $default = '';
+ }
+ }
+ $autoincrement = false;
+ if (!empty($column['autoincrement'])) {
+ $autoincrement = true;
+ }
+
+ $definition[0] = array(
+ 'notnull' => $notnull,
+ 'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
+ );
+ if (!is_null($length)) {
+ $definition[0]['length'] = $length;
+ }
+ if (!is_null($unsigned)) {
+ $definition[0]['unsigned'] = $unsigned;
+ }
+ if (!is_null($fixed)) {
+ $definition[0]['fixed'] = $fixed;
+ }
+ if ($default !== false) {
+ $definition[0]['default'] = $default;
+ }
+ if ($autoincrement !== false) {
+ $definition[0]['autoincrement'] = $autoincrement;
+ }
+ foreach ($types as $key => $type) {
+ $definition[$key] = $definition[0];
+ if ($type == 'clob' || $type == 'blob') {
+ unset($definition[$key]['default']);
+ }
+ $definition[$key]['type'] = $type;
+ $definition[$key]['mdb2type'] = $type;
+ }
+ return $definition;
+ }
+ }
+
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'it was not specified an existing table column', __FUNCTION__);
+ }
+
+ // }}}
+ // {{{ getTableIndexDefinition()
+
+ /**
+ * Get the stucture of an index into an array
+ *
+ * @param string $table_name name of table that should be used in method
+ * @param string $index_name name of index that should be used in method
+ * @return mixed data array on success, a MDB2 error on failure
+ * @access public
+ */
+ function getTableIndexDefinition($table_name, $index_name)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ list($schema, $table) = $this->splitTableSchema($table_name);
+
+ $query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
+ } else {
+ $query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
+ }
+ $query.= ' AND sql NOT NULL ORDER BY name';
+ $index_name_mdb2 = $db->getIndexName($index_name);
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $qry = sprintf($query, $db->quote(strtolower($index_name_mdb2), 'text'));
+ } else {
+ $qry = sprintf($query, $db->quote($index_name_mdb2, 'text'));
+ }
+ $sql = $db->queryOne($qry, 'text');
+ if (PEAR::isError($sql) || empty($sql)) {
+ // fallback to the given $index_name, without transformation
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $qry = sprintf($query, $db->quote(strtolower($index_name), 'text'));
+ } else {
+ $qry = sprintf($query, $db->quote($index_name, 'text'));
+ }
+ $sql = $db->queryOne($qry, 'text');
+ }
+ if (PEAR::isError($sql)) {
+ return $sql;
+ }
+ if (!$sql) {
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'it was not specified an existing table index', __FUNCTION__);
+ }
+
+ $sql = strtolower($sql);
+ $start_pos = strpos($sql, '(');
+ $end_pos = strrpos($sql, ')');
+ $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
+ $column_names = explode(',', $column_names);
+
+ if (preg_match("/^create unique/", $sql)) {
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'it was not specified an existing table index', __FUNCTION__);
+ }
+
+ $definition = array();
+ $count = count($column_names);
+ for ($i=0; $i<$count; ++$i) {
+ $column_name = strtok($column_names[$i], ' ');
+ $collation = strtok(' ');
+ $definition['fields'][$column_name] = array(
+ 'position' => $i+1
+ );
+ if (!empty($collation)) {
+ $definition['fields'][$column_name]['sorting'] =
+ ($collation=='ASC' ? 'ascending' : 'descending');
+ }
+ }
+
+ if (empty($definition['fields'])) {
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'it was not specified an existing table index', __FUNCTION__);
+ }
+ return $definition;
+ }
+
+ // }}}
+ // {{{ getTableConstraintDefinition()
+
+ /**
+ * Get the stucture of a constraint into an array
+ *
+ * @param string $table_name name of table that should be used in method
+ * @param string $constraint_name name of constraint that should be used in method
+ * @return mixed data array on success, a MDB2 error on failure
+ * @access public
+ */
+ function getTableConstraintDefinition($table_name, $constraint_name)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ list($schema, $table) = $this->splitTableSchema($table_name);
+
+ $query = "SELECT sql FROM sqlite_master WHERE type='index' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(name)=%s AND LOWER(tbl_name)=' . $db->quote(strtolower($table), 'text');
+ } else {
+ $query.= 'name=%s AND tbl_name=' . $db->quote($table, 'text');
+ }
+ $query.= ' AND sql NOT NULL ORDER BY name';
+ $constraint_name_mdb2 = $db->getIndexName($constraint_name);
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $qry = sprintf($query, $db->quote(strtolower($constraint_name_mdb2), 'text'));
+ } else {
+ $qry = sprintf($query, $db->quote($constraint_name_mdb2, 'text'));
+ }
+ $sql = $db->queryOne($qry, 'text');
+ if (PEAR::isError($sql) || empty($sql)) {
+ // fallback to the given $index_name, without transformation
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $qry = sprintf($query, $db->quote(strtolower($constraint_name), 'text'));
+ } else {
+ $qry = sprintf($query, $db->quote($constraint_name, 'text'));
+ }
+ $sql = $db->queryOne($qry, 'text');
+ }
+ if (PEAR::isError($sql)) {
+ return $sql;
+ }
+ //default values, eventually overridden
+ $definition = array(
+ 'primary' => false,
+ 'unique' => false,
+ 'foreign' => false,
+ 'check' => false,
+ 'fields' => array(),
+ 'references' => array(
+ 'table' => '',
+ 'fields' => array(),
+ ),
+ 'onupdate' => '',
+ 'ondelete' => '',
+ 'match' => '',
+ 'deferrable' => false,
+ 'initiallydeferred' => false,
+ );
+ if (!$sql) {
+ $query = "SELECT sql FROM sqlite_master WHERE type='table' AND ";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= 'LOWER(name)='.$db->quote(strtolower($table), 'text');
+ } else {
+ $query.= 'name='.$db->quote($table, 'text');
+ }
+ $query.= " AND sql NOT NULL ORDER BY name";
+ $sql = $db->queryOne($query, 'text');
+ if (PEAR::isError($sql)) {
+ return $sql;
+ }
+ if ($constraint_name == 'primary') {
+ // search in table definition for PRIMARY KEYs
+ if (preg_match("/\bPRIMARY\s+KEY\b\s*\(([^)]+)/i", $sql, $tmp)) {
+ $definition['primary'] = true;
+ $definition['fields'] = array();
+ $column_names = explode(',', $tmp[1]);
+ $colpos = 1;
+ foreach ($column_names as $column_name) {
+ $definition['fields'][trim($column_name)] = array(
+ 'position' => $colpos++
+ );
+ }
+ return $definition;
+ }
+ if (preg_match("/\"([^\"]+)\"[^\,\"]+\bPRIMARY\s+KEY\b[^\,\)]*/i", $sql, $tmp)) {
+ $definition['primary'] = true;
+ $definition['fields'] = array();
+ $column_names = explode(',', $tmp[1]);
+ $colpos = 1;
+ foreach ($column_names as $column_name) {
+ $definition['fields'][trim($column_name)] = array(
+ 'position' => $colpos++
+ );
+ }
+ return $definition;
+ }
+ } else {
+ // search in table definition for FOREIGN KEYs
+ $pattern = "/\bCONSTRAINT\b\s+%s\s+
+ \bFOREIGN\s+KEY\b\s*\(([^\)]+)\)\s*
+ \bREFERENCES\b\s+([^\s]+)\s*\(([^\)]+)\)\s*
+ (?:\bMATCH\s*([^\s]+))?\s*
+ (?:\bON\s+UPDATE\s+([^\s,\)]+))?\s*
+ (?:\bON\s+DELETE\s+([^\s,\)]+))?\s*
+ /imsx";
+ $found_fk = false;
+ if (preg_match(sprintf($pattern, $constraint_name_mdb2), $sql, $tmp)) {
+ $found_fk = true;
+ } elseif (preg_match(sprintf($pattern, $constraint_name), $sql, $tmp)) {
+ $found_fk = true;
+ }
+ if ($found_fk) {
+ $definition['foreign'] = true;
+ $definition['match'] = 'SIMPLE';
+ $definition['onupdate'] = 'NO ACTION';
+ $definition['ondelete'] = 'NO ACTION';
+ $definition['references']['table'] = $tmp[2];
+ $column_names = explode(',', $tmp[1]);
+ $colpos = 1;
+ foreach ($column_names as $column_name) {
+ $definition['fields'][trim($column_name)] = array(
+ 'position' => $colpos++
+ );
+ }
+ $referenced_cols = explode(',', $tmp[3]);
+ $colpos = 1;
+ foreach ($referenced_cols as $column_name) {
+ $definition['references']['fields'][trim($column_name)] = array(
+ 'position' => $colpos++
+ );
+ }
+ if (isset($tmp[4])) {
+ $definition['match'] = $tmp[4];
+ }
+ if (isset($tmp[5])) {
+ $definition['onupdate'] = $tmp[5];
+ }
+ if (isset($tmp[6])) {
+ $definition['ondelete'] = $tmp[6];
+ }
+ return $definition;
+ }
+ }
+ $sql = false;
+ }
+ if (!$sql) {
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ $constraint_name . ' is not an existing table constraint', __FUNCTION__);
+ }
+
+ $sql = strtolower($sql);
+ $start_pos = strpos($sql, '(');
+ $end_pos = strrpos($sql, ')');
+ $column_names = substr($sql, $start_pos+1, $end_pos-$start_pos-1);
+ $column_names = explode(',', $column_names);
+
+ if (!preg_match("/^create unique/", $sql)) {
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ $constraint_name . ' is not an existing table constraint', __FUNCTION__);
+ }
+
+ $definition['unique'] = true;
+ $count = count($column_names);
+ for ($i=0; $i<$count; ++$i) {
+ $column_name = strtok($column_names[$i]," ");
+ $collation = strtok(" ");
+ $definition['fields'][$column_name] = array(
+ 'position' => $i+1
+ );
+ if (!empty($collation)) {
+ $definition['fields'][$column_name]['sorting'] =
+ ($collation=='ASC' ? 'ascending' : 'descending');
+ }
+ }
+
+ if (empty($definition['fields'])) {
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ $constraint_name . ' is not an existing table constraint', __FUNCTION__);
+ }
+ return $definition;
+ }
+
+ // }}}
+ // {{{ getTriggerDefinition()
+
+ /**
+ * Get the structure of a trigger into an array
+ *
+ * EXPERIMENTAL
+ *
+ * WARNING: this function is experimental and may change the returned value
+ * at any time until labelled as non-experimental
+ *
+ * @param string $trigger name of trigger that should be used in method
+ * @return mixed data array on success, a MDB2 error on failure
+ * @access public
+ */
+ function getTriggerDefinition($trigger)
+ {
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ $query = "SELECT name as trigger_name,
+ tbl_name AS table_name,
+ sql AS trigger_body,
+ NULL AS trigger_type,
+ NULL AS trigger_event,
+ NULL AS trigger_comment,
+ 1 AS trigger_enabled
+ FROM sqlite_master
+ WHERE type='trigger'";
+ if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $query.= ' AND LOWER(name)='.$db->quote(strtolower($trigger), 'text');
+ } else {
+ $query.= ' AND name='.$db->quote($trigger, 'text');
+ }
+ $types = array(
+ 'trigger_name' => 'text',
+ 'table_name' => 'text',
+ 'trigger_body' => 'text',
+ 'trigger_type' => 'text',
+ 'trigger_event' => 'text',
+ 'trigger_comment' => 'text',
+ 'trigger_enabled' => 'boolean',
+ );
+ $def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
+ if (PEAR::isError($def)) {
+ return $def;
+ }
+ if (empty($def)) {
+ return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'it was not specified an existing trigger', __FUNCTION__);
+ }
+ if (preg_match("/^create\s+(?:temp|temporary)?trigger\s+(?:if\s+not\s+exists\s+)?.*(before|after)?\s+(insert|update|delete)/Uims", $def['trigger_body'], $tmp)) {
+ $def['trigger_type'] = strtoupper($tmp[1]);
+ $def['trigger_event'] = strtoupper($tmp[2]);
+ }
+ return $def;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table
+ *
+ * @param string $result a string containing the name of a table
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A MDB2_Error object on failure.
+ *
+ * @see MDB2_Driver_Common::tableInfo()
+ * @since Method available since Release 1.7.0
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ return parent::tableInfo($result, $mode);
+ }
+
+ $db =$this->getDBInstance();
+ if (PEAR::isError($db)) {
+ return $db;
+ }
+
+ return $db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null,
+ 'This DBMS can not obtain tableInfo from result sets', __FUNCTION__);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/lib/MDB2/Driver/sqlite3.php b/lib/MDB2/Driver/sqlite3.php
new file mode 100644
index 00000000000..3b74afed146
--- /dev/null
+++ b/lib/MDB2/Driver/sqlite3.php
@@ -0,0 +1,1361 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2011 Robin Appelman |
+// | All rights reserved. |
+// +----------------------------------------------------------------------+
+// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
+// | API as well as database abstraction for PHP applications. |
+// | This LICENSE is in the BSD license style. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | |
+// | Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution. |
+// | |
+// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
+// | Lukas Smith nor the names of his contributors may be used to endorse |
+// | or promote products derived from this software without specific prior|
+// | written permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
+// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
+// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
+// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
+// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
+// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
+// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
+// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
+// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
+// | POSSIBILITY OF SUCH DAMAGE. |
+// +----------------------------------------------------------------------+
+// | Author: Robin Appelman <icewind1991@gmail.com> |
+// +----------------------------------------------------------------------+
+//
+//
+
+/**
+ * MDB2 SQLite3 driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_sqlite3 extends MDB2_Driver_Common
+{
+ // {{{ properties
+ public $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false);
+
+ public $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
+
+ public $_lasterror = '';
+
+ public $fix_assoc_fields_names = false;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ parent::__construct();
+
+ $this->phptype = 'sqlite3';
+ $this->dbsyntax = 'sqlite';
+
+ $this->supported['sequences'] = 'emulated';
+ $this->supported['indexes'] = true;
+ $this->supported['affected_rows'] = true;
+ $this->supported['summary_functions'] = true;
+ $this->supported['order_by_text'] = true;
+ $this->supported['current_id'] = 'emulated';
+ $this->supported['limit_queries'] = true;
+ $this->supported['LOBs'] = true;
+ $this->supported['replace'] = true;
+ $this->supported['transactions'] = false;
+ $this->supported['savepoints'] = false;
+ $this->supported['sub_selects'] = true;
+ $this->supported['triggers'] = true;
+ $this->supported['auto_increment'] = true;
+ $this->supported['primary_key'] = false; // requires alter table implementation
+ $this->supported['result_introspection'] = false; // not implemented
+ $this->supported['prepared_statements'] = true;
+ $this->supported['identifier_quoting'] = true;
+ $this->supported['pattern_escaping'] = false;
+ $this->supported['new_link'] = false;
+
+ $this->options['DBA_username'] = false;
+ $this->options['DBA_password'] = false;
+ $this->options['base_transaction_name'] = '___php_MDB2_sqlite_auto_commit_off';
+ $this->options['fixed_float'] = 0;
+ $this->options['database_path'] = '';
+ $this->options['database_extension'] = '';
+ $this->options['server_version'] = '';
+ $this->options['max_identifiers_length'] = 128; //no real limit
+ }
+
+ // }}}
+ // {{{ errorInfo()
+
+ /**
+ * This method is used to collect information about an error
+ *
+ * @param integer $error
+ * @return array
+ * @access public
+ */
+ function errorInfo($error = null)
+ {
+ $native_code = null;
+ if ($this->connection) {
+ $native_code = $this->connection->lastErrorCode();
+ }
+ $native_msg = $this->_lasterror
+ ? html_entity_decode($this->_lasterror) : $this->connection->lastErrorMsg();
+
+ // PHP 5.2+ prepends the function name to $php_errormsg, so we need
+ // this hack to work around it, per bug #9599.
+ $native_msg = preg_replace('/^sqlite[a-z_]+\(\)[^:]*: /', '', $native_msg);
+
+ if (is_null($error)) {
+ static $error_regexps;
+ if (empty($error_regexps)) {
+ $error_regexps = array(
+ '/^no such table:/' => MDB2_ERROR_NOSUCHTABLE,
+ '/^no such index:/' => MDB2_ERROR_NOT_FOUND,
+ '/^(table|index) .* already exists$/' => MDB2_ERROR_ALREADY_EXISTS,
+ '/PRIMARY KEY must be unique/i' => MDB2_ERROR_CONSTRAINT,
+ '/is not unique/' => MDB2_ERROR_CONSTRAINT,
+ '/columns .* are not unique/i' => MDB2_ERROR_CONSTRAINT,
+ '/uniqueness constraint failed/' => MDB2_ERROR_CONSTRAINT,
+ '/may not be NULL/' => MDB2_ERROR_CONSTRAINT_NOT_NULL,
+ '/^no such column:/' => MDB2_ERROR_NOSUCHFIELD,
+ '/no column named/' => MDB2_ERROR_NOSUCHFIELD,
+ '/column not present in both tables/i' => MDB2_ERROR_NOSUCHFIELD,
+ '/^near ".*": syntax error$/' => MDB2_ERROR_SYNTAX,
+ '/[0-9]+ values for [0-9]+ columns/i' => MDB2_ERROR_VALUE_COUNT_ON_ROW,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $native_msg)) {
+ $error = $code;
+ break;
+ }
+ }
+ }
+ return array($error, $native_code, $native_msg);
+ }
+
+ // }}}
+ // {{{ escape()
+
+ /**
+ * Quotes a string so it can be safely used in a query. It will quote
+ * the text so it can safely be used within a query.
+ *
+ * @param string the input string to quote
+ * @param bool escape wildcards
+ *
+ * @return string quoted string
+ *
+ * @access public
+ */
+ public function escape($text, $escape_wildcards = false)
+ {
+ if($this->connection){
+ return $this->connection->escapeString($text);
+ }else{
+ return str_replace("'","''",$text);//TODO; more
+ }
+ }
+
+ // }}}
+ // {{{ beginTransaction()
+
+ /**
+ * Start a transaction or set a savepoint.
+ *
+ * @param string name of a savepoint to set
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ */
+ function beginTransaction($savepoint = null)
+ {
+ $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
+ if (!is_null($savepoint)) {
+ return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'savepoints are not supported', __FUNCTION__);
+ } elseif ($this->in_transaction) {
+ return MDB2_OK; //nothing to do
+ }
+ if (!$this->destructor_registered && $this->opened_persistent) {
+ $this->destructor_registered = true;
+ register_shutdown_function('MDB2_closeOpenTransactions');
+ }
+ $query = 'BEGIN TRANSACTION '.$this->options['base_transaction_name'];
+ $result =$this->_doQuery($query, true);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $this->in_transaction = true;
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commit the database changes done during a transaction that is in
+ * progress or release a savepoint. This function may only be called when
+ * auto-committing is disabled, otherwise it will fail. Therefore, a new
+ * transaction is implicitly started after committing the pending changes.
+ *
+ * @param string name of a savepoint to release
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ */
+ function commit($savepoint = null)
+ {
+ $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
+ if (!$this->in_transaction) {
+ return $this->raiseError(MDB2_ERROR_INVALID, null, null,
+ 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
+ }
+ if (!is_null($savepoint)) {
+ return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'savepoints are not supported', __FUNCTION__);
+ }
+
+ $query = 'COMMIT TRANSACTION '.$this->options['base_transaction_name'];
+ $result =$this->_doQuery($query, true);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $this->in_transaction = false;
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{
+
+ /**
+ * Cancel any database changes done during a transaction or since a specific
+ * savepoint that is in progress. This function may only be called when
+ * auto-committing is disabled, otherwise it will fail. Therefore, a new
+ * transaction is implicitly started after canceling the pending changes.
+ *
+ * @param string name of a savepoint to rollback to
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ */
+ function rollback($savepoint = null)
+ {
+ $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
+ if (!$this->in_transaction) {
+ return $this->raiseError(MDB2_ERROR_INVALID, null, null,
+ 'rollback cannot be done changes are auto committed', __FUNCTION__);
+ }
+ if (!is_null($savepoint)) {
+ return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'savepoints are not supported', __FUNCTION__);
+ }
+
+ $query = 'ROLLBACK TRANSACTION '.$this->options['base_transaction_name'];
+ $result =$this->_doQuery($query, true);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $this->in_transaction = false;
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ function setTransactionIsolation()
+
+ /**
+ * Set the transacton isolation level.
+ *
+ * @param string standard isolation level
+ * READ UNCOMMITTED (allows dirty reads)
+ * READ COMMITTED (prevents dirty reads)
+ * REPEATABLE READ (prevents nonrepeatable reads)
+ * SERIALIZABLE (prevents phantom reads)
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ * @since 2.1.1
+ */
+ static function setTransactionIsolation($isolation,$options=array())
+ {
+ $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
+ switch ($isolation) {
+ case 'READ UNCOMMITTED':
+ $isolation = 0;
+ break;
+ case 'READ COMMITTED':
+ case 'REPEATABLE READ':
+ case 'SERIALIZABLE':
+ $isolation = 1;
+ break;
+ default:
+ return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'isolation level is not supported: '.$isolation, __FUNCTION__);
+ }
+
+ $query = "PRAGMA read_uncommitted=$isolation";
+ return $this->_doQuery($query, true);
+ }
+
+ // }}}
+ // {{{ getDatabaseFile()
+
+ /**
+ * Builds the string with path+dbname+extension
+ *
+ * @return string full database path+file
+ * @access protected
+ */
+ function _getDatabaseFile($database_name)
+ {
+ if ($database_name === '' || $database_name === ':memory:') {
+ return $database_name;
+ }
+ return $this->options['database_path'].$database_name.$this->options['database_extension'];
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database
+ *
+ * @return true on success, MDB2 Error Object on failure
+ **/
+ function connect()
+ {
+ if($this->connection instanceof SQLite3){
+ return MDB2_OK;
+ }
+ global $SERVERROOT;
+ $datadir=OC_CONFIG::getValue( "datadirectory", "$SERVERROOT/data" );
+ $database_file = $this->_getDatabaseFile($this->database_name);
+ if (is_resource($this->connection)) {
+ //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
+ if (MDB2::areEquals($this->connected_dsn, $this->dsn)
+ && $this->connected_database_name == $database_file
+ && $this->opened_persistent == $this->options['persistent']
+ ) {
+ return MDB2_OK;
+ }
+ $this->disconnect(false);
+ }
+
+ if (!PEAR::loadExtension($this->phptype)) {
+ return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
+ }
+
+ if (empty($this->database_name)) {
+ return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
+ 'unable to establish a connection', __FUNCTION__);
+ }
+
+ if ($database_file !== ':memory:') {
+ if(!strpos($database_file,'.db')){
+ $database_file="$datadir/$database_file.db";
+ }
+ if (!file_exists($database_file)) {
+ if (!touch($database_file)) {
+ return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'Could not create database file', __FUNCTION__);
+ }
+ if (!isset($this->dsn['mode'])
+ || !is_numeric($this->dsn['mode'])
+ ) {
+ $mode = 0644;
+ } else {
+ $mode = octdec($this->dsn['mode']);
+ }
+ if (!chmod($database_file, $mode)) {
+ return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'Could not be chmodded database file', __FUNCTION__);
+ }
+ if (!file_exists($database_file)) {
+ return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'Could not be found database file', __FUNCTION__);
+ }
+ }
+ if (!is_file($database_file)) {
+ return $this->raiseError(MDB2_ERROR_INVALID, null, null,
+ 'Database is a directory name', __FUNCTION__);
+ }
+ if (!is_readable($database_file)) {
+ return $this->raiseError(MDB2_ERROR_ACCESS_VIOLATION, null, null,
+ 'Could not read database file', __FUNCTION__);
+ }
+ }
+
+ $php_errormsg = '';
+ $this->connection = new SQLite3($database_file);
+ if(is_callable(array($this->connection,'busyTimeout'))){//busy timout is only available in php>=5.3
+ $this->connection->busyTimeout(100);
+ }
+ $this->_lasterror = $this->connection->lastErrorMsg();
+ if (!$this->connection) {
+ return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
+ 'unable to establish a connection', __FUNCTION__);
+ }
+
+ if ($this->fix_assoc_fields_names ||
+ $this->options['portability'] & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES)
+ {
+ $this->connection->exec("PRAGMA short_column_names = 1");
+ $this->fix_assoc_fields_names = true;
+ }
+
+ $this->connected_dsn = $this->dsn;
+ $this->connected_database_name = $database_file;
+ $this->opened_persistent = $this->getoption('persistent');
+ $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
+
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ databaseExists()
+
+ /**
+ * check if given database name is exists?
+ *
+ * @param string $name name of the database that should be checked
+ *
+ * @return mixed true/false on success, a MDB2 error on failure
+ * @access public
+ */
+ function databaseExists($name)
+ {
+ $database_file = $this->_getDatabaseFile($name);
+ $result = file_exists($database_file);
+ return $result;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Log out and disconnect from the database.
+ *
+ * @param boolean $force if the disconnect should be forced even if the
+ * connection is opened persistently
+ * @return mixed true on success, false if not connected and error
+ * object on error
+ * @access public
+ */
+ function disconnect($force = true)
+ {
+ if ($this->connection instanceof SQLite3) {
+ if ($this->in_transaction) {
+ $dsn = $this->dsn;
+ $database_name = $this->database_name;
+ $persistent = $this->options['persistent'];
+ $this->dsn = $this->connected_dsn;
+ $this->database_name = $this->connected_database_name;
+ $this->options['persistent'] = $this->opened_persistent;
+ $this->rollback();
+ $this->dsn = $dsn;
+ $this->database_name = $database_name;
+ $this->options['persistent'] = $persistent;
+ }
+
+ if (!$this->opened_persistent || $force) {
+ $this->connection->close();
+ }
+ } else {
+ return false;
+ }
+ return parent::disconnect($force);
+ }
+
+ // }}}
+ // {{{ _doQuery()
+
+ /**
+ * Execute a query
+ * @param string $query query
+ * @param boolean $is_manip if the query is a manipulation query
+ * @param resource $connection
+ * @param string $database_name
+ * @return result or error object
+ * @access protected
+ */
+ function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
+ {
+ $this->last_query = $query;
+ $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
+ if ($result) {
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $query = $result;
+ }
+ if ($this->options['disable_query']) {
+ $result = $is_manip ? 0 : null;
+ return $result;
+ }
+ $result=$this->connection->query($query.';');
+ $this->_lasterror = $this->connection->lastErrorMsg();
+
+ if (!$result) {
+ $err =$this->raiseError(null, null, null,
+ 'Could not execute statement', __FUNCTION__);
+ return $err;
+ }
+
+ $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
+ return $result;
+ }
+
+ // }}}
+ // {{{ _affectedRows()
+
+ /**
+ * Returns the number of rows affected
+ *
+ * @param resource $result
+ * @param resource $connection
+ * @return mixed MDB2 Error Object or the number of rows affected
+ * @access private
+ */
+ function _affectedRows($connection, $result = null)
+ {
+ return $this->connection->changes();
+ }
+
+ // }}}
+ // {{{ _modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * @param string $query query to modify
+ * @param boolean $is_manip if it is a DML query
+ * @param integer $limit limit the number of rows
+ * @param integer $offset start reading from given offset
+ * @return string modified query
+ * @access protected
+ */
+ function _modifyQuery($query, $is_manip, $limit, $offset)
+ {
+ if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) {
+ if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
+ $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
+ 'DELETE FROM \1 WHERE 1=1', $query);
+ }
+ }
+ if ($limit > 0
+ && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query)
+ ) {
+ $query = rtrim($query);
+ if (substr($query, -1) == ';') {
+ $query = substr($query, 0, -1);
+ }
+ if ($is_manip) {
+ $query.= " LIMIT $limit";
+ } else {
+ $query.= " LIMIT $offset,$limit";
+ }
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ getServerVersion()
+
+ /**
+ * return version information about the server
+ *
+ * @param bool $native determines if the raw version string should be returned
+ * @return mixed array/string with version information or MDB2 error object
+ * @access public
+ */
+ function getServerVersion($native = false)
+ {
+ $server_info = false;
+ if ($this->connected_server_info) {
+ $server_info = $this->connected_server_info;
+ } elseif ($this->options['server_version']) {
+ $server_info = $this->options['server_version'];
+ } elseif (function_exists('sqlite_libversion')) {
+ $server_info = @sqlite_libversion();
+ }
+ if (!$server_info) {
+ return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+ 'Requires either the "server_version" option or the sqlite_libversion() function', __FUNCTION__);
+ }
+ // cache server_info
+ $this->connected_server_info = $server_info;
+ if (!$native) {
+ $tmp = explode('.', $server_info, 3);
+ $server_info = array(
+ 'major' => isset($tmp[0]) ? $tmp[0] : null,
+ 'minor' => isset($tmp[1]) ? $tmp[1] : null,
+ 'patch' => isset($tmp[2]) ? $tmp[2] : null,
+ 'extra' => null,
+ 'native' => $server_info,
+ );
+ }
+ return $server_info;
+ }
+
+ // }}}
+ // {{{ replace()
+
+ /**
+ * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
+ * query, except that if there is already a row in the table with the same
+ * key field values, the old row is deleted before the new row is inserted.
+ *
+ * The REPLACE type of query does not make part of the SQL standards. Since
+ * practically only SQLite implements it natively, this type of query is
+ * emulated through this method for other DBMS using standard types of
+ * queries inside a transaction to assure the atomicity of the operation.
+ *
+ * @access public
+ *
+ * @param string $table name of the table on which the REPLACE query will
+ * be executed.
+ * @param array $fields associative array that describes the fields and the
+ * values that will be inserted or updated in the specified table. The
+ * indexes of the array are the names of all the fields of the table. The
+ * values of the array are also associative arrays that describe the
+ * values and other properties of the table fields.
+ *
+ * Here follows a list of field properties that need to be specified:
+ *
+ * value:
+ * Value to be assigned to the specified field. This value may be
+ * of specified in database independent type format as this
+ * function can perform the necessary datatype conversions.
+ *
+ * Default:
+ * this property is required unless the Null property
+ * is set to 1.
+ *
+ * type
+ * Name of the type of the field. Currently, all types Metabase
+ * are supported except for clob and blob.
+ *
+ * Default: no type conversion
+ *
+ * null
+ * Boolean property that indicates that the value for this field
+ * should be set to null.
+ *
+ * The default value for fields missing in INSERT queries may be
+ * specified the definition of a table. Often, the default value
+ * is already null, but since the REPLACE may be emulated using
+ * an UPDATE query, make sure that all fields of the table are
+ * listed in this function argument array.
+ *
+ * Default: 0
+ *
+ * key
+ * Boolean property that indicates that this field should be
+ * handled as a primary key or at least as part of the compound
+ * unique index of the table that will determine the row that will
+ * updated if it exists or inserted a new row otherwise.
+ *
+ * This function will fail if no key field is specified or if the
+ * value of a key field is set to null because fields that are
+ * part of unique index they may not be null.
+ *
+ * Default: 0
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ */
+ function replace($table, $fields)
+ {
+ $count = count($fields);
+ $query = $values = '';
+ $keys = $colnum = 0;
+ for (reset($fields); $colnum < $count; next($fields), $colnum++) {
+ $name = key($fields);
+ if ($colnum > 0) {
+ $query .= ',';
+ $values.= ',';
+ }
+ $query.= $this->quoteIdentifier($name, true);
+ if (isset($fields[$name]['null']) && $fields[$name]['null']) {
+ $value = 'NULL';
+ } else {
+ $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
+ $value = $this->quote($fields[$name]['value'], $type);
+ if (PEAR::isError($value)) {
+ return $value;
+ }
+ }
+ $values.= $value;
+ if (isset($fields[$name]['key']) && $fields[$name]['key']) {
+ if ($value === 'NULL') {
+ return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
+ 'key value '.$name.' may not be NULL', __FUNCTION__);
+ }
+ $keys++;
+ }
+ }
+ if ($keys == 0) {
+ return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
+ 'not specified which fields are keys', __FUNCTION__);
+ }
+
+ $connection = $this->getConnection();
+ if (PEAR::isError($connection)) {
+ return $connection;
+ }
+
+ $table = $this->quoteIdentifier($table, true);
+ $query = "REPLACE INTO $table ($query) VALUES ($values)";
+ $result =$this->_doQuery($query, true, $connection);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ return $this->_affectedRows($connection, $result);
+ }
+
+ // }}}
+ // {{{ nextID()
+
+ /**
+ * Returns the next free id of a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true the sequence is
+ * automatic created, if it
+ * not exists
+ *
+ * @return mixed MDB2 Error Object or id
+ * @access public
+ */
+ function nextID($seq_name, $ondemand = true)
+ {
+ $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
+ $seqcol_name = $this->options['seqcol_name'];
+ $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)";
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $this->expectError(MDB2_ERROR_NOSUCHTABLE);
+ $result =$this->_doQuery($query, true);
+ $this->popExpect();
+ $this->popErrorHandling();
+ if (PEAR::isError($result)) {
+ if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
+ $this->loadModule('Manager', null, true);
+ $result = $this->manager->createSequence($seq_name);
+ if (PEAR::isError($result)) {
+ return $this->raiseError($result, null, null,
+ 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__);
+ } else {
+ return $this->nextID($seq_name, false);
+ }
+ }
+ return $result;
+ }
+ $value = $this->lastInsertID();
+ if (is_numeric($value)) {
+ $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value";
+ $result =$this->_doQuery($query, true);
+ if (PEAR::isError($result)) {
+ $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name;
+ }
+ }
+ return $value;
+ }
+
+ // }}}
+ // {{{ lastInsertID()
+
+ /**
+ * Returns the autoincrement ID if supported or $id or fetches the current
+ * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
+ *
+ * @param string $table name of the table into which a new row was inserted
+ * @param string $field name of the field into which a new row was inserted
+ * @return mixed MDB2 Error Object or id
+ * @access public
+ */
+ function lastInsertID($table = null, $field = null)
+ {
+ return $this->connection->lastInsertRowID();
+ }
+
+ // }}}
+ // {{{ currID()
+
+ /**
+ * Returns the current id of a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @return mixed MDB2 Error Object or id
+ * @access public
+ */
+ function currID($seq_name)
+ {
+ $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
+ $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true);
+ $query = "SELECT MAX($seqcol_name) FROM $sequence_name";
+ return $this->queryOne($query, 'integer');
+ }
+
+ /**
+ * Prepares a query for multiple execution with execute().
+ * With some database backends, this is emulated.
+ * prepare() requires a generic query as string like
+ * 'INSERT INTO numbers VALUES(?,?)' or
+ * 'INSERT INTO numbers VALUES(:foo,:bar)'.
+ * The ? and :name and are placeholders which can be set using
+ * bindParam() and the query can be sent off using the execute() method.
+ * The allowed format for :name can be set with the 'bindname_format' option.
+ *
+ * @param string $query the query to prepare
+ * @param mixed $types array that contains the types of the placeholders
+ * @param mixed $result_types array that contains the types of the columns in
+ * the result set or MDB2_PREPARE_RESULT, if set to
+ * MDB2_PREPARE_MANIP the query is handled as a manipulation query
+ * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders
+ * @return mixed resource handle for the prepared query on success, a MDB2
+ * error on failure
+ * @access public
+ * @see bindParam, execute
+ */
+ function &prepare($query, $types = null, $result_types = null, $lobs = array())
+ {
+ if ($this->options['emulate_prepared']
+ || $this->supported['prepared_statements'] !== true
+ ) {
+ $obj =& parent::prepare($query, $types, $result_types, $lobs);
+ return $obj;
+ }
+ $this->last_query = $query;
+ $is_manip = ($result_types === MDB2_PREPARE_MANIP);
+ $offset = $this->offset;
+ $limit = $this->limit;
+ $this->offset = $this->limit = 0;
+ $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
+ $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
+ if ($result) {
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $query = $result;
+ }
+ $placeholder_type_guess = $placeholder_type = null;
+ $question = '?';
+ $colon = ':';
+ $positions = array();
+ $position = 0;
+ while ($position < strlen($query)) {
+ $q_position = strpos($query, $question, $position);
+ $c_position = strpos($query, $colon, $position);
+ if ($q_position && $c_position) {
+ $p_position = min($q_position, $c_position);
+ } elseif ($q_position) {
+ $p_position = $q_position;
+ } elseif ($c_position) {
+ $p_position = $c_position;
+ } else {
+ break;
+ }
+ if (is_null($placeholder_type)) {
+ $placeholder_type_guess = $query[$p_position];
+ }
+
+ $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
+ if (PEAR::isError($new_pos)) {
+ return $new_pos;
+ }
+ if ($new_pos != $position) {
+ $position = $new_pos;
+ continue; //evaluate again starting from the new position
+ }
+
+
+ if ($query[$position] == $placeholder_type_guess) {
+ if (is_null($placeholder_type)) {
+ $placeholder_type = $query[$p_position];
+ $question = $colon = $placeholder_type;
+ }
+ if ($placeholder_type == ':') {
+ $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s';
+ $parameter = preg_replace($regexp, '\\1', $query);
+ if ($parameter === '') {
+ $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
+ 'named parameter name must match "bindname_format" option', __FUNCTION__);
+ return $err;
+ }
+ $positions[$p_position] = $parameter;
+ $query = substr_replace($query, '?', $position, strlen($parameter)+1);
+ } else {
+ $positions[$p_position] = count($positions);
+ }
+ $position = $p_position + 1;
+ } else {
+ $position = $p_position;
+ }
+ }
+ $connection = $this->getConnection();
+ if (PEAR::isError($connection)) {
+ return $connection;
+ }
+ $statement =$this->connection->prepare($query);
+ if (!$statement) {
+ return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+ 'unable to prepare statement: '.$query);
+ }
+
+ $class_name = 'MDB2_Statement_'.$this->phptype;
+ $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
+ $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
+ return $obj;
+ }
+}
+
+/**
+ * MDB2 SQLite result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Result_sqlite3 extends MDB2_Result_Common
+{
+ // }}}
+ // {{{ fetchRow()
+
+ /**
+ * Fetch a row and insert the data into an existing array.
+ *
+ * @param int $fetchmode how the array data should be indexed
+ * @param int $rownum number of the row where the data can be found
+ * @return int data array on success, a MDB2 error on failure
+ * @access public
+ */
+ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
+ {
+ if (!is_null($rownum)) {
+ $seek = $this->seek($rownum);
+ if (PEAR::isError($seek)) {
+ return $seek;
+ }
+ }
+ if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
+ $fetchmode = $this->db->fetchmode;
+ }
+ if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
+ //$row = @sqlite_fetch_array($this->result, SQLITE_ASSOC);
+ $row=$this->result->fetchArray(SQLITE3_ASSOC);
+ if (is_array($row)
+ && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
+ ) {
+ $row = array_change_key_case($row, $this->db->options['field_case']);
+ }
+ } else {
+ $row=$this->result->fetchArray(SQLITE3_NUM);
+ }
+ if (!$row) {
+ if ($this->result === false) {
+ $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+ 'resultset has already been freed', __FUNCTION__);
+ return $err;
+ }
+ $null = null;
+ return $null;
+ }
+ $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
+ $rtrim = false;
+ if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
+ if (empty($this->types)) {
+ $mode += MDB2_PORTABILITY_RTRIM;
+ } else {
+ $rtrim = true;
+ }
+ }
+ if ($mode) {
+ $this->db->_fixResultArrayValues($row, $mode);
+ }
+ if (!empty($this->types)) {
+ $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
+ }
+ if (!empty($this->values)) {
+ $this->_assignBindColumns($row);
+ }
+ if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
+ $object_class = $this->db->options['fetch_class'];
+ if ($object_class == 'stdClass') {
+ $row = (object) $row;
+ } else {
+ $row = new $object_class($row);
+ }
+ }
+ ++$this->rownum;
+ return $row;
+ }
+
+ // }}}
+ // {{{ _getColumnNames()
+
+ /**
+ * Retrieve the names of columns returned by the DBMS in a query result.
+ *
+ * @return mixed Array variable that holds the names of columns as keys
+ * or an MDB2 error on failure.
+ * Some DBMS may not return any columns when the result set
+ * does not contain any rows.
+ * @access private
+ */
+ function _getColumnNames()
+ {
+ $columns = array();
+ $numcols = $this->numCols();
+ if (PEAR::isError($numcols)) {
+ return $numcols;
+ }
+ for ($column = 0; $column < $numcols; $column++) {
+ $column_name = $this->result->getColumnName($column);
+ $columns[$column_name] = $column;
+ }
+ if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+ $columns = array_change_key_case($columns, $this->db->options['field_case']);
+ }
+ return $columns;
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Count the number of columns returned by the DBMS in a query result.
+ *
+ * @access public
+ * @return mixed integer value with the number of columns, a MDB2 error
+ * on failure
+ */
+ function numCols()
+ {
+ $this->result->numColumns();
+ }
+}
+
+/**
+ * MDB2 SQLite buffered result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_BufferedResult_sqlite3 extends MDB2_Result_sqlite3
+{
+ // {{{ seek()
+
+ /**
+ * Seek to a specific row in a result set
+ *
+ * @param int $rownum number of the row where the data can be found
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function seek($rownum = 0)
+ {
+ $this->result->reset();
+ for($i=0;$i<$rownum;$i++){
+ $this->result->fetchArray();
+ }
+ $this->rownum = $rownum - 1;
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ valid()
+
+ /**
+ * Check if the end of the result set has been reached
+ *
+ * @return mixed true or false on sucess, a MDB2 error on failure
+ * @access public
+ */
+ function valid()
+ {
+ $numrows = $this->numRows();
+ if (PEAR::isError($numrows)) {
+ return $numrows;
+ }
+ return $this->rownum < ($numrows - 1);
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Returns the number of rows in a result object
+ *
+ * @return mixed MDB2 Error Object or the number of rows
+ * @access public
+ */
+ function numRows()
+ {
+ $rows = 0;
+ $this->result->reset();
+ while($this->result->fetchArray()){
+ $rows++;
+ }
+ $this->result->reset();
+ return $rows;
+ }
+}
+
+/**
+ * MDB2 SQLite statement driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Statement_sqlite3 extends MDB2_Statement_Common
+{
+ // }}}
+ // {{{ function bindValue($parameter, &$value, $type = null)
+
+ private function getParamType($type){
+ switch(strtolower($type)){
+ case 'text':
+ return SQLITE3_TEXT;
+ case 'boolean':
+ case 'integer':
+ return SQLITE3_INTEGER;
+ case 'float':
+ return SQLITE3_FLOAT;
+ case 'blob':
+ return SQLITE3_BLOB;
+ }
+ }
+ /**
+ * Set the value of a parameter of a prepared query.
+ *
+ * @param int the order number of the parameter in the query
+ * statement. The order number of the first parameter is 1.
+ * @param mixed value that is meant to be assigned to specified
+ * parameter. The type of the value depends on the $type argument.
+ * @param string specifies the type of the field
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ */
+ function bindValue($parameter, $value, $type = null){
+ if($type){
+ $type=$this->getParamType($type);
+ $this->statement->bindValue($parameter,$value,$type);
+ }else{
+ $this->statement->bindValue($parameter,$value);
+ }
+ return MDB2_OK;
+ }
+
+ /**
+ * Bind a variable to a parameter of a prepared query.
+ *
+ * @param int the order number of the parameter in the query
+ * statement. The order number of the first parameter is 1.
+ * @param mixed variable that is meant to be bound to specified
+ * parameter. The type of the value depends on the $type argument.
+ * @param string specifies the type of the field
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ */
+ function bindParam($parameter, &$value, $type = null){
+ if($type){
+ $type=$this->getParamType($type);
+ $this->statement->bindParam($parameter,$value,$type);
+ }else{
+ $this->statement->bindParam($parameter,$value);
+ }
+ return MDB2_OK;
+ }
+
+ /**
+ * Release resources allocated for the specified prepared query.
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ * @access public
+ */
+ function free()
+ {
+ $this->statement->close();
+ }
+
+ /**
+ * Execute a prepared query statement helper method.
+ *
+ * @param mixed $result_class string which specifies which result class to use
+ * @param mixed $result_wrap_class string which specifies which class to wrap results in
+ *
+ * @return mixed MDB2_Result or integer (affected rows) on success,
+ * a MDB2 error on failure
+ * @access private
+ */
+ function &_execute($result_class = true, $result_wrap_class = false){
+ if (is_null($this->statement)) {
+ $result =& parent::_execute($result_class, $result_wrap_class);
+ return $result;
+ }
+ $this->db->last_query = $this->query;
+ $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values));
+ if ($this->db->getOption('disable_query')) {
+ $result = $this->is_manip ? 0 : null;
+ return $result;
+ }
+
+ $connection = $this->db->getConnection();
+ if (PEAR::isError($connection)) {
+ return $connection;
+ }
+
+ $result = $this->statement->execute();
+ if ($result==false) {
+ $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+ 'cant execute statement', __FUNCTION__);
+ }
+
+ if ($this->is_manip) {
+ $affected_rows = $this->db->_affectedRows($connection, $result);
+ return $affected_rows;
+ }
+
+ $result =& $this->db->_wrapResult($result, $this->result_types,
+ $result_class, $result_wrap_class, $this->limit, $this->offset);
+ $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
+ return $result;
+ }
+
+ /**
+ * Set the values of multiple a parameter of a prepared query in bulk.
+ *
+ * @param array specifies all necessary information
+ * for bindValue() the array elements must use keys corresponding to
+ * the number of the position of the parameter.
+ * @param array specifies the types of the fields
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ * @see bindParam()
+ */
+ function bindValueArray($values, $types = null)
+ {
+ $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
+ $parameters = array_keys($values);
+ foreach ($parameters as $key => $parameter) {
+ $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
+ $this->db->expectError(MDB2_ERROR_NOT_FOUND);
+ $err = $this->bindValue($parameter+1, $values[$parameter], $types[$key]);
+ $this->db->popExpect();
+ $this->db->popErrorHandling();
+ if (PEAR::isError($err)) {
+ if ($err->getCode() == MDB2_ERROR_NOT_FOUND) {
+ //ignore (extra value for missing placeholder)
+ continue;
+ }
+ return $err;
+ }
+ }
+ return MDB2_OK;
+ }
+ // }}}
+ // {{{ function bindParamArray(&$values, $types = null)
+
+ /**
+ * Bind the variables of multiple a parameter of a prepared query in bulk.
+ *
+ * @param array specifies all necessary information
+ * for bindParam() the array elements must use keys corresponding to
+ * the number of the position of the parameter.
+ * @param array specifies the types of the fields
+ *
+ * @return mixed MDB2_OK on success, a MDB2 error on failure
+ *
+ * @access public
+ * @see bindParam()
+ */
+ function bindParamArray(&$values, $types = null)
+ {
+ $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
+ $parameters = array_keys($values);
+ foreach ($parameters as $key => $parameter) {
+ $err = $this->bindParam($parameter+1, $values[$parameter], $types[$key]);
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ return MDB2_OK;
+ }
+
+ // }}}
+ // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false)
+
+ /**
+ * Execute a prepared query statement.
+ *
+ * @param array specifies all necessary information
+ * for bindParam() the array elements must use keys corresponding
+ * to the number of the position of the parameter.
+ * @param mixed specifies which result class to use
+ * @param mixed specifies which class to wrap results in
+ *
+ * @return mixed MDB2_Result or integer (affected rows) on success,
+ * a MDB2 error on failure
+ * @access public
+ */
+ function &execute($values = null, $result_class = true, $result_wrap_class = false)
+ {
+ if (is_null($this->positions)) {
+ return $this->db->raiseError(MDB2_ERROR, null, null,
+ 'Prepared statement has already been freed', __FUNCTION__);
+ }
+ $values = (array)$values;
+ if (!empty($values)) {
+ if(count($this->types)){
+ $types=$this->types;
+ }else{
+ $types=null;
+ }
+ $err = $this->bindValueArray($values,$types);
+ if (PEAR::isError($err)) {
+ return $this->db->raiseError(MDB2_ERROR, null, null,
+ 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__);
+ }
+ }
+ $result =$this->_execute($result_class, $result_wrap_class);
+ return $result;
+ }
+
+ function __destruct() {
+ $this->free();
+ }
+}
+
+?> \ No newline at end of file
diff --git a/lib/User/backend.php b/lib/User/backend.php
index e99016a5214..1797d0c475a 100644
--- a/lib/User/backend.php
+++ b/lib/User/backend.php
@@ -4,7 +4,9 @@
* ownCloud
*
* @author Frank Karlitschek
+ * @author Dominik Schmidt
* @copyright 2010 Frank Karlitschek karlitschek@kde.org
+ * @copyright 2011 Dominik Schmidt dev@dominik-schmidt.de
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
@@ -21,92 +23,64 @@
*
*/
+/**
+ * error code for functions not provided by the user backend
+ */
+define('OC_USER_BACKEND_NOT_IMPLEMENTED', -501);
+
+/**
+ * actions that user backends can define
+ */
+define('OC_USER_BACKEND_CREATE_USER', 0x000001);
+define('OC_USER_BACKEND_DELETE_USER', 0x000010);
+define('OC_USER_BACKEND_SET_PASSWORD', 0x000100);
+define('OC_USER_BACKEND_CHECK_PASSWORD', 0x001000);
+define('OC_USER_BACKEND_GET_USERS', 0x010000);
+define('OC_USER_BACKEND_USER_EXISTS', 0x100000);
/**
* abstract base class for user management
+ * subclass this for your own backends and see OC_USER_EXAMPLE for descriptions
*/
abstract class OC_USER_BACKEND {
- /**
- * @brief Create a new user
- * @param $uid The username of the user to create
- * @param $password The password of the new user
- * @returns true/false
- *
- * Creates a new user. Basic checking of username is done in OC_USER
- * itself, not in its subclasses.
- */
- public static function createUser($uid, $password){}
-
- /**
- * @brief delete a user
- * @param $uid The username of the user to delete
- * @returns true/false
- *
- * Deletes a user
- */
- public static function deleteUser( $uid ){}
+ protected $possibleActions = array(
+ OC_USER_BACKEND_CREATE_USER => 'createUser',
+ OC_USER_BACKEND_DELETE_USER => 'deleteUser',
+ OC_USER_BACKEND_SET_PASSWORD => 'setPassword',
+ OC_USER_BACKEND_CHECK_PASSWORD => 'checkPassword',
+ OC_USER_BACKEND_GET_USERS => 'getUsers',
+ OC_USER_BACKEND_USER_EXISTS => 'userExists'
+ );
/**
- * @brief Try to login a user
- * @param $uid The username of the user to log in
- * @param $password The password of the user
- * @returns true/false
- *
- * Log in a user - if the password is ok
- */
- public static function login($uid, $password){}
+ * @brief Get all supported actions
+ * @returns bitwise-or'ed actions
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function getSupportedActions(){
+ $actions = 0;
+ foreach($this->possibleActions AS $action => $methodName){
+ if(method_exists($this, $methodName)) {
+ $actions |= $action;
+ }
+ }
- /**
- * @brief Kick the user
- * @returns true
- *
- * Logout, destroys session
- */
- public static function logout(){}
-
- /**
- * @brief Check if the user is logged in
- * @returns true/false
- *
- * Checks if the user is logged in
- */
- public static function isLoggedIn(){}
-
- /**
- * @brief Autogenerate a password
- * @returns string
- *
- * generates a password
- */
- public static function generatePassword(){}
-
- /**
- * @brief Set password
- * @param $uid The username
- * @param $password The new password
- * @returns true/false
- *
- * Change the password of a user
- */
- public static function setPassword($uid, $password){}
-
- /**
- * @brief Check if the password is correct
- * @param $uid The username
- * @param $password The password
- * @returns true/false
- *
- * Check if the password is correct without logging in the user
- */
- public static function checkPassword($uid, $password){}
+ return $actions;
+ }
/**
- * @brief Get a list of all users
- * @returns array with all uids
- *
- * Get a list of all users.
- */
- public static function getUsers(){}
+ * @brief Check if backend implements actions
+ * @param $actions bitwise-or'ed actions
+ * @returns boolean
+ *
+ * Returns the supported actions as int to be
+ * compared with OC_USER_BACKEND_CREATE_USER etc.
+ */
+ public function implementsActions($actions){
+ return (bool)($this->getSupportedActions() & $actions);
+ }
}
diff --git a/lib/User/database.php b/lib/User/database.php
index eeabb592c22..0396ac30958 100644
--- a/lib/User/database.php
+++ b/lib/User/database.php
@@ -50,12 +50,8 @@ class OC_USER_DATABASE extends OC_USER_BACKEND {
* Creates a new user. Basic checking of username is done in OC_USER
* itself, not in its subclasses.
*/
- public static function createUser( $uid, $password ){
- // Check if the user already exists
- $query = OC_DB::prepare( "SELECT * FROM `*PREFIX*users` WHERE uid = ?" );
- $result = $query->execute( array( $uid ));
-
- if ( $result->numRows() > 0 ){
+ public function createUser( $uid, $password ){
+ if( $this->userExists($uid) ){
return false;
}
else{
@@ -73,7 +69,7 @@ class OC_USER_DATABASE extends OC_USER_BACKEND {
*
* Deletes a user
*/
- public static function deleteUser( $uid ){
+ public function deleteUser( $uid ){
// Delete user-group-relation
$query = OC_DB::prepare( "DELETE FROM `*PREFIX*users` WHERE uid = ?" );
$result = $query->execute( array( $uid ));
@@ -81,69 +77,6 @@ class OC_USER_DATABASE extends OC_USER_BACKEND {
}
/**
- * @brief Try to login a user
- * @param $uid The username of the user to log in
- * @param $password The password of the user
- * @returns true/false
- *
- * Log in a user - if the password is ok
- */
- public static function login( $uid, $password ){
- // Query
- $query = OC_DB::prepare( "SELECT uid FROM *PREFIX*users WHERE uid = ? AND password = ?" );
- $result = $query->execute( array( $uid, sha1( $password )));
-
- if( $result->numRows() > 0 ){
- // Set username if name and password are known
- $row = $result->fetchRow();
- $_SESSION['user_id'] = $row["uid"];
- OC_LOG::add( "core", $_SESSION['user_id'], "login" );
- return true;
- }
- else{
- return false;
- }
- }
-
- /**
- * @brief Kick the user
- * @returns true
- *
- * Logout, destroys session
- */
- public static function logout(){
- OC_LOG::add( "core", $_SESSION['user_id'], "logout" );
- $_SESSION['user_id'] = false;
-
- return true;
- }
-
- /**
- * @brief Check if the user is logged in
- * @returns true/false
- *
- * Checks if the user is logged in
- */
- public static function isLoggedIn() {
- if( isset($_SESSION['user_id']) AND $_SESSION['user_id'] ){
- return true;
- }
- else{
- return false;
- }
- }
-
- /**
- * @brief Autogenerate a password
- * @returns string
- *
- * generates a password
- */
- public static function generatePassword(){
- return uniqId();
- }
-
- /**
* @brief Set password
* @param $uid The username
* @param $password The new password
@@ -151,12 +84,8 @@ class OC_USER_DATABASE extends OC_USER_BACKEND {
*
* Change the password of a user
*/
- public static function setPassword( $uid, $password ){
- // Check if the user already exists
- $query = OC_DB::prepare( "SELECT * FROM `*PREFIX*users` WHERE uid = ?" );
- $result = $query->execute( array( $uid ));
-
- if( $result->numRows() > 0 ){
+ public function setPassword( $uid, $password ){
+ if( $this->userExists($uid) ){
$query = OC_DB::prepare( "UPDATE *PREFIX*users SET password = ? WHERE uid = ?" );
$result = $query->execute( array( sha1( $password ), $uid ));
@@ -175,7 +104,7 @@ class OC_USER_DATABASE extends OC_USER_BACKEND {
*
* Check if the password is correct without logging in the user
*/
- public static function checkPassword( $uid, $password ){
+ public function checkPassword( $uid, $password ){
$query = OC_DB::prepare( "SELECT uid FROM *PREFIX*users WHERE uid = ? AND password = ?" );
$result = $query->execute( array( $uid, sha1( $password )));
@@ -193,7 +122,7 @@ class OC_USER_DATABASE extends OC_USER_BACKEND {
*
* Get a list of all users.
*/
- public static function getUsers(){
+ public function getUsers(){
$query = OC_DB::prepare( "SELECT uid FROM *PREFIX*users" );
$result = $query->execute();
@@ -203,4 +132,16 @@ class OC_USER_DATABASE extends OC_USER_BACKEND {
}
return $users;
}
+
+ /**
+ * @brief check if a user exists
+ * @param string $uid the username
+ * @return boolean
+ */
+ public function userExists($uid){
+ $query = OC_DB::prepare( "SELECT * FROM `*PREFIX*users` WHERE uid = ?" );
+ $result = $query->execute( array( $uid ));
+
+ return $result->numRows() > 0;
+ }
}
diff --git a/lib/User/example.php b/lib/User/example.php
new file mode 100644
index 00000000000..4abc1b3d49c
--- /dev/null
+++ b/lib/User/example.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @copyright 2010 Frank Karlitschek karlitschek@kde.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+require_once('User/backend.php');
+
+/**
+ * abstract reference class for user management
+ * this class should only be used as a reference for method signatures and their descriptions
+ */
+abstract class OC_USER_EXAMPLE extends OC_USER_BACKEND {
+ /**
+ * @brief Create a new user
+ * @param $uid The username of the user to create
+ * @param $password The password of the new user
+ * @returns true/false
+ *
+ * Creates a new user. Basic checking of username is done in OC_USER
+ * itself, not in its subclasses.
+ */
+ public function createUser($uid, $password){
+ return OC_USER_BACKEND_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * @brief delete a user
+ * @param $uid The username of the user to delete
+ * @returns true/false
+ *
+ * Deletes a user
+ */
+ public function deleteUser( $uid ){
+ return OC_USER_BACKEND_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * @brief Set password
+ * @param $uid The username
+ * @param $password The new password
+ * @returns true/false
+ *
+ * Change the password of a user
+ */
+ public function setPassword($uid, $password){
+ return OC_USER_BACKEND_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * @brief Check if the password is correct
+ * @param $uid The username
+ * @param $password The password
+ * @returns true/false
+ *
+ * Check if the password is correct without logging in the user
+ */
+ public function checkPassword($uid, $password){
+ return OC_USER_BACKEND_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * @brief Get a list of all users
+ * @returns array with all uids
+ *
+ * Get a list of all users.
+ */
+ public function getUsers(){
+ return OC_USER_BACKEND_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * @brief check if a user exists
+ * @param string $uid the username
+ * @return boolean
+ */
+ public function userExists($uid){
+ return OC_USER_BACKEND_NOT_IMPLEMENTED;
+ }
+}
diff --git a/lib/app.php b/lib/app.php
index a56e0c2f9e6..64704f09bbd 100644
--- a/lib/app.php
+++ b/lib/app.php
@@ -29,7 +29,7 @@
class OC_APP{
static private $init = false;
static private $apps = array();
- static private $activeapp = "";
+ static private $activeapp = '';
static private $adminpages = array();
static private $settingspages = array();
static private $navigation = array();
@@ -52,33 +52,19 @@ class OC_APP{
}
// Our very own core apps are hardcoded
- foreach( array( "admin", "files", "log", "help", "settings" ) as $app ){
- require( "$app/appinfo/app.php" );
+ foreach( array( 'admin', 'files', 'log', 'help', 'settings' ) as $app ){
+ require( $app.'/appinfo/app.php' );
}
// The rest comes here
- $dir = opendir( "$SERVERROOT/apps" );
- while( false !== ( $filename = readdir( $dir ))){
- if( substr( $filename, 0, 1 ) != '.' ){
- if( file_exists( "$SERVERROOT/apps/$filename/appinfo/app.php" )){
- if(OC_APPCONFIG::getValue($filename,'installed_version',0)==0){ //check if the plugin is fully installed
- //install the database
- if(is_file("$SERVERROOT/apps/$filename/appinfo/database.xml")){
- OC_DB::createDbFromStructure("$SERVERROOT/apps/$filename/appinfo/database.xml");
- }
-
- //run appinfo/install.php
- if(is_file("$SERVERROOT/apps/$filename/appinfo/install.php")){
- include("$SERVERROOT/apps/$filename/appinfo/install.php");
- }
- $info=self::getAppInfo("$SERVERROOT/apps/$filename/appinfo/info.xml");
- OC_APPCONFIG::setValue($filename,'installed_version',$info['version']);
- }
- require( "apps/$filename/appinfo/app.php" );
+ $apps = OC_APPCONFIG::getApps();
+ foreach( $apps as $app ){
+ if( self::isEnabled( $app )){
+ if(is_file($SERVERROOT.'/apps/'.$app.'/appinfo/app.php')){
+ require( 'apps/'.$app.'/appinfo/app.php' );
}
}
}
- closedir( $dir );
self::$init = true;
@@ -87,14 +73,51 @@ class OC_APP{
}
/**
+ * @brief checks whether or not an app is enabled
+ * @param $app app
+ * @returns true/false
+ *
+ * This function checks whether or not an app is enabled.
+ */
+ public static function isEnabled( $app ){
+ if( 'yes' == OC_APPCONFIG::getValue( $app, 'enabled' )){
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @brief enables an app
+ * @param $app app
+ * @returns true/false
+ *
+ * This function set an app as enabled in appconfig.
+ */
+ public static function enable( $app ){
+ OC_APPCONFIG::setValue( $app, 'enabled', 'yes' );
+ }
+
+ /**
+ * @brief enables an app
+ * @param $app app
+ * @returns true/false
+ *
+ * This function set an app as enabled in appconfig.
+ */
+ public static function disable( $app ){
+ OC_APPCONFIG::setValue( $app, 'enabled', 'no' );
+ }
+
+ /**
* @brief makes owncloud aware of this app
* @param $data array with all information
* @returns true/false
*
* This function registers the application. $data is an associative array.
* The following keys are required:
- * - id: id of the application, has to be unique ("addressbook")
- * - name: Human readable name ("Addressbook")
+ * - id: id of the application, has to be unique ('addressbook')
+ * - name: Human readable name ('Addressbook')
* - version: array with Version (major, minor, bugfix) ( array(1, 0, 2))
*
* The following keys are optional:
@@ -125,9 +148,9 @@ class OC_APP{
* This function adds a new entry to the navigation visible to users. $data
* is an associative array.
* The following keys are required:
- * - id: unique id for this entry ("addressbook_index")
+ * - id: unique id for this entry ('addressbook_index')
* - href: link to the page
- * - name: Human readable name ("Addressbook")
+ * - name: Human readable name ('Addressbook')
*
* The following keys are optional:
* - icon: path to the icon of the app
@@ -151,9 +174,9 @@ class OC_APP{
* as being active (see activateNavigationEntry()). $data is an associative
* array.
* The following keys are required:
- * - id: unique id for this entry ("addressbook_index")
+ * - id: unique id for this entry ('addressbook_index')
* - href: link to the page
- * - name: Human readable name ("Addressbook")
+ * - name: Human readable name ('Addressbook')
*
* The following keys are optional:
* - icon: path to the icon of the app
@@ -173,7 +196,7 @@ class OC_APP{
* @param $id id of the entry
* @returns true/false
*
- * This function sets a navigation entry as active and removes the "active"
+ * This function sets a navigation entry as active and removes the 'active'
* property from all other entries. The templates can use this for
* highlighting the current position of the user.
*/
@@ -201,9 +224,9 @@ class OC_APP{
* This function registers a admin page that will be shown in the admin
* menu. $data is an associative array.
* The following keys are required:
- * - id: unique id for this entry ("files_admin")
+ * - id: unique id for this entry ('files_admin')
* - href: link to the admin page
- * - name: Human readable name ("Files Administration")
+ * - name: Human readable name ('Files Administration')
*
* The following keys are optional:
* - order: integer, that influences the position of your application in
@@ -222,16 +245,16 @@ class OC_APP{
*
* This function registers a settings page. $data is an associative array.
* The following keys are required:
- * - app: app the settings belong to ("files")
- * - id: unique id for this entry ("files_public")
+ * - app: app the settings belong to ('files')
+ * - id: unique id for this entry ('files_public')
* - href: link to the admin page
- * - name: Human readable name ("Public files")
+ * - name: Human readable name ('Public files')
*
* The following keys are optional:
* - order: integer, that influences the position of your application in
* the list. Lower values come first.
*
- * For the main settings page of an app, the keys "app" and "id" have to be
+ * For the main settings page of an app, the keys 'app' and 'id' have to be
* the same.
*/
public static function addSettingsPage( $data = array()){
@@ -245,11 +268,11 @@ class OC_APP{
* @returns associative array
*
* This function returns an array containing all entries added. The
- * entries are sorted by the key "order" ascending. Additional to the keys
+ * entries are sorted by the key 'order' ascending. Additional to the keys
* given for each app the following keys exist:
* - active: boolean, signals if the user is on this navigation entry
- * - children: array that is empty if the key "active" is false or
- * contains the subentries if the key "active" is true
+ * - children: array that is empty if the key 'active' is false or
+ * contains the subentries if the key 'active' is true
*/
public static function getNavigation(){
$navigation = self::proceedNavigation( self::$navigation );
@@ -262,7 +285,7 @@ class OC_APP{
* @returns associative array
*
* This function returns an array containing all settings pages added. The
- * entries are sorted by the key "order" ascending.
+ * entries are sorted by the key 'order' ascending.
*/
public static function getSettingsNavigation(){
$navigation = self::proceedNavigation( self::$settingspages );
@@ -276,7 +299,7 @@ class OC_APP{
* @returns associative array
*
* This function returns an array containing all admin pages added. The
- * entries are sorted by the key "order" ascending.
+ * entries are sorted by the key 'order' ascending.
*/
public static function getAdminNavigation(){
$navigation = self::proceedNavigation( self::$adminpages );
@@ -290,38 +313,47 @@ class OC_APP{
$found = false;
foreach( self::$subnavigation as $parent => $selection ){
foreach( $selection as $subentry ){
- if( $subentry["id"] == self::$activeapp ){
+ if( $subentry['id'] == self::$activeapp ){
foreach( $list as &$naventry ){
- if( $naventry["id"] == $parent ){
- $naventry["active"] = true;
- $naventry["subnavigation"] = $selection;
+ if( $naventry['id'] == $parent ){
+ $naventry['active'] = true;
+ $naventry['subnavigation'] = $selection;
}
else{
- $naventry["active"] = false;
+ $naventry['active'] = false;
}
- }
+ } unset( $naventry );
$found = true;
}
}
}
+ // Mark subentry as active
+ foreach( $list as &$naventry ){
+ if( $naventry['active'] ){
+ foreach( $naventry['subnavigation'] as &$subnaventry ){
+ $subnaventry['active'] = $subnaventry['id'] == self::$activeapp? true : false;
+ } unset( $subnaventry );
+ }
+ } unset( $naventry );
+
return $list;
}
/// This is private as well. It simply works, so don't ask for more details
private static function proceedNavigation( $list ){
foreach( $list as &$naventry ){
- $naventry["subnavigation"] = array();
- if( $naventry["id"] == self::$activeapp ){
- $naventry["active"] = true;
- if( array_key_exists( $naventry["id"], self::$subnavigation )){
- $naventry["subnavigation"] = self::$subnavigation[$naventry["id"]];
+ $naventry['subnavigation'] = array();
+ if( $naventry['id'] == self::$activeapp ){
+ $naventry['active'] = true;
+ if( array_key_exists( $naventry['id'], self::$subnavigation )){
+ $naventry['subnavigation'] = self::$subnavigation[$naventry['id']];
}
}
else{
- $naventry["active"] = false;
+ $naventry['active'] = false;
}
- }
+ } unset( $naventry );
usort( $list, create_function( '$a, $b', 'if( $a["order"] == $b["order"] ){return 0;}elseif( $a["order"] < $b["order"] ){return -1;}else{return 1;}' ));
diff --git a/lib/base.php b/lib/base.php
index e5b6656c2a9..e821d78fd8c 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -35,7 +35,11 @@ $SERVERROOT=substr(__FILE__,0,-13);
$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']);
$SERVERROOT=str_replace("\\",'/',$SERVERROOT);
$SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT));
-$WEBROOT=substr($_SERVER["SCRIPT_NAME"],0,strlen($_SERVER["SCRIPT_NAME"])-strlen($SUBURI));
+$scriptName=$_SERVER["SCRIPT_NAME"];
+if(substr($scriptName,-1)=='/'){//if the script isn't a file assume index.php
+ $scriptName.='index.php';
+}
+$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen($SUBURI));
@@ -80,7 +84,12 @@ require_once('appconfig.php');
require_once('files.php');
require_once('filesystem.php');
require_once('filestorage.php');
+<<<<<<< HEAD
require_once('apps/files_sharing/sharedstorage.php');
+=======
+require_once('l10n.php');
+require_once('preferences.php');
+>>>>>>> master
require_once('log.php');
require_once('user.php');
require_once('group.php');
@@ -92,7 +101,7 @@ require_once('search.php');
$error=(count(OC_UTIL::checkServer())>0);
-OC_USER::setBackend( OC_CONFIG::getValue( "userbackend", "database" ));
+OC_USER::useBackend( OC_CONFIG::getValue( "userbackend", "database" ));
OC_GROUP::setBackend( OC_CONFIG::getValue( "groupbackend", "database" ));
// Set up file system unless forbidden
@@ -119,6 +128,7 @@ if(!$error and !$RUNTIME_NOAPPS ){
class OC_UTIL {
public static $scripts=array();
public static $styles=array();
+ public static $headers=array();
private static $fsSetup=false;
// Can be set up
@@ -141,12 +151,12 @@ class OC_UTIL {
// If we are not forced to load a specific user we load the one that is logged in
if( $user == "" && OC_USER::isLoggedIn()){
- $user = $_SESSION['user_id'];
+ $user = OC_USER::getUser();
}
if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem
//first set up the local "root" storage and the backupstorage if needed
- $rootStorage=OC_FILESYSTEM::createStorage('local',array('datadir'=>$CONFIG_DATADIRECTORY));
+ $rootStorage=OC_FILESYSTEM::createStorage('local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT));
// if( OC_CONFIG::getValue( "enablebackup", false )){
// // This creates the Directorys recursively
// if(!is_dir( "$CONFIG_BACKUPDIRECTORY/$user/$root" )){
@@ -187,6 +197,11 @@ class OC_UTIL {
}
}
+ public static function tearDownFS(){
+ OC_FILESYSTEM::tearDown();
+ self::$fsSetup=false;
+ }
+
/**
* get the current installed version of ownCloud
* @return array
@@ -229,6 +244,16 @@ class OC_UTIL {
}
}
+ /**
+ * @brief Add a custom element to the header
+ * @param string tag tag name of the element
+ * @param array $attributes array of attrobutes for the element
+ * @param string $text the text content for the element
+ */
+ public static function addHeader( $tag, $attributes, $text=''){
+ self::$headers[]=array('tag'=>$tag,'attributes'=>$attributes,'text'=>$text);
+ }
+
/**
* formats a timestamp in the "right" way
*
diff --git a/lib/config.php b/lib/config.php
index 6af9fcbe5ad..cd18ddd499c 100644
--- a/lib/config.php
+++ b/lib/config.php
@@ -139,7 +139,7 @@ class OC_CONFIG{
// Include the file, save the data from $CONFIG
include( "$SERVERROOT/config/config.php" );
- if( isset( $CONFIG ) || is_array( $CONFIG )){
+ if( isset( $CONFIG ) && is_array( $CONFIG )){
self::$cache = $CONFIG;
}
@@ -169,9 +169,6 @@ class OC_CONFIG{
$value = $value ? 'true' : 'false';
$content .= "\"$key\" => $value,\n";
}
- elseif( is_numeric( $value )){
- $content .= "\"$key\" => $value,\n";
- }
else{
$value = str_replace( "'", "\\'", $value );
$content .= "\"$key\" => '$value',\n";
diff --git a/lib/database.php b/lib/database.php
index 728e7359040..8d7c76756c1 100644
--- a/lib/database.php
+++ b/lib/database.php
@@ -60,10 +60,10 @@ class OC_DB {
'quote_identifier' => true );
// Add the dsn according to the database type
- if( $CONFIG_DBTYPE == 'sqlite' ){
+ if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){
// sqlite
$dsn = array(
- 'phptype' => 'sqlite',
+ 'phptype' => $CONFIG_DBTYPE,
'database' => "$datadir/$CONFIG_DBNAME.db",
'mode' => '0644' );
}
@@ -100,6 +100,9 @@ class OC_DB {
// We always, really always want associative arrays
self::$DBConnection->setFetchMode(MDB2_FETCHMODE_ASSOC);
+
+ //we need to function module for query pre-procesing
+ self::$DBConnection->loadModule('Function');
}
// we are done. great!
@@ -297,15 +300,14 @@ class OC_DB {
* and replaces the ` woth ' or " according to the database driver.
*/
private static function processQuery( $query ){
+ self::connect();
// We need Database type and table prefix
$CONFIG_DBTYPE = OC_CONFIG::getValue( "dbtype", "sqlite" );
$CONFIG_DBTABLEPREFIX = OC_CONFIG::getValue( "dbtableprefix", "oc_" );
// differences is getting the current timestamp
- if( $CONFIG_DBTYPE == 'sqlite' ){
- $query = str_replace( 'NOW()', "strftime('%s', 'now')", $query );
- $query = str_replace( 'now()', "strftime('%s', 'now')", $query );
- }
+ $query = str_replace( 'NOW()', self::$DBConnection->now(), $query );
+ $query = str_replace( 'now()', self::$DBConnection->now(), $query );
// differences in escaping of table names (` for mysql)
// Problem: what if there is a ` in the value we want to insert?
@@ -321,5 +323,43 @@ class OC_DB {
return $query;
}
+
+ /**
+ * @brief drop a table
+ * @param string $tableNamme the table to drop
+ */
+ public static function dropTable($tableName){
+ self::connect();
+ self::$DBConnection->loadModule('Manager');
+ self::$DBConnection->dropTable($tableName);
+ }
+
+ /**
+ * remove all tables defined in a database structure xml file
+ * @param string $file the xml file describing the tables
+ */
+ public static function removeDBStructure($file){
+ $CONFIG_DBNAME = OC_CONFIG::getValue( "dbname", "owncloud" );
+ $CONFIG_DBTABLEPREFIX = OC_CONFIG::getValue( "dbtableprefix", "oc_" );
+ self::connectScheme();
+
+ // read file
+ $content = file_get_contents( $file );
+
+ // Make changes and save them to a temporary file
+ $file2 = tempnam( sys_get_temp_dir(), 'oc_db_scheme_' );
+ $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content );
+ $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
+ file_put_contents( $file2, $content );
+
+ // get the tables
+ $definition = self::$schema->parseDatabaseDefinitionFile( $file2 );
+
+ // Delete our temporary file
+ unlink( $file2 );
+ foreach($definition['tables'] as $name=>$table){
+ self::dropTable($name);
+ }
+ }
}
?> \ No newline at end of file
diff --git a/lib/files.php b/lib/files.php
index 9e66f9dab82..d8133667954 100644
--- a/lib/files.php
+++ b/lib/files.php
@@ -30,15 +30,6 @@ require_once("log.php");
*/
class OC_FILES {
static $tmpFiles=array();
- /**
- * show a web GUI filebrowser
- *
- * @param basedir $basedir
- * @param dir $dir
- */
- public static function showBrowser($basedir,$dir){
- echo '<div id="content"></div>';
- }
/**
* get the content of a directory
@@ -101,7 +92,7 @@ class OC_FILES {
if(strpos($files,';')){
$files=explode(';',$files);
}
- echo 't';
+
if(is_array($files)){
$zip = new ZipArchive();
$filename = sys_get_temp_dir()."/ownCloud.zip";
@@ -300,13 +291,13 @@ class OC_FILES {
global $SERVERROOT;
global $WEBROOT;
$size=OC_HELPER::humanFileSize($size);
- echo $size;
$size=substr($size,0,-1);//strip the B
$size=str_replace(' ','',$size); //remove the space between the size and the postfix
- $content = "ErrorDocument 404 /$WEBROOT/templates/404.php\n";//custom 404 error page
+ $content = "ErrorDocument 404 /$WEBROOT/core/templates/404.php\n";//custom 404 error page
$content.= "php_value upload_max_filesize $size\n";//upload limit
$content.= "php_value post_max_size $size\n";
$content.= "SetEnv htaccessWorking true\n";
+ $content.= "Options -Indexes\n";
@file_put_contents($SERVERROOT.'/.htaccess', $content); //supress errors in case we don't have permissions for it
}
}
diff --git a/lib/filestorage.php b/lib/filestorage.php
index 157e44ff298..819ad2e60b3 100644
--- a/lib/filestorage.php
+++ b/lib/filestorage.php
@@ -377,7 +377,7 @@ class OC_FILESTORAGE_LOCAL extends OC_FILESTORAGE{
if ($item == '.' || $item == '..') continue;
if(is_file($dir.'/'.$item)){
if(unlink($dir.'/'.$item)){
- $this->clearFolderSizeCache($path);
+ $this->clearFolderSizeCache($dir);
}
}elseif(is_dir($dir.'/'.$item)){
if (!$this->delTree($dirRelative. "/" . $item)){
diff --git a/lib/filesystem.php b/lib/filesystem.php
index a13363f832d..4116cb93164 100644
--- a/lib/filesystem.php
+++ b/lib/filesystem.php
@@ -29,10 +29,18 @@
*
* Hooks provided:
* read(path)
- * write(path)
- * create(path) (when a file is created both, write and create will be emited)
- * delete(path)
- * rename(oldpath,newpath)
+ * write(path, &run)
+ * post_write(path)
+ * create(path, &run) (when a file is created, both create and write will be emited in that order)
+ * post_create(path)
+ * delete(path, &run)
+ * post_delete(path)
+ * rename(oldpath,newpath, &run)
+ * post_rename(oldpath,newpath)
+ * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order)
+ * post_rename(oldpath,newpath)
+ *
+ * the &run parameter can be set to false to prevent the operation from occuring
*/
class OC_FILESYSTEM{
static private $storages=array();
@@ -68,6 +76,16 @@ class OC_FILESYSTEM{
}
/**
+ * tear down the filesystem, removing all storage providers
+ */
+ static public function tearDown(){
+ foreach(self::$storages as $mountpoint=>$storage){
+ unset(self::$storages[$mountpoint]);
+ }
+ $fakeRoot='';
+ }
+
+ /**
* create a new storage of a specific type
* @param string type
* @param array arguments
@@ -91,8 +109,10 @@ class OC_FILESYSTEM{
* @return bool
*/
static public function chroot($fakeRoot){
- if($fakeRoot[0]!=='/'){
- $fakeRoot='/'.$fakeRoot;
+ if(!$fakeRoot==''){
+ if($fakeRoot[0]!=='/'){
+ $fakeRoot='/'.$fakeRoot;
+ }
}
self::$fakeRoot=$fakeRoot;
}
@@ -210,14 +230,28 @@ class OC_FILESYSTEM{
static public function mkdir($path){
$parent=substr($path,0,strrpos($path,'/'));
if(self::canWrite($parent) and $storage=self::getStorage($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path));
- return $storage->mkdir(self::getInternalPath($path));
+ $run=true;
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path, 'run' => &$run));
+ if($run){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path, 'run' => &$run));
+ }
+ if($run){
+ $result=$storage->mkdir(self::getInternalPath($path));
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_create', array( 'path' => $path));
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_write', array( 'path' => $path));
+ return $result;
+ }
}
}
static public function rmdir($path){
if(self::canWrite($path) and $storage=self::getStorage($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'delete', array( 'path' => $path));
- return $storage->rmdir(self::getInternalPath($path));
+ $run=true;
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'delete', array( 'path' => $path, 'run' => &$run));
+ if($run){
+ $result=$storage->rmdir(self::getInternalPath($path));
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_delete', array( 'path' => $path));
+ return $result;
+ }
}
}
static public function opendir($path){
@@ -307,55 +341,94 @@ class OC_FILESYSTEM{
}
static public function file_put_contents($path,$data){
if(self::canWrite($path) and $storage=self::getStorage($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path));
- if(!self::file_exists($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path));
+ $run=true;
+ $exists=self::file_exists($path);
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path, 'run' => &$run));
+ }
+ if($run){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path, 'run' => &$run));
+ }
+ if($run){
+ $result=$storage->file_put_contents(self::getInternalPath($path),$data);
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_create', array( 'path' => $path));
+ }
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_write', array( 'path' => $path));
+ return $result;
}
- return $storage->file_put_contents(self::getInternalPath($path),$data);
}
}
static public function unlink($path){
if(self::canWrite($path) and $storage=self::getStorage($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'delete', array( 'path' => $path));
- return $storage->unlink(self::getInternalPath($path));
+ $run=true;
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'delete', array( 'path' => $path, 'run' => &$run));
+ if($run){
+ $result=$storage->unlink(self::getInternalPath($path));
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_delete', array( 'path' => $path));
+ return $result;
+ }
}
}
static public function rename($path1,$path2){
if(self::canWrite($path1) and self::canWrite($path2)){
- $mp1=self::getMountPoint($path1);
- $mp2=self::getMountPoint($path2);
- if($mp1==$mp2){
- if($storage=self::getStorage($path1)){
- return $storage->rename(self::getInternalPath($path1),self::getInternalPath($path2));
+ $run=true;
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'rename', array( 'oldpath' => $path1 ,'newpath'=>$path2, 'run' => &$run));
+ if($run){
+ $mp1=self::getMountPoint($path1);
+ $mp2=self::getMountPoint($path2);
+ if($mp1==$mp2){
+ if($storage=self::getStorage($path1)){
+ $result=$storage->rename(self::getInternalPath($path1),self::getInternalPath($path2));
+ }
+ }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){
+ $tmpFile=$storage1->toTmpFile(self::getInternalPath($path1));
+ $result=$storage2->fromTmpFile(self::getInternalPath($path2));
+ $storage1->unlink(self::getInternalPath($path1));
}
- }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){
- $tmpFile=$storage1->toTmpFile(self::getInternalPath($path1));
- $result=$storage2->fromTmpFile(self::getInternalPath($path2));
- $storage1->unlink(self::getInternalPath($path1));
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_rename', array( 'oldpath' => $path1, 'newpath'=>$path2));
return $result;
}
- OC_HOOK::emit( 'OC_FILESYSTEM', 'rename', array( 'oldpath' => $path1 ,'newpath'=>$path2));
}
}
static public function copy($path1,$path2){
if(self::canRead($path1) and self::canWrite($path2)){
- $mp1=self::getMountPoint($path1);
- $mp2=self::getMountPoint($path2);
- if($mp1==$mp2){
- if($storage=self::getStorage($path1)){
- return $storage->copy(self::getInternalPath($path1),self::getInternalPath($path2));
+ $run=true;
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'copy', array( 'oldpath' => $path1 ,'newpath'=>$path2, 'run' => &$run));
+ $exists=self::file_exists($path2);
+ if($run and !$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path2, 'run' => &$run));
+ }
+ if($run){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path2, 'run' => &$run));
+ }
+ if($run){
+ $mp1=self::getMountPoint($path1);
+ $mp2=self::getMountPoint($path2);
+ if($mp1==$mp2){
+ if($storage=self::getStorage($path1)){
+ $result=$storage->copy(self::getInternalPath($path1),self::getInternalPath($path2));
+ }
+ }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){
+ $tmpFile=$storage1->toTmpFile(self::getInternalPath($path1));
+ $result=$storage2->fromTmpFile(self::getInternalPath($path2));
+ }
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_copy', array( 'oldpath' => $path1 ,'newpath'=>$path2));
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_create', array( 'path' => $path));
}
- }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){
- $tmpFile=$storage1->toTmpFile(self::getInternalPath($path1));
- return $storage2->fromTmpFile(self::getInternalPath($path2));
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_write', array( 'path' => $path));
+ return $result;
}
- OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path2));
}
}
static public function fopen($path,$mode){
$allowed=((strpos($path,'r')===false and strpos($path,'r+')!==false and self::canRead) or self::canWrite($path));
if($allowed){
if($storage=self::getStorage($path)){
+ $run=true;
+ $exists=self::file_exists($path);
+ $write=false;
switch($mode){
case 'r':
OC_HOOK::emit( 'OC_FILESYSTEM', 'read', array( 'path' => $path));
@@ -365,21 +438,34 @@ class OC_FILESYSTEM{
case 'x+':
case 'a+':
OC_HOOK::emit( 'OC_FILESYSTEM', 'read', array( 'path' => $path));
- OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path));
- if(!self::file_exists($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path));
- }
+ $write=true;
break;
case 'w':
case 'x':
case 'a':
- OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path));
- if(!self::file_exists($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path));
- }
+ $write=true;
break;
}
- return $storage->fopen(self::getInternalPath($path),$mode);
+ if($write){
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path));
+ }
+ if($run){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path, 'run' => &$run));
+ }
+ }
+ if($run){
+ $result=$storage->fopen(self::getInternalPath($path),$mode);
+ if($write){
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_create', array( 'path' => $path));
+ }
+ if($run){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_write', array( 'path' => $path));
+ }
+ }
+ return $result;
+ }
}
}
}
@@ -391,20 +477,44 @@ class OC_FILESYSTEM{
}
static public function fromTmpFile($tmpFile,$path){
if(self::canWrite($path) and $storage=self::getStorage($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path));
- if(!self::file_exists($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path));
+ $run=true;
+ $exists=self::file_exists($path);
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path, 'run' => &$run));
+ }
+ if($run){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path, 'run' => &$run));
+ }
+ if($run){
+ $result=$storage->fromTmpFile($tmpFile,self::getInternalPath($path));
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_create', array( 'path' => $path));
+ }
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_write', array( 'path' => $path));
+ return $result;
}
- return $storage->fromTmpFile($tmpFile,self::getInternalPath($path));
}
}
static public function fromUploadedFile($tmpFile,$path){
+ error_log('upload');
if(self::canWrite($path) and $storage=self::getStorage($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path));
- if(!self::file_exists($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path));
+ $run=true;
+ $exists=self::file_exists($path);
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'create', array( 'path' => $path, 'run' => &$run));
+ }
+ if($run){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'write', array( 'path' => $path, 'run' => &$run));
+ }
+ error_log('upload2');
+ if($run){
+ $result=$storage->fromUploadedFile($tmpFile,self::getInternalPath($path));
+ if(!$exists){
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_create', array( 'path' => $path));
+ }
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'post_write', array( 'path' => $path));
+ return $result;
}
- return $storage->fromUploadedFile($tmpFile,self::getInternalPath($path));
}
}
static public function getMimeType($path){
@@ -414,8 +524,11 @@ class OC_FILESYSTEM{
}
static public function delTree($path){
if(self::canWrite($path) and $storage=self::getStorage($path)){
- OC_HOOK::emit( 'OC_FILESYSTEM', 'delete', array( 'path' => $path));
- return $storage->delTree(self::getInternalPath($path));
+ $run=true;
+ OC_HOOK::emit( 'OC_FILESYSTEM', 'delete', array( 'path' => $path, 'run' => &$run));
+ if($run){
+ return $storage->delTree(self::getInternalPath($path));
+ }
}
}
static public function find($path){
@@ -463,10 +576,12 @@ class OC_FILESYSTEM{
$fakeRootLength=strlen(self::$fakeRoot);
foreach(self::$storages as $mountpoint=>$storage){
$results=$storage->search($query);
- foreach($results as $result){
- $file=str_replace('//','/',$mountpoint.$result);
- $file=substr($file,$fakeRootLength);
- $files[]=$file;
+ if(is_array($results)){
+ foreach($results as $result){
+ $file=str_replace('//','/',$mountpoint.$result);
+ $file=substr($file,$fakeRootLength);
+ $files[]=$file;
+ }
}
}
return $files;
diff --git a/lib/helper.php b/lib/helper.php
index 072607f742b..96d5bfad826 100644..100755
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -29,22 +29,39 @@ class OC_HELPER {
* @brief Creates an url
* @param $app app
* @param $file file
+ * @param $redirect_url redirect_url variable is appended to the URL
* @returns the url
*
* Returns a url to the given app and file.
*/
- public static function linkTo( $app, $file ){
+ public static function linkTo( $app, $file , $redirect_url=NULL ){
global $WEBROOT;
global $SERVERROOT;
- if(!empty($app)) {
+ if( $app != '' ){
$app .= '/';
// Check if the app is in the app folder
- if( file_exists( $SERVERROOT . '/apps/'. $app )){
- return $WEBROOT . '/apps/' . $app . $file;
+ if( file_exists( $SERVERROOT . '/apps/'. $app.$file )){
+ $urlLinkTo = $WEBROOT . '/apps/' . $app . $file;
+ }
+ else{
+ $urlLinkTo = $WEBROOT . '/' . $app . $file;
+ }
+ }
+ else{
+ if( file_exists( $SERVERROOT . '/core/'. $file )){
+ $urlLinkTo = $WEBROOT . '/core/'.$file;
+ }
+ else{
+ $urlLinkTo = $WEBROOT . '/'.$file;
}
}
- return $WEBROOT . '/' . $app . $file;
+
+ if($redirect_url)
+ return $urlLinkTo.'?redirect_url='.$redirect_url;
+ else
+ return $urlLinkTo;
+
}
/**
@@ -58,14 +75,16 @@ class OC_HELPER {
public static function imagePath( $app, $image ){
global $SERVERROOT;
global $WEBROOT;
+
// Check if the app is in the app folder
if( file_exists( "$SERVERROOT/apps/$app/img/$image" )){
return "$WEBROOT/apps/$app/img/$image";
}
- if( !empty( $app )){
+ elseif( !empty( $app )){
return "$WEBROOT/$app/img/$image";
- }else{
- return "$WEBROOT/img/$image";
+ }
+ else{
+ return "$WEBROOT/core/img/$image";
}
}
@@ -84,15 +103,15 @@ class OC_HELPER {
// Is it a dir?
if( $mimetype == "dir" ){
- return "$WEBROOT/img/places/folder.png";
+ return "$WEBROOT/core/img/places/folder.png";
}
// Icon exists?
- if( file_exists( "$SERVERROOT/img/mimetypes/$mimetype.png" )){
- return "$WEBROOT/img/mimetypes/$mimetype.png";
+ if( file_exists( "$SERVERROOT/core/img/mimetypes/$mimetype.png" )){
+ return "$WEBROOT/core/img/mimetypes/$mimetype.png";
}
else{
- return "$WEBROOT/img/mimetypes/file.png";
+ return "$WEBROOT/core/img/mimetypes/file.png";
}
}
diff --git a/lib/installer.php b/lib/installer.php
index 7ab07bf5077..a237caa0983 100644
--- a/lib/installer.php
+++ b/lib/installer.php
@@ -59,7 +59,7 @@ class OC_INSTALLER{
if(!isset($data['source'])){
error_log("No source specified when installing app");
- return;
+ return false;
}
//download the file if necesary
@@ -67,13 +67,13 @@ class OC_INSTALLER{
$path=tempnam(sys_get_temp_dir(),'oc_installer_');
if(!isset($data['href'])){
error_log("No href specified when installing app from http");
- return;
+ return false;
}
copy($data['href'],$path);
}else{
if(!isset($data['path'])){
error_log("No path specified when installing app from local file");
- return;
+ return false;
}
$path=$data['path'];
}
@@ -92,7 +92,7 @@ class OC_INSTALLER{
if($data['source']=='http'){
unlink($path);
}
- return;
+ return false;
}
//load the info.xml file of the app
@@ -102,23 +102,33 @@ class OC_INSTALLER{
if($data['source']=='http'){
unlink($path);
}
- return;
+ return false;
}
$info=OC_APP::getAppInfo($extractDir.'/appinfo/info.xml');
$basedir=$SERVERROOT.'/apps/'.$info['id'];
//check if an app with the same id is already installed
- if(is_dir($basedir)){
+ if(self::isInstalled( $info['id'] )){
error_log("App already installed");
OC_HELPER::rmdirr($extractDir);
if($data['source']=='http'){
unlink($path);
}
- return;
+ return false;
+ }
+
+ //check if the destination directory already exists
+ if(is_dir($basedir)){
+ error_log("App's directory already exists");
+ OC_HELPER::rmdirr($extractDir);
+ if($data['source']=='http'){
+ unlink($path);
+ }
+ return false;
}
if(isset($data['pretent']) and $data['pretent']==true){
- return;
+ return false;
}
//copy the app to the correct place
@@ -128,7 +138,7 @@ class OC_INSTALLER{
if($data['source']=='http'){
unlink($path);
}
- return;
+ return false;
}
OC_HELPER::copyr($extractDir,$basedir);
@@ -150,6 +160,23 @@ class OC_INSTALLER{
//set the installed version
OC_APPCONFIG::setValue($info['id'],'installed_version',$info['version']);
+ OC_APPCONFIG::setValue($info['id'],'enabled','no');
+ return true;
+ }
+
+ /**
+ * @brief checks whether or not an app is installed
+ * @param $app app
+ * @returns true/false
+ *
+ * Checks whether or not an app is installed, i.e. registered in apps table.
+ */
+ public static function isInstalled( $app ){
+
+ if( null == OC_APPCONFIG::getValue( $app, "installed_version" )){
+ return false;
+ }
+
return true;
}
@@ -209,4 +236,42 @@ class OC_INSTALLER{
// TODO: write function
return true;
}
+
+ /**
+ * @brief Installs shipped apps
+ * @param $enabled
+ *
+ * This function installs all apps found in the 'apps' directory;
+ * If $enabled is true, apps are installed as enabled.
+ * If $enabled is false, apps are installed as disabled.
+ */
+ public static function installShippedApps( $enabled ){
+ global $SERVERROOT;
+ $dir = opendir( "$SERVERROOT/apps" );
+ while( false !== ( $filename = readdir( $dir ))){
+ if( substr( $filename, 0, 1 ) != '.' and is_dir("$SERVERROOT/apps/$filename") ){
+ if( file_exists( "$SERVERROOT/apps/$filename/appinfo/app.php" )){
+ if(!OC_INSTALLER::isInstalled($filename)){
+ //install the database
+ if(is_file("$SERVERROOT/apps/$filename/appinfo/database.xml")){
+ OC_DB::createDbFromStructure("$SERVERROOT/apps/$filename/appinfo/database.xml");
+ }
+
+ //run appinfo/install.php
+ if(is_file("$SERVERROOT/apps/$filename/appinfo/install.php")){
+ include("$SERVERROOT/apps/$filename/appinfo/install.php");
+ }
+ $info=OC_APP::getAppInfo("$SERVERROOT/apps/$filename/appinfo/info.xml");
+ OC_APPCONFIG::setValue($filename,'installed_version',$info['version']);
+ if( $enabled ){
+ OC_APPCONFIG::setValue($filename,'enabled','yes');
+ }else{
+ OC_APPCONFIG::setValue($filename,'enabled','no');
+ }
+ }
+ }
+ }
+ }
+ closedir( $dir );
+ }
}
diff --git a/lib/l10n.php b/lib/l10n.php
new file mode 100644
index 00000000000..053c6fbc10e
--- /dev/null
+++ b/lib/l10n.php
@@ -0,0 +1,269 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2010 Frank Karlitschek karlitschek@kde.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * This class is for i18n and l10n
+ */
+class OC_L10N{
+ /**
+ * cache
+ */
+ protected static $cache = array();
+
+ /**
+ * The best language
+ */
+ protected static $language = '';
+
+ /**
+ * Translations
+ */
+ private $translations = array();
+
+ /**
+ * Localization
+ */
+ private $localizations = array(
+ 'date' => 'd.m.Y',
+ 'datetime' => 'd.m.Y H:i:s',
+ 'time' => 'H:i:s' );
+
+ /**
+ * @brief The constructor
+ * @param $app the app requesting l10n
+ * @param $lang default: null Language
+ * @returns OC_L10N-Object
+ *
+ * If language is not set, the constructor tries to find the right
+ * language.
+ */
+ public function __construct( $app, $lang = null ){
+ global $SERVERROOT;
+ // Find the right language
+ if( is_null( $lang )){
+ $lang = self::findLanguage( $app );
+ }
+
+ // Use cache if possible
+ if(array_key_exists($app.'::'.$lang, self::$cache )){
+
+ $this->translations = self::$cache[$app.'::'.$lang]['t'];
+ $this->localizations = self::$cache[$app.'::'.$lang]['l'];
+ }
+ else{
+ $i18ndir = self::findI18nDir( $app );
+ // Localization is in /l10n, Texts are in $i18ndir
+ // (Just no need to define date/time format etc. twice)
+ if( file_exists( $i18ndir.$lang.'.php' )){
+ // Include the file, save the data from $CONFIG
+ include( $i18ndir.$lang.'.php' );
+ if( isset( $TRANSLATIONS ) && is_array( $TRANSLATIONS )){
+ $this->translations = $TRANSLATIONS;
+ }
+ }
+
+ if( file_exists( $SERVERROOT.'/core/l10n/l10n-'.$lang.'.php' )){
+ // Include the file, save the data from $CONFIG
+ include( $SERVERROOT.'/core/l10n/l10n-'.$lang.'.php' );
+ if( isset( $LOCALIZATIONS ) && is_array( $LOCALIZATIONS )){
+ $this->localizations = array_merge( $this->localizations, $LOCALIZATIONS );
+ }
+ }
+
+ self::$cache[$app.'::'.$lang]['t'] = $this->translations;
+ self::$cache[$app.'::'.$lang]['l'] = $this->localizations;
+ }
+ }
+
+ /**
+ * @brief Translating
+ * @param $text The text we need a translation for
+ * @returns Translation or the same text
+ *
+ * Returns the translation. If no translation is found, $text will be
+ * returned.
+ */
+ public function t($text){
+ if(array_key_exists($text, $this->translations)){
+ return $this->translations[$text];
+ }
+ return $text;
+ }
+
+ /**
+ * @brief getTranslations
+ * @returns Fetch all translations
+ *
+ * Returns an associative array with all translations
+ */
+ public function getTranslations(){
+ return $this->translations;
+ }
+
+ /**
+ * @brief Localization
+ * @param $type Type of localization
+ * @param $params parameters for this localization
+ * @returns String or false
+ *
+ * Returns the localized data.
+ *
+ * Implemented types:
+ * - date
+ * - Creates a date
+ * - l10n-field: date
+ * - params: timestamp (int/string)
+ * - datetime
+ * - Creates date and time
+ * - l10n-field: datetime
+ * - params: timestamp (int/string)
+ * - time
+ * - Creates a time
+ * - l10n-field: time
+ * - params: timestamp (int/string)
+ */
+ public function l($type, $data){
+ switch($type){
+ // If you add something don't forget to add it to $localizations
+ // at the top of the page
+ case 'date':
+ if( is_string( $data )) $data = strtotime( $data );
+ return date( $this->localizations['date'], $data );
+ break;
+ case 'datetime':
+ if( is_string( $data )) $data = strtotime( $data );
+ return date( $this->localizations['datetime'], $data );
+ break;
+ case 'time':
+ if( is_string( $data )) $data = strtotime( $data );
+ return date( $this->localizations['time'], $data );
+ break;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * @brief Choose a language
+ * @param $texts Associative Array with possible strings
+ * @returns String
+ *
+ * $text is an array 'de' => 'hallo welt', 'en' => 'hello world', ...
+ *
+ * This function is useful to avoid loading thousands of files if only one
+ * simple string is needed, for example in appinfo.php
+ */
+ public static function selectLanguage( $text ){
+ $lang = self::findLanguage( array_keys( $text ));
+ return $text[$lang];
+ }
+
+ /**
+ * @brief find the best language
+ * @param $app Array or string, details below
+ * @returns language
+ *
+ * If $app is an array, ownCloud assumes that these are the available
+ * languages. Otherwise ownCloud tries to find the files in the l10n
+ * folder.
+ *
+ * If nothing works it returns 'en'
+ */
+ public static function findLanguage( $app = null ){
+ if( !is_array( $app) && self::$language != '' ){
+ return self::$language;
+ }
+
+ $available = array();
+ if( is_array( $app )){
+ $available = $app;
+ }
+ else{
+ $available=self::findAvailableLanguages( $app );
+ }
+ if( OC_USER::getUser() && OC_PREFERENCES::getValue( OC_USER::getUser(), 'core', 'lang' )){
+ $lang = OC_PREFERENCES::getValue( OC_USER::getUser(), 'core', 'lang' );
+ self::$language = $lang;
+ if( array_search( $lang, $available ) !== false ){
+ return $lang;
+ }
+ }
+
+ if( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] )){
+ $accepted_languages = preg_split( '/,\s*/', $_SERVER['HTTP_ACCEPT_LANGUAGE'] );
+ foreach( $accepted_languages as $i ){
+ $temp = explode( ';', $i );
+ if( array_search( $temp[0], $available ) !== false ){
+ return $temp[0];
+ }
+ }
+ }
+
+ // Last try: English
+ return 'en';
+ }
+
+ /**
+ * @brief find the l10n directory
+ * @param $app App that needs to be translated
+ * @returns directory
+ */
+ protected static function findI18nDir( $app ){
+ global $SERVERROOT;
+
+ // find the i18n dir
+ $i18ndir = $SERVERROOT.'/core/l10n/';
+ if( $app != '' ){
+ // Check if the app is in the app folder
+ if( file_exists( $SERVERROOT.'/apps/'.$app.'/l10n/' )){
+ $i18ndir = $SERVERROOT.'/apps/'.$app.'/l10n/';
+ }
+ else{
+ $i18ndir = $SERVERROOT.'/'.$app.'/l10n/';
+ }
+ }
+ return $i18ndir;
+ }
+
+ /**
+ * @brief find all available languages for an app
+ * @param $app App that needs to be translated
+ * @returns array an array of available languages
+ */
+ public static function findAvailableLanguages( $app=null ){
+ $available=array('en');//english is always available
+ $dir = self::findI18nDir( $app );
+ if( file_exists($dir)){
+ $dh = opendir($dir);
+ while(( $file = readdir( $dh )) !== false ){
+ if( substr( $file, -4, 4 ) == '.php' and strlen($file)==6 ){
+ $i = substr( $file, 0, -4 );
+ if( $i != '' ){
+ $available[] = $i;
+ }
+ }
+ }
+ closedir($dh);
+ }
+ return $available;
+ }
+} \ No newline at end of file
diff --git a/lib/log.php b/lib/log.php
index aebeba7d4b3..894575ef059 100644
--- a/lib/log.php
+++ b/lib/log.php
@@ -79,7 +79,7 @@ class OC_LOG {
* - app: only entries for this app
*/
public static function get( $filter = array()){
- $queryString='SELECT * FROM *PREFIX*log WHERE 1=1 ';
+ $queryString='SELECT * FROM *PREFIX*log WHERE 1=1 ORDER BY moment DESC';
$params=array();
if(isset($filter['from'])){
$queryString.='AND moment>? ';
@@ -120,6 +120,18 @@ class OC_LOG {
$query->execute(array($date));
return true;
}
+
+ /**
+ * @brief removes all log entries
+ * @returns true/false
+ *
+ * This function deletes all log entries.
+ */
+ public static function deleteAll(){
+ $query=OC_DB::prepare("DELETE FROM *PREFIX*log");
+ $query->execute();
+ return true;
+ }
/**
* @brief filter an array of log entries on action
diff --git a/lib/ocsclient.php b/lib/ocsclient.php
index a3c4659c6e4..cfd529b2ec4 100644
--- a/lib/ocsclient.php
+++ b/lib/ocsclient.php
@@ -37,7 +37,10 @@ class OC_OCSCLIENT{
public static function getCategories(){
$url='http://api.apps.owncloud.com/v1/content/categories';
- $xml=file_get_contents($url);
+ $xml=@file_get_contents($url);
+ if($xml==FALSE){
+ return NULL;
+ }
$data=simplexml_load_string($xml);
$tmp=$data->data;
@@ -61,16 +64,19 @@ class OC_OCSCLIENT{
* This function returns a list of all the applications on the OCS server
*/
public static function getApplications($categories){
- if(is_array($categories)) {
+ if(is_array($categories)) {
$categoriesstring=implode('x',$categories);
}else{
$categoriesstring=$categories;
}
$url='http://api.apps.owncloud.com/v1/content/data?categories='.urlencode($categoriesstring).'&sortmode=new&page=0&pagesize=10';
$apps=array();
- $xml=file_get_contents($url);
+ $xml=@file_get_contents($url);
+ if($xml==FALSE){
+ return NULL;
+ }
$data=simplexml_load_string($xml);
-
+
$tmp=$data->data->content;
for($i = 0; $i < count($tmp); $i++) {
$app=array();
@@ -90,60 +96,66 @@ class OC_OCSCLIENT{
}
- /**
- * @brief Get an the applications from the OCS server
- * @returns array with application data
- *
- * This function returns an applications from the OCS server
- */
- public static function getApplication($id){
- $url='http://api.apps.owncloud.com/v1/content/data/'.urlencode($id);
-
- $xml=file_get_contents($url);
- $data=simplexml_load_string($xml);
-
- $tmp=$data->data->content;
- $app=array();
- $app['id']=$tmp->id;
- $app['name']=$tmp->name;
- $app['type']=$tmp->typeid;
- $app['typename']=$tmp->typename;
- $app['personid']=$tmp->personid;
- $app['detailpage']=$tmp->detailpage;
- $app['preview1']=$tmp->smallpreviewpic1;
- $app['preview2']=$tmp->smallpreviewpic2;
- $app['preview3']=$tmp->smallpreviewpic3;
- $app['changed']=strtotime($tmp->changed);
- $app['description']=$tmp->description;
-
- return $app;
- }
-
- /**
- * @brief Get all the knowledgebase entries from the OCS server
- * @returns array with q and a data
- *
- * This function returns a list of all the knowledgebase entries from the OCS server
- */
- public static function getKnownledgebaseEntries(){
- $url='http://api.apps.owncloud.com/v1/knowledgebase/data?type=150&page=0&pagesize=10';
-
- $kbe=array();
- $xml=file_get_contents($url);
- $data=simplexml_load_string($xml);
-
- $tmp=$data->data->content;
- for($i = 0; $i < count($tmp); $i++) {
- $kb=array();
- $kb['id']=$tmp[$i]->id;
- $kb['name']=$tmp[$i]->name;
- $kb['description']=$tmp[$i]->description;
- $kb['answer']=$tmp[$i]->answer;
- $kb['preview1']=$tmp[$i]->smallpreviewpic1;
- $kbe[]=$kb;
- }
- return $kbe;
- }
+ /**
+ * @brief Get an the applications from the OCS server
+ * @returns array with application data
+ *
+ * This function returns an applications from the OCS server
+ */
+ public static function getApplication($id){
+ $url='http://api.apps.owncloud.com/v1/content/data/'.urlencode($id);
+
+ $xml=@file_get_contents($url);
+ if($xml==FALSE){
+ return NULL;
+ }
+ $data=simplexml_load_string($xml);
+
+ $tmp=$data->data->content;
+ $app=array();
+ $app['id']=$tmp->id;
+ $app['name']=$tmp->name;
+ $app['type']=$tmp->typeid;
+ $app['typename']=$tmp->typename;
+ $app['personid']=$tmp->personid;
+ $app['detailpage']=$tmp->detailpage;
+ $app['preview1']=$tmp->smallpreviewpic1;
+ $app['preview2']=$tmp->smallpreviewpic2;
+ $app['preview3']=$tmp->smallpreviewpic3;
+ $app['changed']=strtotime($tmp->changed);
+ $app['description']=$tmp->description;
+
+ return $app;
+ }
+
+ /**
+ * @brief Get all the knowledgebase entries from the OCS server
+ * @returns array with q and a data
+ *
+ * This function returns a list of all the knowledgebase entries from the OCS server
+ */
+ public static function getKnownledgebaseEntries(){
+ $url='http://api.apps.owncloud.com/v1/knowledgebase/data?type=150&page=0&pagesize=10';
+
+ $kbe=array();
+ $xml=@file_get_contents($url);
+ if($xml==FALSE){
+ return NULL;
+ }
+ $data=simplexml_load_string($xml);
+
+ $tmp=$data->data->content;
+ for($i = 0; $i < count($tmp); $i++) {
+ $kb=array();
+ $kb['id']=$tmp[$i]->id;
+ $kb['name']=$tmp[$i]->name;
+ $kb['description']=$tmp[$i]->description;
+ $kb['answer']=$tmp[$i]->answer;
+ $kb['preview1']=$tmp[$i]->smallpreviewpic1;
+ $kbe[]=$kb;
+ }
+ return $kbe;
+ }
diff --git a/lib/preferences.php b/lib/preferences.php
index 5a9ed395085..0f4636f6832 100644
--- a/lib/preferences.php
+++ b/lib/preferences.php
@@ -139,10 +139,12 @@ class OC_PREFERENCES{
*/
public static function setValue( $user, $app, $key, $value ){
// Check if the key does exist
- $exists = self::getValue( $user, $app, $key, null );
+ $query = OC_DB::prepare( 'SELECT configvalue FROM *PREFIX*preferences WHERE userid = ? AND appid = ? AND configkey = ?' );
+ $values=$query->execute(array($user,$app,$key))->fetchAll();
+ error_log(print_r($values,true));
+ $exists=(count($values)>0);
- // null: does not exist. Insert.
- if( is_null( $exists )){
+ if( !$exists ){
$query = OC_DB::prepare( 'INSERT INTO *PREFIX*preferences ( userid, appid, configkey, configvalue ) VALUES( ?, ?, ?, ? )' );
$query->execute( array( $user, $app, $key, $value ));
}
diff --git a/lib/setup.php b/lib/setup.php
index 72507f221b9..1be4dea286d 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -1,6 +1,8 @@
<?php
-$hasSQLite = is_callable('sqlite_open');
+include_once( 'installer.php' );
+
+$hasSQLite = (is_callable('sqlite_open') or class_exists('SQLite3'));
$hasMySQL = is_callable('mysql_connect');
$datadir = OC_CONFIG::getValue('datadir', $SERVERROOT.'/data');
$opts = array(
@@ -21,7 +23,7 @@ if(isset($_POST['install']) AND $_POST['install']=='true') {
OC_TEMPLATE::printGuestPage("", "installation", $options);
}
else {
- header("Location: $WEBROOT");
+ header("Location: ".$WEBROOT.'/');
exit();
}
}
@@ -65,10 +67,15 @@ class OC_SETUP {
$username = htmlspecialchars_decode($options['adminlogin']);
$password = htmlspecialchars_decode($options['adminpass']);
$datadir = htmlspecialchars_decode($options['directory']);
+
+ //use sqlite3 when available, otherise sqlite2 will be used.
+ if($dbtype=='sqlite' and class_exists('SQLite3')){
+ $dbtype='sqlite3';
+ }
//write the config file
OC_CONFIG::setValue('datadirectory', $datadir);
- OC_CONFIG::setValue('dbtype', $dbtype);
+ OC_CONFIG::setValue('dbtype', $dbtype);
if($dbtype == 'mysql') {
$dbuser = $options['dbuser'];
$dbpass = $options['dbpass'];
@@ -90,13 +97,20 @@ class OC_SETUP {
else {
$query="SELECT user FROM mysql.user WHERE user='$dbuser'"; //this should be enough to check for admin rights in mysql
if(mysql_query($query, $connection)) {
- self::createDBUser($username, $password, $connection);
//use the admin login data for the new database user
- OC_CONFIG::setValue('dbuser', $username);
- OC_CONFIG::setValue('dbpassword', $password);
+
+ //add prefix to the mysql user name to prevent collissions
+ $dbusername='oc_mysql_'.$username;
+ //hash the password so we don't need to store the admin config in the config file
+ $dbpassowrd=md5(time().$password);
+
+ self::createDBUser($dbusername, $dbpassowrd, $connection);
+
+ OC_CONFIG::setValue('dbuser', $dbusername);
+ OC_CONFIG::setValue('dbpassword', $dbpassowrd);
//create the database
- self::createDatabase($dbname, $username, $connection);
+ self::createDatabase($dbname, $dbusername, $connection);
}
else {
OC_CONFIG::setValue('dbuser', $dbuser);
@@ -128,6 +142,9 @@ class OC_SETUP {
OC_GROUP::createGroup('admin');
OC_GROUP::addToGroup($username, 'admin');
+ //guess what this does
+ OC_INSTALLER::installShippedApps(true);
+
//create htaccess files for apache hosts
self::createHtaccess(); //TODO detect if apache is used
@@ -167,10 +184,11 @@ class OC_SETUP {
private static function createHtaccess() {
global $SERVERROOT;
global $WEBROOT;
- $content = "ErrorDocument 404 /$WEBROOT/templates/404.php\n";//custom 404 error page
+ $content = "ErrorDocument 404 /$WEBROOT/core/templates/404.php\n";//custom 404 error page
$content.= "php_value upload_max_filesize 20M\n";//upload limit
$content.= "php_value post_max_size 20M\n";
$content.= "SetEnv htaccessWorking true\n";
+ $content.= "Options -Indexes\n";
@file_put_contents($SERVERROOT.'/.htaccess', $content); //supress errors in case we don't have permissions for it
$content = "deny from all";
@@ -178,4 +196,4 @@ class OC_SETUP {
}
}
-?> \ No newline at end of file
+?>
diff --git a/lib/template.php b/lib/template.php
index 0d6776aa26d..9393fe6908e 100644
--- a/lib/template.php
+++ b/lib/template.php
@@ -73,8 +73,10 @@ function human_file_size( $bytes ){
class OC_TEMPLATE{
private $renderas; // Create a full page?
private $application; // template Application
- private $vars; // The smarty object
- private $template; // The smarty object
+ private $vars; // Vars
+ private $template; // The path to the template
+ private $l10n; // The l10n-Object
+ private $headers=array(); //custom headers
/**
* @brief Constructor
@@ -94,8 +96,8 @@ class OC_TEMPLATE{
global $SERVERROOT;
// Get the right template folder
- $template = "$SERVERROOT/templates/";
- if( $app != "core" && $app != "" ){
+ $template = "$SERVERROOT/core/templates/";
+ if( $app != "" ){
// Check if the app is in the app folder
if( file_exists( "$SERVERROOT/apps/$app/templates/" )){
$template = "$SERVERROOT/apps/$app/templates/";
@@ -113,6 +115,7 @@ class OC_TEMPLATE{
$this->application = $app;
$this->template = $template;
$this->vars = array();
+ $this->l10n = new OC_L10N($app);
}
/**
@@ -149,6 +152,16 @@ class OC_TEMPLATE{
$this->vars[$key] = array( $value );
}
}
+
+ /**
+ * @brief Add a custom element to the header
+ * @param string tag tag name of the element
+ * @param array $attributes array of attrobutes for the element
+ * @param string $text the text content for the element
+ */
+ public function addHeader( $tag, $attributes, $text=''){
+ $this->headers[]=array('tag'=>$tag,'attributes'=>$attributes,'text'=>$text);
+ }
/**
* @brief Prints the proceeded template
@@ -193,7 +206,6 @@ class OC_TEMPLATE{
$search=new OC_TEMPLATE( 'core', 'part.searchbox');
$search->assign('searchurl',OC_HELPER::linkTo( 'search', 'index.php' ));
$page->assign('searchbox', $search->fetchPage());
- // Add menu data
// Add navigation entry
$page->assign( "navigation", OC_APP::getNavigation());
@@ -204,34 +216,48 @@ class OC_TEMPLATE{
$search=new OC_TEMPLATE( 'core', 'part.searchbox');
$search->assign('searchurl',OC_HELPER::linkTo( 'search', 'index.php' ));
$page->assign('searchbox', $search->fetchPage());
+
// Add menu data
if( OC_GROUP::inGroup( $_SESSION["user_id"], "admin" )){
- $page->assign( "settingsnavigation", OC_APP::getSettingsNavigation());
+ $page->assign( "adminnavigation", OC_APP::getAdminNavigation());
}
- $page->assign( "adminnavigation", OC_APP::getAdminNavigation());
+ $page->assign( "settingsnavigation", OC_APP::getSettingsNavigation());
}
else
{
$page = new OC_TEMPLATE( "core", "layout.guest" );
- // Add data if required
}
// Add the css and js files
foreach(OC_UTIL::$scripts as $script){
if(is_file("$SERVERROOT/apps/$script.js" )){
$page->append( "jsfiles", "$WEBROOT/apps/$script.js" );
- }else{
+ }
+ elseif(is_file("$SERVERROOT/$script.js" )){
$page->append( "jsfiles", "$WEBROOT/$script.js" );
}
+ else{
+ $page->append( "jsfiles", "$WEBROOT/core/$script.js" );
+ }
}
foreach(OC_UTIL::$styles as $style){
if(is_file("$SERVERROOT/apps/$style.css" )){
$page->append( "cssfiles", "$WEBROOT/apps/$style.css" );
- }else{
+ }
+ elseif(is_file("$SERVERROOT/$style.css" )){
$page->append( "cssfiles", "$WEBROOT/$style.css" );
}
+ else{
+ $page->append( "cssfiles", "$WEBROOT/core/$style.css" );
+ }
}
-
+
+ // Add custom headers
+ $page->assign('headers',$this->headers);
+ foreach(OC_UTIL::$headers as $header){
+ $page->append('headers',$header);
+ }
+
// Add css files and js files
$page->assign( "content", $data );
return $page->fetchPage();
@@ -251,6 +277,7 @@ class OC_TEMPLATE{
private function _fetch(){
// Register the variables
$_ = $this->vars;
+ $l = $this->l10n;
// Execute the template
ob_start();
diff --git a/lib/testcase.php b/lib/testcase.php
new file mode 100644
index 00000000000..19494dc2f19
--- /dev/null
+++ b/lib/testcase.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmailc.om
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library 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 AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+
+/**
+ * base class for unit tests
+ */
+class OC_TestCase{
+ private $tests; //array of all tests in this test case
+
+ public function __construct(){
+ $this->tests=array();
+ $this->results=array();
+ $functions=get_class_methods(get_class($this));
+ $exclude=get_class_methods('OC_TestCase');
+ foreach($functions as $function){
+ if(array_search($function,$exclude)===false){
+ $this->tests[]=$function;
+ }
+ }
+ }
+
+ public function getTests(){
+ return $this->tests;
+ }
+
+ /**
+ * function that gets called before each test
+ */
+ private function setup(){
+ }
+
+ /**
+ * function that gets called after each test
+ */
+ private function tearDown(){
+ }
+
+ /**
+ * check if the result equals the expected result
+ * @param mixed $expected the expected result
+ * @param mixed $result the actual result
+ * @param string $error (optional) the error message to display if the result isn't expected
+ */
+ protected function assertEquals($expected,$result,$error=''){
+ if($expected!==$result){
+ if($expected===true){
+ $expected='true';
+ }
+ if($expected===false){
+ $expected='false';
+ }
+ if($result===true){
+ $result='true';
+ }
+ if($result===false){
+ $result='false';
+ }
+ if($error==''){
+ $error="Unexpected result, expected '$expected' but was '$result'";
+ }
+ throw new Exception($error);
+ }
+ }
+
+ /**
+ * fail the test
+ * @param string $error the error message
+ */
+ protected function fail($error){
+ throw new Exception($error);
+ }
+} \ No newline at end of file
diff --git a/lib/user.php b/lib/user.php
index 09501e59c58..25f555b47b1 100644
--- a/lib/user.php
+++ b/lib/user.php
@@ -40,7 +40,7 @@ if( !OC_CONFIG::getValue( "installed", false )){
*/
class OC_USER {
// The backend used for user management
- private static $_backend = null;
+ private static $_usedBackends = array();
// Backends available (except database)
private static $_backends = array();
@@ -68,13 +68,23 @@ class OC_USER {
}
/**
- * @brief Sets the backend
+ * @brief gets used backends
+ * @returns array of backends
+ *
+ * Returns the names of all used backends.
+ */
+ public static function getUsedBackends(){
+ return array_keys(self::$_usedBackends);
+ }
+
+ /**
+ * @brief Adds the backend to the list of used backends
* @param $backend default: database The backend to use for user managment
* @returns true/false
*
* Set the User Authentication Module
*/
- public static function setBackend( $backend = 'database' ){
+ public static function useBackend( $backend = 'database' ){
// You'll never know what happens
if( null === $backend OR !is_string( $backend )){
$backend = 'database';
@@ -86,11 +96,11 @@ class OC_USER {
case 'mysql':
case 'sqlite':
require_once('User/database.php');
- self::$_backend = new OC_USER_DATABASE();
+ self::$_usedBackends[$backend] = new OC_USER_DATABASE();
break;
default:
$className = 'OC_USER_' . strToUpper($backend);
- self::$_backend = new $className();
+ self::$_usedBackends[$backend] = new $className();
break;
}
@@ -119,7 +129,7 @@ class OC_USER {
return false;
}
// Check if user already exists
- if( in_array( $uid, self::getUsers())){
+ if( self::userExists($uid) ){
return false;
}
@@ -127,13 +137,19 @@ class OC_USER {
$run = true;
OC_HOOK::emit( "OC_USER", "pre_createUser", array( "run" => &$run, "uid" => $uid, "password" => $password ));
- if( $run && self::$_backend->createUser( $uid, $password )){
- OC_HOOK::emit( "OC_USER", "post_createUser", array( "uid" => $uid, "password" => $password ));
- return true;
- }
- else{
- return false;
+ if( $run ){
+ //create the user in the first backend that supports creating users
+ foreach(self::$_usedBackends as $backend){
+ if(!$backend->implementsActions(OC_USER_BACKEND_CREATE_USER))
+ continue;
+
+ $backend->createUser($uid,$password);
+ OC_HOOK::emit( "OC_USER", "post_createUser", array( "uid" => $uid, "password" => $password ));
+
+ return true;
+ }
}
+ return false;
}
/**
@@ -147,7 +163,13 @@ class OC_USER {
$run = true;
OC_HOOK::emit( "OC_USER", "pre_deleteUser", array( "run" => &$run, "uid" => $uid ));
- if( $run && self::$_backend->deleteUser( $uid )){
+ if( $run ){
+ //delete the user from all backends
+ foreach(self::$_usedBackends as $backend){
+ if($backend->implementsActions(OC_USER_BACKEND_DELETE_USER)){
+ $backend->deleteUser($uid);
+ }
+ }
// We have to delete the user from all groups
foreach( OC_GROUP::getUserGroups( $uid ) as $i ){
OC_GROUP::removeFromGroup( $uid, $i );
@@ -174,7 +196,9 @@ class OC_USER {
$run = true;
OC_HOOK::emit( "OC_USER", "pre_login", array( "run" => &$run, "uid" => $uid ));
- if( $run && self::$_backend->login( $uid, $password )){
+ if( $run && self::checkPassword( $uid, $password )){
+ $_SESSION['user_id'] = $uid;
+ OC_LOG::add( "core", $_SESSION['user_id'], "login" );
OC_HOOK::emit( "OC_USER", "post_login", array( "uid" => $uid ));
return true;
}
@@ -191,7 +215,9 @@ class OC_USER {
*/
public static function logout(){
OC_HOOK::emit( "OC_USER", "logout", array());
- return self::$_backend->logout();
+ OC_LOG::add( "core", $_SESSION['user_id'], "logout" );
+ $_SESSION['user_id'] = false;
+ return true;
}
/**
@@ -201,7 +227,25 @@ class OC_USER {
* Checks if the user is logged in
*/
public static function isLoggedIn(){
- return self::$_backend->isLoggedIn();
+ if( isset($_SESSION['user_id']) AND $_SESSION['user_id'] ){
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+
+ /**
+ * @brief get the user idea of the user currently logged in.
+ * @return string uid or false
+ */
+ public static function getUser(){
+ if( isset($_SESSION['user_id']) AND $_SESSION['user_id'] ){
+ return $_SESSION['user_id'];
+ }
+ else{
+ return false;
+ }
}
/**
@@ -211,7 +255,7 @@ class OC_USER {
* generates a password
*/
public static function generatePassword(){
- return substr( md5( uniqId().time()), 0, 10 );
+ return uniqId();
}
/**
@@ -226,7 +270,14 @@ class OC_USER {
$run = true;
OC_HOOK::emit( "OC_USER", "pre_setPassword", array( "run" => &$run, "uid" => $uid, "password" => $password ));
- if( $run && self::$_backend->setPassword( $uid, $password )){
+ if( $run ){
+ foreach(self::$_usedBackends as $backend){
+ if($backend->implementsActions(OC_USER_BACKEND_SET_PASSWORD)){
+ if($backend->userExists($uid)){
+ $backend->setPassword($uid,$password);
+ }
+ }
+ }
OC_HOOK::emit( "OC_USER", "post_setPassword", array( "uid" => $uid, "password" => $password ));
return true;
}
@@ -244,7 +295,14 @@ class OC_USER {
* Check if the password is correct without logging in the user
*/
public static function checkPassword( $uid, $password ){
- return self::$_backend->checkPassword( $uid, $password );
+ foreach(self::$_usedBackends as $backend){
+ if($backend->implementsActions(OC_USER_BACKEND_CHECK_PASSWORD)){
+ $result=$backend->checkPassword( $uid, $password );
+ if($result===true){
+ return true;
+ }
+ }
+ }
}
/**
@@ -254,6 +312,29 @@ class OC_USER {
* Get a list of all users.
*/
public static function getUsers(){
- return self::$_backend->getUsers();
+ $users=array();
+ foreach(self::$_usedBackends as $backend){
+ if($backend->implementsActions(OC_USER_BACKEND_GET_USERS)){
+ $users=array_merge($users,$backend->getUsers());
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * @brief check if a user exists
+ * @param string $uid the username
+ * @return boolean
+ */
+ public static function userExists($uid){
+ foreach(self::$_usedBackends as $backend){
+ if($backend->implementsActions(OC_USER_BACKEND_USER_EXISTS)){
+ $result=$backend->userExists($uid);
+ if($result===true){
+ return true;
+ }
+ }
+ }
+ return false;
}
}